mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Merge branch 'feature/release-4.1'
This commit is contained in:
commit
7db1fe5380
1078 changed files with 49173 additions and 293095 deletions
44
.gitignore
vendored
44
.gitignore
vendored
|
|
@ -6,55 +6,21 @@
|
|||
.settings
|
||||
adb.pid
|
||||
bc-android.keystore
|
||||
bin
|
||||
build
|
||||
certdata.txt
|
||||
check_tools.mk
|
||||
default.properties
|
||||
doc
|
||||
gen
|
||||
liblinphone-junit-report.xml
|
||||
liblinphone-sdk/android-*
|
||||
liblinphonetester_*.zip
|
||||
libs
|
||||
libs-debug
|
||||
linphone-android.iml
|
||||
linphone-junit-report*.xml
|
||||
linphonetester_*.zip
|
||||
*.iml
|
||||
lint.xml
|
||||
local.properties
|
||||
Makefile
|
||||
obj
|
||||
proguard-project.txt
|
||||
project.properties
|
||||
res/.DS_Store
|
||||
res/raw/lpconfig.xsd
|
||||
submodules/externals/build/cunit/CUnit/
|
||||
submodules/externals/build/ffmpeg/arm/
|
||||
submodules/externals/build/ffmpeg/x86
|
||||
submodules/externals/build/libvpx/arm
|
||||
submodules/externals/build/libvpx/x86
|
||||
submodules/externals/build/openh264/arm
|
||||
submodules/externals/build/openh264/x86
|
||||
submodules/externals/ffmpeg/arm/
|
||||
tests/*$py.class
|
||||
tests/build.xml
|
||||
tests/project.properties
|
||||
ant_password.properties
|
||||
liblinphone_tester/liblinphonetester_*
|
||||
liblinphone_tester/tests.output
|
||||
tests/linphonetester_*
|
||||
tests/tests.output
|
||||
WORK
|
||||
.d
|
||||
google-services.json
|
||||
.*clang*
|
||||
**/*.iml
|
||||
src/linphone-wrapper
|
||||
liblinphone_tester/res/raw/
|
||||
**/.classpath
|
||||
**/.project
|
||||
**/*.kdev4
|
||||
liblinphone-sdk/res/
|
||||
**/.vscode
|
||||
res/value-hi_IN
|
||||
linphone-sdk-android/*.aar
|
||||
app/release
|
||||
keystore.properties
|
||||
app/src/main/res/xml/contacts.xml
|
||||
|
|
|
|||
33
.gitlab-ci-files/job-android.yml
Normal file
33
.gitlab-ci-files/job-android.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
job-android:
|
||||
|
||||
stage: build
|
||||
tags: [ "docker-android" ]
|
||||
image: gitlab.linphone.org:4567/bc/public/linphone-android/bc-dev-android:28
|
||||
|
||||
before_script:
|
||||
- if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then eval $(ssh-agent -s); fi
|
||||
- if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then echo "$SCP_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi
|
||||
|
||||
script:
|
||||
- sdkmanager
|
||||
- scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_KEYSTORE_PATH app/
|
||||
- scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_GOOGLE_SERVICES_PATH app/
|
||||
- echo storePassword=$ANDROID_KEYSTORE_PASSWORD > keystore.properties
|
||||
- echo keyPassword=$ANDROID_KEYSTORE_KEY_PASSWORD >> keystore.properties
|
||||
- echo keyAlias=$ANDROID_KEYSTORE_KEY_ALIAS >> keystore.properties
|
||||
- echo storeFile=$ANDROID_KEYSTORE_FILE >> keystore.properties
|
||||
- ./gradlew assembleDebug
|
||||
- ./gradlew assembleRelease
|
||||
|
||||
artifacts:
|
||||
paths:
|
||||
- ./app/build/outputs/apk/debug/linphone-android-debug-*.apk
|
||||
- ./app/build/outputs/apk/release/linphone-android-release-*.apk
|
||||
when: always
|
||||
expire_in: 1 week
|
||||
|
||||
|
||||
.scheduled-job-android:
|
||||
extends: job-android
|
||||
only:
|
||||
- schedules
|
||||
12
.gitlab-ci-files/job-upload.yml
Normal file
12
.gitlab-ci-files/job-upload.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
job-android-upload:
|
||||
|
||||
stage: deploy
|
||||
tags: [ "deploy" ]
|
||||
|
||||
only:
|
||||
- schedules
|
||||
dependencies:
|
||||
- job-android
|
||||
|
||||
script:
|
||||
- cd app/build/outputs/apk/ && rsync ./debug/*.apk $DEPLOY_SERVER:$ANDROID_DEPLOY_DIRECTORY
|
||||
19
.gitlab-ci.yml
Normal file
19
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#################################################
|
||||
# Base configuration
|
||||
#################################################
|
||||
|
||||
|
||||
|
||||
#################################################
|
||||
# Platforms to test
|
||||
#################################################
|
||||
|
||||
|
||||
include:
|
||||
- '.gitlab-ci-files/job-android.yml'
|
||||
- '.gitlab-ci-files/job-upload.yml'
|
||||
|
||||
|
||||
stages:
|
||||
- build
|
||||
- deploy
|
||||
122
.gitmodules
vendored
122
.gitmodules
vendored
|
|
@ -1,122 +0,0 @@
|
|||
[submodule "submodules/linphone"]
|
||||
path = submodules/linphone
|
||||
url = https://gitlab.linphone.org/BC/public/linphone.git
|
||||
[submodule "submodules/externals/gsm"]
|
||||
path = submodules/externals/gsm
|
||||
url = https://gitlab.linphone.org/BC/public/external/gsm.git
|
||||
[submodule "submodules/externals/speex"]
|
||||
path = submodules/externals/speex
|
||||
url = https://gitlab.linphone.org/BC/public/external/speex.git
|
||||
[submodule "submodules/externals/ffmpeg"]
|
||||
path = submodules/externals/ffmpeg
|
||||
url = https://gitlab.linphone.org/BC/public/external/ffmpeg.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/externals/x264"]
|
||||
path = submodules/externals/x264
|
||||
url = https://gitlab.linphone.org/BC/public/external/x264.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/msx264"]
|
||||
path = submodules/msx264
|
||||
url = https://gitlab.linphone.org/BC/public/msx264.git
|
||||
[submodule "submodules/externals/opencore-amr"]
|
||||
path = submodules/externals/opencore-amr
|
||||
url = https://gitlab.linphone.org/BC/public/external/opencore-amr.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/msamr"]
|
||||
path = submodules/msamr
|
||||
url = https://gitlab.linphone.org/BC/public/msamr.git
|
||||
[submodule "submodules/externals/libvpx"]
|
||||
path = submodules/externals/libvpx
|
||||
url = https://gitlab.linphone.org/BC/public/external/libvpx.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/bzrtp"]
|
||||
path = submodules/bzrtp
|
||||
url = https://gitlab.linphone.org/BC/public/bzrtp.git
|
||||
[submodule "submodules/externals/srtp"]
|
||||
path = submodules/externals/srtp
|
||||
url = https://gitlab.linphone.org/BC/public/external/srtp.git
|
||||
[submodule "submodules/mssilk"]
|
||||
path = submodules/mssilk
|
||||
url = https://gitlab.linphone.org/BC/public/mssilk.git
|
||||
[submodule "submodules/bcg729"]
|
||||
path = submodules/bcg729
|
||||
url = https://gitlab.linphone.org/BC/public/bcg729.git
|
||||
[submodule "submodules/belle-sip"]
|
||||
path = submodules/belle-sip
|
||||
url = https://gitlab.linphone.org/BC/public/belle-sip.git
|
||||
[submodule "submodules/externals/libxml2"]
|
||||
path = submodules/externals/libxml2
|
||||
url = https://gitlab.linphone.org/BC/public/external/libxml2.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/externals/libupnp"]
|
||||
path = submodules/externals/libupnp
|
||||
url = https://gitlab.linphone.org/BC/public/external/libupnp.git
|
||||
[submodule "submodules/externals/opus"]
|
||||
path = submodules/externals/opus
|
||||
url = https://gitlab.linphone.org/BC/public/external/opus.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/mswebrtc"]
|
||||
path = submodules/mswebrtc
|
||||
url = https://gitlab.linphone.org/BC/public/mswebrtc.git
|
||||
[submodule "submodules/msopenh264"]
|
||||
path = submodules/msopenh264
|
||||
url = https://gitlab.linphone.org/BC/public/msopenh264.git
|
||||
[submodule "submodules/externals/openh264"]
|
||||
path = submodules/externals/openh264
|
||||
url = https://gitlab.linphone.org/BC/public/external/openh264.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/mscodec2"]
|
||||
path = submodules/mscodec2
|
||||
url = https://gitlab.linphone.org/BC/public/mscodec2.git
|
||||
[submodule "submodules/bctoolbox"]
|
||||
path = submodules/bctoolbox
|
||||
url = https://gitlab.linphone.org/BC/public/bctoolbox.git
|
||||
[submodule "submodules/externals/mbedtls"]
|
||||
path = submodules/externals/mbedtls
|
||||
url = https://gitlab.linphone.org/BC/public/external/mbedtls.git
|
||||
ignore = dirty
|
||||
[submodule "submodules/cmake-builder"]
|
||||
path = submodules/cmake-builder
|
||||
url = https://gitlab.linphone.org/BC/public/linphone-cmake-builder.git
|
||||
[submodule "submodules/externals/bv16-floatingpoint"]
|
||||
path = submodules/externals/bv16-floatingpoint
|
||||
url = https://gitlab.linphone.org/BC/public/external/bv16-floatingpoint.git
|
||||
[submodule "submodules/belr"]
|
||||
path = submodules/belr
|
||||
url = https://gitlab.linphone.org/BC/public/belr.git
|
||||
[submodule "submodules/belcard"]
|
||||
path = submodules/belcard
|
||||
url = https://gitlab.linphone.org/BC/public/belcard.git
|
||||
[submodule "submodules/bcunit"]
|
||||
path = submodules/bcunit
|
||||
url = https://gitlab.linphone.org/BC/public/bcunit.git
|
||||
[submodule "submodules/externals/vo-amrwbenc"]
|
||||
path = submodules/externals/vo-amrwbenc
|
||||
url = https://gitlab.linphone.org/BC/public/external/vo-amrwbenc.git
|
||||
[submodule "submodules/externals/codec2"]
|
||||
path = submodules/externals/codec2
|
||||
url = https://gitlab.linphone.org/BC/public/external/codec2.git
|
||||
[submodule "submodules/externals/libjpeg-turbo"]
|
||||
path = submodules/externals/libjpeg-turbo
|
||||
url = https://gitlab.linphone.org/BC/public/external/libjpeg-turbo.git
|
||||
[submodule "submodules/mediastreamer2"]
|
||||
path = submodules/mediastreamer2
|
||||
url = https://gitlab.linphone.org/BC/public/mediastreamer2.git
|
||||
[submodule "submodules/oRTP"]
|
||||
path = submodules/oRTP
|
||||
url = https://gitlab.linphone.org/BC/public/ortp.git
|
||||
[submodule "submodules/bcmatroska2"]
|
||||
path = submodules/bcmatroska2
|
||||
url = https://gitlab.linphone.org/BC/public/bcmatroska2.git
|
||||
[submodule "submodules/externals/xerces-c"]
|
||||
path = submodules/externals/xerces-c
|
||||
url = https://gitlab.linphone.org/BC/public/external/xerces-c.git
|
||||
[submodule "submodules/externals/libxsd"]
|
||||
path = submodules/externals/libxsd
|
||||
url = https://gitlab.linphone.org/BC/public/external/libxsd.git
|
||||
[submodule "submodules/externals/soci"]
|
||||
path = submodules/externals/soci
|
||||
url = https://gitlab.linphone.org/BC/public/external/soci.git
|
||||
[submodule "submodules/externals/zxing-cpp"]
|
||||
path = submodules/externals/zxing-cpp
|
||||
url = https://gitlab.linphone.org/BC/public/external/zxing-cpp.git
|
||||
|
|
@ -5,6 +5,6 @@ minimum_perc = 1
|
|||
type = ANDROID
|
||||
|
||||
[linphone-android.stringsxml]
|
||||
file_filter = res/values-<lang>/strings.xml
|
||||
source_file = res/values/strings.xml
|
||||
file_filter = app/src/main/res/values-<lang>/strings.xml
|
||||
source_file = app/src/main/res/values/strings.xml
|
||||
source_lang = en
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="org.linphone"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="4001"
|
||||
android:versionName="4.0.0">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="28"/>
|
||||
</manifest>
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
|
|
@ -10,9 +10,39 @@ Group changes to describe their impact on the project, as follows:
|
|||
Fixed for any bug fixes.
|
||||
Security to invite users to upgrade in case of vulnerabilities.
|
||||
|
||||
## [Incomming]
|
||||
- feature: support of H265 codec.
|
||||
- feature: use TextureView instead of GL2JNIView, easier to use and will fix issues
|
||||
## [4.1] - 2019-xx-xx
|
||||
|
||||
### Improvements
|
||||
- Improved UI.
|
||||
- Added adaptive icon for Android 8+.
|
||||
- Use of binary SDK hosted in Maven repository instead of having to build it manually.
|
||||
- Asynchronous fetch of native contacts.
|
||||
- Removed unused graphical resources and improved some existing ones.
|
||||
- Updated translations.
|
||||
- Updated sliders used to answer or hangup incoming call so they can be used with accessibility tools.
|
||||
- Reworked settings, new ones are available, some unused have been removed.
|
||||
- Video overlay also displays local preview now.
|
||||
- In About fragment, the license text is a link to the full license on gnu.org.
|
||||
|
||||
### Features
|
||||
- Added new end to end LIME encryption for instant messaging, both for single chat rooms and group ones.
|
||||
- Send multiple files with text in the same chat message.
|
||||
- Allow multiple images to be shared from an external app through linphone at once.
|
||||
- Support of H265 codec.
|
||||
- Use TextureView instead of GL2JNIView, easier to use and will fix issues.
|
||||
- Send SMS to invite your friends in using Linphone.
|
||||
- Reply to / mark as read chat message in notification.
|
||||
- Answer or hangup calls in notification.
|
||||
- Setting to automatically download incoming files, either always, never or depending on their sizes. This adds the feature of having images received by file transfer in notifications on Android 8+.
|
||||
- Call recording.
|
||||
- Get remote provisioning URL from QR code scan.
|
||||
- Allow rich input while composing chat message to easily send images from keyboard.
|
||||
- Animated GIFs are no longer displayed as static images.
|
||||
- A dark mode is available.
|
||||
- List all calls with the same SIP address in history detail view.
|
||||
|
||||
### Fixes
|
||||
- Fixed basic chat room with same correspondant with multiple local accounts always displaying the same one when clicking on it in chat rooms list view.
|
||||
|
||||
## [4.0.1] - 2018-06-26
|
||||
|
||||
|
|
@ -34,7 +64,6 @@ Group changes to describe their impact on the project, as follows:
|
|||
### Fixed
|
||||
- issue with changing push notification token not passed to library, possibly resulting in a loss of incoming calls.
|
||||
|
||||
|
||||
## [3.3.0] - 2017-10-18
|
||||
|
||||
### Added
|
||||
|
|
|
|||
156
README.md
156
README.md
|
|
@ -1,100 +1,104 @@
|
|||
[](https://gitlab.linphone.org/BC/public/linphone-android/commits/master)
|
||||
[](https://gitlab.linphone.org/BC/public/linphone-android/commits/feature/release-4.1)
|
||||
|
||||
Linphone is a free VoIP and video softphone based on the SIP protocol.
|
||||
|
||||
# COMPILATION INSTRUCTIONS
|
||||
# What's new
|
||||
|
||||
## To build liblinphone for Android, you must:
|
||||
Now the default way of building linphone-android is to download the AAR SDK in our maven repository.
|
||||
Compared to previous versions, this project no longer uses submodules developper has to build in order to get a working app.
|
||||
However, if you wish to use a locally compiled SDK see below how to proceed.
|
||||
|
||||
1. Download the latest Android sdk with platform-tools and tools updated to latest revision, then add both 'tools' and 'platform-tools' folders in your path and the android-sdk folder to ANDROID_HOME environment variable.
|
||||
We offer different flavors for the SDK in our maven repository: org.linphone.no-video (a build without video) and org.linphone.legacy (old java wrapper if you didn't migrate your app code to the new one yet).
|
||||
|
||||
2. Download the latest Android NDK from google and add it to your path (no symlink !!!) and ANDROID_NDK environment variable.
|
||||
The repository structure has also been cleaned and updated, and changing the package name can now be done in a single step.
|
||||
This allows developpers to keep a stable version as well as a developpment one on the same device easily.
|
||||
|
||||
3. Install _yasm_, _nasm_ (For OpenH224 support only), _python_, _pkg_config_, _doxygen_, _graphviz_ and _cmake(>=3.12)_.
|
||||
* On 64 bits linux systems you'll need the _ia32-libs_ package.
|
||||
* With the latest Debian (multiarch), you need this:
|
||||
* `dpkg --add-architecture i386`
|
||||
* `aptitude update`
|
||||
* `aptitude install libstdc++6:i386 libgcc1:i386 zlib1g:i386 libncurses5:i386`
|
||||
# Building the app
|
||||
|
||||
4. Run `./prepare.py` in the top level directory. This will configure the build and generate a Makefile in the top level directory. Some options can be passed to choose what you want to include in the build and the platforms for which you want to build. Use `./prepare.py --help` to see what these options are.
|
||||
|
||||
5. Run the Makefile script in the top level directory, `make`.
|
||||
|
||||
6. _(optional)_ To install the generated apk into a plugged device, run `make install`.
|
||||
|
||||
7. _(optional)_ To generate a liblinphone SDK zip containing a full jar and native libraries, run `make liblinphone-android-sdk`
|
||||
|
||||
8. _(optional)_ To generate a libmediastreamer2 SDK zip containing a full jar and native libraries, run `make mediastreamer2-sdk`
|
||||
|
||||
9. _(optional)_ To generate a signed apk to publish on the Google Play, run `make release`. Make sure you filled the gradle.properties values for version.name, store file, store password, key alias and key password to correctly sign the generated apk:
|
||||
* RELEASE_STORE_FILE=""
|
||||
* RELEASE_STORE_PASSWORD=
|
||||
* RELEASE_KEY_ALIAS=
|
||||
* RELEASE_KEY_PASSWORD=
|
||||
|
||||
If you don't, the passwords will be asked at the signing phase.
|
||||
|
||||
10. _(optional)_ Once you compiled the libraries succesfully with 'make', you can reduce the compilation time using 'make quick': it will only generate a new APK from java files.
|
||||
|
||||
## To create an apk with a different package name
|
||||
|
||||
You need to edit the build.gradle file:
|
||||
|
||||
1. look for the function named "getPackageName()" and change it value accordingly
|
||||
2. also update the values in the AndroidManifest file where the comment <!-- Change package ! --> appears
|
||||
3. change the package name also in the files: res/xml/syncadapter.xml, res/xml/contacts.xml and res/values/non_localizable_custom where <!-- Change package ! --> appears
|
||||
4. run again the Makefile script by calling "make"
|
||||
|
||||
## To run the liblinphone test suite on android
|
||||
|
||||
Simply run `make liblinphone_tester`. This will be build everything, generate an apk, and install it on the connected device if any.
|
||||
|
||||
You can speed up the compilation by using ccache (compiler cache, see [ccache.samba.org](https://ccache.samba.org/)). Give the *"-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"* options to the *prepare.py* script.
|
||||
|
||||
# PUSH NOTIFICATION
|
||||
|
||||
## Firebase
|
||||
|
||||
To enable firebase in Linphone, just add your 'google-service.json' in project root, add your key at 'push_sender_id' and add 'firebase' at 'push_type' in 'res/values/non_localizable_custom.xml'
|
||||
Be sure to have all services for Firebase in your 'AndroidManifest.xml'
|
||||
|
||||
# TROUBLESHOOTING
|
||||
|
||||
If you encounter the following issue:
|
||||
If you have Android Studio, simply open the project, wait for the gradle synchronization and then build/install the app.
|
||||
It will download the linphone library from our Maven repository as an AAR file so you don't have to build anything yourself.
|
||||
|
||||
If you don't have Android Studio, you can build and install the app using gradle:
|
||||
```
|
||||
E/dalvikvm( 2465): dlopen("/data/app-lib/org.linphone-1/liblinphone-armeabi-v7a.so") failed:
|
||||
Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol "rand" referenced
|
||||
by "liblinphone-armeabi-v7a.so"
|
||||
./gradlew assembleDebug
|
||||
```
|
||||
will compile the APK file (assembleRelease to instead if you want to build a release package), and then
|
||||
```
|
||||
./gradlew installDebug
|
||||
```
|
||||
to install the generated APK in the previous step (use installRelease instead if you built a release package).
|
||||
|
||||
APK files are stored within ```./app/build/outputs/apk/debug/``` and ```./app/build/outputs/apk/release/``` directories.
|
||||
|
||||
## Building a local SDK
|
||||
|
||||
1. Clone the linphone-sdk repository from out gitlab:
|
||||
```
|
||||
git clone https://gitlab.linphone.org/BC/public/linphone-sdk.git --recursive
|
||||
```
|
||||
|
||||
It's because you have installed the android-21 platform (which is chosen automatically because it's the most recent) and you deployed the apk on a android < 5 device.
|
||||
|
||||
To fix this, in the Makefile, force *ANDROID_MOST_RECENT_TARGET=android-19*.
|
||||
|
||||
If you encounter troubles with the make clean target and you are using the 8e android ndk, the solution can be found [here](https://groups.google.com/forum/?fromgroups=#!topic/android-ndk/3wIbb-h3nDU).
|
||||
|
||||
If you built the app using eclipse, ensure you ran at least once the make command (see above steps 0 to 3) ! Else you'll have this exceptions:
|
||||
2. Follow the instructions in the linphone-sdk/README file to build the SDK.
|
||||
|
||||
3. Edit in the linphone-sdk-android folder of this project the symbolic link (debug and/or release) to the generated AAR.
|
||||
We recommend to at least create the link for the release AAR that can be used for debug APK flavor because it is smaller and will reduce the time required to install the APK.
|
||||
```
|
||||
FATAL EXCEPTION: main
|
||||
java.lang.ExceptionInInitializerError
|
||||
...
|
||||
Caused by: java.lang.UnsatisfiedLinkError: Couldn't load linphone-armeabi-v7a: findLibrary
|
||||
returned null
|
||||
ln -s <path to linphone-sdk>/linphone-sdk/build/linphone-sdk/bin/outputs/aar/linphone-sdk-android-release.aar linphone-sdk-android/linphone-sdk-android-release.aar
|
||||
ln -s <path to linphone-sdk>/linphone-sdk/build/linphone-sdk/bin/outputs/aar/linphone-sdk-android-debug.aar linphone-sdk-android/linphone-sdk-android-debug.aar
|
||||
```
|
||||
|
||||
# BUILD OPTIONS
|
||||
4. Rebuild the app in Android Studio.
|
||||
|
||||
The build options are to be passed to the *prepare.py* script. For example to enable the x264 encoder give the *"-DENABLE_X264=YES"* to *prepare.py*.
|
||||
## Native debugging
|
||||
|
||||
The available options can be listed with the `./prepare.py --list-features`
|
||||
1. Install LLDB from SDK Tools in Android-studio.
|
||||
|
||||
2. In Android-studio go to Run->Edit Configurations->Debugger.
|
||||
|
||||
3. Select 'Dual' or 'Native' and add the path to linphone-sdk libraries.
|
||||
|
||||
4. Open native file and put your breakpoint on it.
|
||||
|
||||
5. Make sure you are using the debug AAR in the app/build.gradle script and not the release one (to have faster builds by default the release AAR is used even for debug APK flavor).
|
||||
|
||||
6. Debug app.
|
||||
|
||||
## Create an APK with a different package name
|
||||
|
||||
Before the 4.1 release, there were a lot of files to edit to change the package name.
|
||||
Now, simply edit the app/build.gradle file and change the value returned by method ```getPackageName()```
|
||||
The next build will automatically use this value everywhere thanks to ```manifestPlaceholders``` feature of gradle and Android.
|
||||
|
||||
You may have already noticed that the app installed by Android Studio has ```org.linphone.debug``` package name.
|
||||
If you build the app as release, the package name will be ```org.linphone```.
|
||||
|
||||
## Firebase push notifications
|
||||
|
||||
Now that Google Cloud Messaging has been deprecated and will be completely removed on April 11th 2019, the only official way of using push notifications is through Firebase.
|
||||
|
||||
However to make Firebase push notifications work, the project needs to have a file named app/google-services.json that contains some confidential informations, so you won't find it (it has been added to the .gitignore file).
|
||||
This means that if you compile this project, you won't have push notification feature working in the app!
|
||||
|
||||
To enable them, just add your own ```google-services.json``` in the app folder.
|
||||
|
||||
## Translations
|
||||
|
||||
We use transifex so the community can translate the strings of the app in their own language.
|
||||
|
||||
Note for developpers: here's how to push/pull string resources to/from transifex:
|
||||
```
|
||||
tx pull -af
|
||||
```
|
||||
to update local translations with latest transifex changes
|
||||
```
|
||||
tx push -s -f --no-interactive
|
||||
```
|
||||
to push new strings to transifex so they can be translated.
|
||||
|
||||
# CONTRIBUTIONS
|
||||
|
||||
In order to submit a patch for inclusion in linphone's source code:
|
||||
|
||||
1. First make sure your patch applies to latest git sources before submitting: patches made to old versions can't be merged.
|
||||
2. Fill out and send us an email with the link of pullrequest and the [Contributor Agreement](http://www.belledonne-communications.com/downloads/Belledonne_communications_CA.pdf) for your patch to be included in the git tree. The goal of this agreement to grant us peaceful exercise of our rights on the linphone source code, while not losing your rights on your contribution.
|
||||
1. First make sure your patch applies to latest git sources before submitting: patches made to old versions can't and won't be merged.
|
||||
2. Fill out and send us an email with the link of pullrequest and the [Contributor Agreement](http://www.belledonne-communications.com/downloads/Belledonne_communications_CA.pdf) for your patch to be included in the git tree.
|
||||
|
||||
The goal of this agreement to grant us peaceful exercise of our rights on the linphone source code, while not losing your rights on your contribution.
|
||||
|
|
|
|||
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
198
app/build.gradle
Normal file
198
app/build.gradle
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
static def getPackageName() {
|
||||
return "org.linphone"
|
||||
}
|
||||
|
||||
static def firebaseEnabled() {
|
||||
File googleFile = new File('app/google-services.json')
|
||||
return googleFile.exists()
|
||||
}
|
||||
|
||||
static def isLocalDebugAarAvailable() {
|
||||
File debugAar = new File('linphone-sdk-android/linphone-sdk-android-debug.aar')
|
||||
return debugAar.exists()
|
||||
}
|
||||
|
||||
static def isLocalReleaseAarAvailable() {
|
||||
File releaseAar = new File('linphone-sdk-android/linphone-sdk-android-release.aar')
|
||||
return releaseAar.exists()
|
||||
}
|
||||
|
||||
static def isLocalAarAvailable() {
|
||||
return isLocalDebugAarAvailable() || isLocalReleaseAarAvailable()
|
||||
}
|
||||
|
||||
///// Exclude Files /////
|
||||
|
||||
def excludeFiles = []
|
||||
if (!firebaseEnabled()) {
|
||||
excludeFiles.add('**/Firebase*')
|
||||
println '[Push Notification] Firebase disabled'
|
||||
}
|
||||
|
||||
def excludePackage = []
|
||||
|
||||
excludePackage.add('**/gdb.*')
|
||||
excludePackage.add('**/libopenh264**')
|
||||
excludePackage.add('**/**tester**')
|
||||
excludePackage.add('**/LICENSE.txt')
|
||||
|
||||
|
||||
def gitVersion = new ByteArrayOutputStream()
|
||||
|
||||
task getGitVersion {
|
||||
exec {
|
||||
commandLine 'git', 'describe', '--always'
|
||||
standardOutput = gitVersion
|
||||
}
|
||||
doLast {
|
||||
gitVersion = gitVersion.toString().trim()
|
||||
println("Git version: " + gitVersion)
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks['preBuild'].dependsOn 'getGitVersion'
|
||||
|
||||
/////////////////////////
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
// Replace snapshots by releases for releases !
|
||||
url "https://linphone.org/releases/maven_repository"
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 4119
|
||||
versionName "4.1"
|
||||
applicationId getPackageName()
|
||||
multiDexEnabled true
|
||||
manifestPlaceholders = [linphone_address_mime_type:"vnd.android.cursor.item/vnd." + getPackageName() + ".provider.sip_address"]
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all {
|
||||
outputFileName = "linphone-android-${variant.buildType.name}-${gitVersion.toString().trim()}.apk"
|
||||
}
|
||||
}
|
||||
|
||||
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
def keystoreProperties = new Properties()
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(keystoreProperties['storeFile'])
|
||||
storePassword keystoreProperties['storePassword']
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
signingConfig signingConfigs.release
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
|
||||
resValue "string", "sync_account_type", getPackageName() + ".sync"
|
||||
resValue "string", "file_provider", getPackageName() + ".provider"
|
||||
resValue "string", "linphone_address_mime_type", "vnd.android.cursor.item/vnd." + getPackageName() + ".provider.sip_address"
|
||||
|
||||
if (!firebaseEnabled()) {
|
||||
resValue "string", "gcm_defaultSenderId", "none"
|
||||
}
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
debuggable true
|
||||
jniDebuggable true
|
||||
versionNameSuffix '-debug'
|
||||
|
||||
resValue "string", "sync_account_type", getPackageName() + ".sync"
|
||||
resValue "string", "file_provider", getPackageName() + ".provider"
|
||||
resValue "string", "linphone_address_mime_type", "vnd.android.cursor.item/vnd." + getPackageName() + ".provider.sip_address"
|
||||
|
||||
if (!firebaseEnabled()) {
|
||||
resValue "string", "gcm_defaultSenderId", "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.excludes = excludeFiles
|
||||
|
||||
packagingOptions {
|
||||
excludes = excludePackage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst 'META-INF/NOTICE'
|
||||
pickFirst 'META-INF/LICENSE'
|
||||
exclude 'META-INF/MANIFEST.MF'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
if (firebaseEnabled()) {
|
||||
implementation 'com.google.firebase:firebase-messaging:17.5.0'
|
||||
}
|
||||
implementation 'com.android.billingclient:billing:1.2'
|
||||
implementation 'org.apache.commons:commons-compress:1.18'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha04'
|
||||
implementation 'com.google.android:flexbox:1.1.0'
|
||||
implementation 'com.github.bumptech.glide:glide:4.9.0'
|
||||
|
||||
if (isLocalAarAvailable()) {
|
||||
if (isLocalReleaseAarAvailable()) {
|
||||
//debug AAR is a lot bigger than release one, and APK install time will be much longer so use release one for day-to-day development
|
||||
implementation project(path: ":linphone-sdk-android", configuration: 'release')
|
||||
} else {
|
||||
releaseImplementation project(path: ":linphone-sdk-android", configuration: 'release')
|
||||
debugImplementation project(path: ":linphone-sdk-android", configuration: 'debug')
|
||||
}
|
||||
} else {
|
||||
debugImplementation "org.linphone:linphone-sdk-android-debug:4.1+"
|
||||
releaseImplementation "org.linphone:linphone-sdk-android:4.1+"
|
||||
}
|
||||
}
|
||||
if (firebaseEnabled()) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
|
||||
task generateContactsXml(type: Copy) {
|
||||
from 'contacts.xml'
|
||||
into "src/main/res/xml/"
|
||||
filter {
|
||||
line -> line
|
||||
.replaceAll('%%AUTO_GENERATED%%', 'This file has been automatically generated, do not edit or commit !')
|
||||
.replaceAll('%%PACKAGE_NAME%%', getPackageName())
|
||||
|
||||
}
|
||||
}
|
||||
project.tasks['preBuild'].dependsOn 'generateContactsXml'
|
||||
|
||||
apply plugin: "com.diffplug.gradle.spotless"
|
||||
spotless {
|
||||
java {
|
||||
target '**/*.java'
|
||||
googleJavaFormat('1.6').aosp()
|
||||
removeUnusedImports()
|
||||
}
|
||||
}
|
||||
project.tasks['preBuild'].dependsOn 'spotlessApply'
|
||||
11
app/contacts.xml
Normal file
11
app/contacts.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- %%AUTO_GENERATED%% -->
|
||||
<ContactsDataKind
|
||||
android:detailColumn="data3"
|
||||
android:detailSocialSummary="true"
|
||||
android:icon="@drawable/linphone_logo"
|
||||
android:mimeType="vnd.android.cursor.item/vnd.%%PACKAGE_NAME%%.provider.sip_address"
|
||||
android:summaryColumn="data2" />
|
||||
<!-- You can use @string/linphone_address_mime_type above ! You have to hardcode it... -->
|
||||
</ContactsSource>
|
||||
57
app/google-services.json
Normal file
57
app/google-services.json
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "929724111839",
|
||||
"firebase_url": "https://linphone-android-8a563.firebaseio.com",
|
||||
"project_id": "linphone-android-8a563",
|
||||
"storage_bucket": "linphone-android-8a563.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:929724111839:android:4662ea9a056188c4",
|
||||
"android_client_info": {
|
||||
"package_name": "org.linphone"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "929724111839-co5kffto4j7dets7oolvfv0056cvpfbl.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "org.linphone",
|
||||
"certificate_hash": "85463a95603f7b6331899b74b85d53d043dcd500"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "929724111839-v5so1tcd65iil7dd7sde8jgii44h8luf.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyCKrwWhkbA7Iy3wpEI8_ZvKOMp5jf6vV6A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:929724111839:android:3cf90ee1d2f8fcb6",
|
||||
"android_client_info": {
|
||||
"package_name": "org.linphone.debug"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "929724111839-v5so1tcd65iil7dd7sde8jgii44h8luf.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyCKrwWhkbA7Iy3wpEI8_ZvKOMp5jf6vV6A"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
1
app/proguard-rules.pro
vendored
Normal file
1
app/proguard-rules.pro
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
-dontwarn org.apache.**
|
||||
|
|
@ -1,153 +1,163 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="org.linphone"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="4002"
|
||||
android:versionName="4.0.1">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.linphone"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="28"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
|
||||
<uses-permission android:name="android.permission.CALL_PHONE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<!-- Needed to allow Linphone to install on tablets, since android.permission.CALL_PHONE implies android.hardware.telephony is required -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false"/>
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<!-- Needed to allow Linphone to install on tablets, since android.permission.CAMERA implies android.hardware.camera and android.hardware.camera.autofocus are required -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false"/>
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<!-- Needed to store received images if the user wants to -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<!-- Needed to use our own Contact editor -->
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<!-- Needed to route the audio to the bluetooth headset if available -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
<!-- Needed to pre fill the wizard email field (only if enabled in custom settings) -->
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
|
||||
<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.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<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" />
|
||||
<!-- Needed for in-app purchase -->
|
||||
<!-- <uses-permission android:name="com.android.vending.BILLING"/> -->
|
||||
<!-- Needed for overlay widget and floating notifications -->
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<!-- Needed for kill application yourself -->
|
||||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
|
||||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:xlargeScreens="true"/>
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true">
|
||||
android:largeHeap="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round">
|
||||
|
||||
<activity
|
||||
android:name=".activities.LinphoneLauncherActivity"
|
||||
android:name=".LinphoneLauncherActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/NoTitle"
|
||||
android:launchMode="singleTask"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/LinphoneStyleLight"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".LinphoneActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CALL"/>
|
||||
<action android:name="android.intent.action.CALL_PRIVILEGED"/>
|
||||
<action android:name="android.intent.action.CALL" />
|
||||
<action android:name="android.intent.action.CALL_PRIVILEGED" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="tel"/>
|
||||
<data android:scheme="sip"/>
|
||||
<data android:scheme="tel" />
|
||||
<data android:scheme="sip" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SENDTO"/>
|
||||
<action android:name="android.intent.action.SENDTO" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="sip"/>
|
||||
<data android:scheme="imto"/>
|
||||
<data android:scheme="sip" />
|
||||
<data android:scheme="imto" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<data android:mimeType="@string/sync_mimetype"/> <!-- Change package in res/values/non_localizable_custom.xml ! -->
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="${linphone_address_mime_type}" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.OPENABLE"/>
|
||||
<data android:mimeType="text/*"/>
|
||||
<data android:mimeType="image/*"/>
|
||||
<data android:mimeType="audio/*"/>
|
||||
<data android:mimeType="video/*"/>
|
||||
<data android:mimeType="application/*"/>
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
|
||||
<data android:mimeType="text/*" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="audio/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
<data android:mimeType="application/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.linphone.intent.action.CallLaunched" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activities.LinphoneActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/NoTitle">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".call.CallIncomingActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/NoTitle">
|
||||
android:noHistory="true"
|
||||
android:showWhenLocked="true"
|
||||
android:turnScreenOn="true"
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".call.CallOutgoingActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/NoTitle">
|
||||
android:noHistory="true"
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".call.CallActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/FullScreen">
|
||||
android:noHistory="true"
|
||||
android:showWhenLocked="true"
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
<!--
|
||||
<intent-filter>
|
||||
|
|
@ -161,134 +171,131 @@
|
|||
<activity
|
||||
android:name=".assistant.AssistantActivity"
|
||||
android:screenOrientation="behind"
|
||||
android:theme="@style/NoTitle">
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".purchase.InAppPurchaseActivity"
|
||||
android:screenOrientation="nosensor"
|
||||
android:theme="@style/NoTitle">
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".assistant.RemoteProvisioningLoginActivity"
|
||||
android:screenOrientation="nosensor"
|
||||
android:theme="@style/NoTitle">
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".assistant.RemoteProvisioningActivity"
|
||||
android:screenOrientation="nosensor"
|
||||
android:theme="@style/NoTitle">
|
||||
android:theme="@style/LinphoneStyleLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<data android:scheme="linphone-config"/> <!-- Change if needed -->
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<data android:scheme="linphone-config" /> <!-- Change if needed -->
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".LinphoneService"
|
||||
android:label="@string/service_name"/>
|
||||
android:label="@string/service_name" />
|
||||
<service
|
||||
android:name=".sync.SyncService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
<action android:name="android.content.SyncAdapter" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/syncadapter"/>
|
||||
android:resource="@xml/syncadapter" />
|
||||
<meta-data
|
||||
android:name="android.provider.CONTACTS_STRUCTURE"
|
||||
android:resource="@xml/contacts"/>
|
||||
android:resource="@xml/contacts" />
|
||||
</service>
|
||||
<service android:name=".sync.AuthenticationService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator"/>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator"/>
|
||||
android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.BluetoothManager"
|
||||
android:enabled="false">
|
||||
</receiver>
|
||||
android:enabled="false"/>
|
||||
<receiver android:name=".receivers.BootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_SHUTDOWN"/>
|
||||
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".receivers.PhoneStateChangedReceiver">
|
||||
<intent-filter android:priority="999">
|
||||
<action android:name="android.intent.action.PHONE_STATE"/>
|
||||
<action android:name="android.intent.action.PHONE_STATE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- This one needs to be registered from application -->
|
||||
<receiver android:name=".receivers.KeepAliveReceiver"/>
|
||||
|
||||
<receiver android:name=".receivers.HookReceiver" >
|
||||
<receiver android:name=".receivers.HookReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="com.base.module.phone.HOOKEVENT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receivers.OutgoingCallReceiver"
|
||||
android:permission="android.permission.PROCESS_OUTGOING_CALLS">
|
||||
<!--<receiver
|
||||
android:name=".receivers.OutgoingCallReceiver"
|
||||
android:permission="android.permission.PROCESS_OUTGOING_CALLS">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</receiver>-->
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.AccountEnableReceiver"
|
||||
android:permission="android.permission.USE_SIP">
|
||||
<intent-filter>
|
||||
<action android:name="org.linphone.intent.ACCOUNTACTIVATE"/>
|
||||
<action android:name="org.linphone.intent.ACCOUNTACTIVATE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name=".firebase.FirebaseIdService" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".firebase.FirebaseMessaging">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="org.linphone.provider"
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths"/>
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<activity android:name=".activities.LinphoneGenericActivity"/>
|
||||
<activity android:name=".utils.LinphoneGenericActivity" />
|
||||
|
||||
<receiver
|
||||
android:name=".notifications.NotificationBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
2046
app/src/main/java/org/linphone/LinphoneActivity.java
Normal file
2046
app/src/main/java/org/linphone/LinphoneActivity.java
Normal file
File diff suppressed because it is too large
Load diff
107
app/src/main/java/org/linphone/LinphoneLauncherActivity.java
Normal file
107
app/src/main/java/org/linphone/LinphoneLauncherActivity.java
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package org.linphone;
|
||||
|
||||
/*
|
||||
LinphoneLauncherActivity.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import static android.content.Intent.ACTION_MAIN;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import org.linphone.assistant.RemoteProvisioningActivity;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
/** Launch Linphone main activity when Service is ready. */
|
||||
public class LinphoneLauncherActivity extends Activity {
|
||||
|
||||
private Handler mHandler;
|
||||
private ServiceWaitThread mServiceThread;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// Hack to avoid to draw twice LinphoneActivity on tablets
|
||||
if (getResources().getBoolean(R.bool.orientation_portrait_only)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
if (getResources().getBoolean(R.bool.use_full_screen_image_splashscreen)) {
|
||||
setContentView(R.layout.launch_screen_full_image);
|
||||
} else {
|
||||
setContentView(R.layout.launch_screen);
|
||||
}
|
||||
|
||||
mHandler = new Handler();
|
||||
|
||||
if (LinphoneService.isReady()) {
|
||||
onServiceReady();
|
||||
} else {
|
||||
// start linphone as background
|
||||
startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class));
|
||||
mServiceThread = new ServiceWaitThread();
|
||||
mServiceThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void onServiceReady() {
|
||||
final Class<? extends Activity> classToStart;
|
||||
/*if (getResources().getBoolean(R.bool.show_tutorials_instead_of_app)) {
|
||||
classToStart = TutorialLauncherActivity.class;
|
||||
} else */
|
||||
if (getResources().getBoolean(R.bool.display_sms_remote_provisioning_activity)
|
||||
&& LinphonePreferences.instance().isFirstRemoteProvisioning()) {
|
||||
classToStart = RemoteProvisioningActivity.class;
|
||||
} else {
|
||||
classToStart = LinphoneActivity.class;
|
||||
}
|
||||
|
||||
mHandler.postDelayed(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startActivity(
|
||||
getIntent().setClass(LinphoneLauncherActivity.this, classToStart));
|
||||
}
|
||||
},
|
||||
500);
|
||||
|
||||
LinphoneManager.getInstance().changeStatusToOnline();
|
||||
}
|
||||
|
||||
private class ServiceWaitThread extends Thread {
|
||||
public void run() {
|
||||
while (!LinphoneService.isReady()) {
|
||||
try {
|
||||
sleep(30);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("waiting thread sleep() has been interrupted");
|
||||
}
|
||||
}
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onServiceReady();
|
||||
}
|
||||
});
|
||||
mServiceThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
1869
app/src/main/java/org/linphone/LinphoneManager.java
Normal file
1869
app/src/main/java/org/linphone/LinphoneManager.java
Normal file
File diff suppressed because it is too large
Load diff
542
app/src/main/java/org/linphone/LinphoneService.java
Normal file
542
app/src/main/java/org/linphone/LinphoneService.java
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
package org.linphone;
|
||||
|
||||
/*
|
||||
LinphoneService.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.provider.ContactsContract;
|
||||
import android.view.WindowManager;
|
||||
import java.util.ArrayList;
|
||||
import org.linphone.call.CallIncomingActivity;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.Call.State;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.Factory;
|
||||
import org.linphone.core.GlobalState;
|
||||
import org.linphone.core.LogLevel;
|
||||
import org.linphone.core.LoggingService;
|
||||
import org.linphone.core.LoggingServiceListener;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.RegistrationState;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.mediastream.Version;
|
||||
import org.linphone.notifications.NotificationsManager;
|
||||
import org.linphone.receivers.BluetoothManager;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.LinphoneGL2JNIViewOverlay;
|
||||
import org.linphone.views.LinphoneOverlay;
|
||||
import org.linphone.views.LinphoneTextureViewOverlay;
|
||||
|
||||
/**
|
||||
* Linphone service, reacting to Incoming calls, ...<br>
|
||||
*
|
||||
* <p>Roles include:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Initializing LinphoneManager
|
||||
* <li>Starting C libLinphone through LinphoneManager
|
||||
* <li>Reacting to LinphoneManager state changes
|
||||
* <li>Delegating GUI state change actions to GUI listener
|
||||
*/
|
||||
public final class LinphoneService extends Service {
|
||||
/* Listener needs to be implemented in the Service as it calls
|
||||
* setLatestEventInfo and startActivity() which needs a context.
|
||||
*/
|
||||
private static final String START_LINPHONE_LOGS = " ==== Phone information dump ====";
|
||||
|
||||
private static LinphoneService sInstance;
|
||||
|
||||
public final Handler handler = new Handler();
|
||||
|
||||
private boolean mTestDelayElapsed = true;
|
||||
private CoreListenerStub mListener;
|
||||
private WindowManager mWindowManager;
|
||||
private LinphoneOverlay mOverlay;
|
||||
private Application.ActivityLifecycleCallbacks mActivityCallbacks;
|
||||
private NotificationsManager mNotificationManager;
|
||||
private String mIncomingReceivedActivityName;
|
||||
private Class<? extends Activity> mIncomingReceivedActivity = CallIncomingActivity.class;
|
||||
|
||||
private LoggingServiceListener mJavaLoggingService =
|
||||
new LoggingServiceListener() {
|
||||
@Override
|
||||
public void onLogMessageWritten(
|
||||
LoggingService logService, String domain, LogLevel lev, String message) {
|
||||
switch (lev) {
|
||||
case Debug:
|
||||
android.util.Log.d(domain, message);
|
||||
break;
|
||||
case Message:
|
||||
android.util.Log.i(domain, message);
|
||||
break;
|
||||
case Warning:
|
||||
android.util.Log.w(domain, message);
|
||||
break;
|
||||
case Error:
|
||||
android.util.Log.e(domain, message);
|
||||
break;
|
||||
case Fatal:
|
||||
default:
|
||||
android.util.Log.wtf(domain, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public LoggingServiceListener getJavaLoggingService() {
|
||||
return mJavaLoggingService;
|
||||
}
|
||||
|
||||
public static boolean isReady() {
|
||||
return sInstance != null && sInstance.mTestDelayElapsed;
|
||||
}
|
||||
|
||||
public static LinphoneService instance() {
|
||||
if (isReady()) return sInstance;
|
||||
|
||||
throw new RuntimeException("LinphoneService not instantiated yet");
|
||||
}
|
||||
|
||||
public NotificationsManager getNotificationManager() {
|
||||
return mNotificationManager;
|
||||
}
|
||||
|
||||
public void removeForegroundServiceNotificationIfPossible() {
|
||||
mNotificationManager.removeForegroundServiceNotificationIfPossible();
|
||||
}
|
||||
|
||||
public Class<? extends Activity> getIncomingReceivedActivity() {
|
||||
return mIncomingReceivedActivity;
|
||||
}
|
||||
|
||||
public void setCurrentlyDisplayedChatRoom(String address) {
|
||||
if (address != null) {
|
||||
mNotificationManager.resetMessageNotifCount(address);
|
||||
}
|
||||
}
|
||||
|
||||
private void onBackgroundMode() {
|
||||
Log.i("[Service] App has entered background mode");
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) {
|
||||
LinphoneManager.getLcIfManagerNotDestroyedOrNull().enterBackground();
|
||||
}
|
||||
}
|
||||
|
||||
private void onForegroundMode() {
|
||||
Log.i("[Service] App has left background mode");
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) {
|
||||
LinphoneManager.getLcIfManagerNotDestroyedOrNull().enterForeground();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupActivityMonitor() {
|
||||
if (mActivityCallbacks != null) return;
|
||||
getApplication()
|
||||
.registerActivityLifecycleCallbacks(mActivityCallbacks = new ActivityMonitor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
|
||||
boolean isPush = false;
|
||||
if (intent != null && intent.getBooleanExtra("PushNotification", false)) {
|
||||
Log.i("[Service] [Push Notification] LinphoneService started because of a push");
|
||||
isPush = true;
|
||||
}
|
||||
|
||||
if (sInstance != null) {
|
||||
Log.w("[Service] Attempt to start the LinphoneService but it is already running !");
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
LinphoneManager.createAndStart(this, isPush);
|
||||
|
||||
sInstance = this; // sInstance is ready once linphone manager has been created
|
||||
mNotificationManager = new NotificationsManager(this);
|
||||
LinphoneManager.getLc()
|
||||
.addListener(
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onCallStateChanged(
|
||||
Core lc, Call call, Call.State state, String message) {
|
||||
if (sInstance == null) {
|
||||
Log.i(
|
||||
"[Service] Service not ready, discarding call state change to ",
|
||||
state.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (getResources()
|
||||
.getBoolean(R.bool.enable_call_notification)) {
|
||||
mNotificationManager.displayCallNotification(call);
|
||||
}
|
||||
|
||||
if (state == Call.State.IncomingReceived
|
||||
|| state == State.IncomingEarlyMedia) {
|
||||
if (!LinphoneManager.getInstance().getCallGsmON())
|
||||
onIncomingReceived();
|
||||
}
|
||||
|
||||
if (state == State.End
|
||||
|| state == State.Released
|
||||
|| state == State.Error) {
|
||||
destroyOverlay();
|
||||
}
|
||||
|
||||
if (state == State.Released
|
||||
&& call.getCallLog().getStatus()
|
||||
== Call.Status.Missed) {
|
||||
mNotificationManager.displayMissedCallNotification(
|
||||
call);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGlobalStateChanged(
|
||||
Core lc, GlobalState state, String message) {
|
||||
// TODO global state if ON
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationStateChanged(
|
||||
Core lc,
|
||||
ProxyConfig cfg,
|
||||
RegistrationState state,
|
||||
String smessage) {
|
||||
// TODO registration status
|
||||
}
|
||||
});
|
||||
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)
|
||||
&& intent.getBooleanExtra("ForceStartForeground", false)) {
|
||||
mNotificationManager.startForeground();
|
||||
}
|
||||
|
||||
if (!Version.sdkAboveOrEqual(Version.API26_O_80)
|
||||
|| (ContactsManager.getInstance() != null
|
||||
&& ContactsManager.getInstance().hasReadContactsAccess())) {
|
||||
getContentResolver()
|
||||
.registerContentObserver(
|
||||
ContactsContract.Contacts.CONTENT_URI,
|
||||
true,
|
||||
ContactsManager.getInstance());
|
||||
}
|
||||
|
||||
if (!mTestDelayElapsed) {
|
||||
// Only used when testing. Simulates a 5 seconds delay for launching service
|
||||
handler.postDelayed(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mTestDelayElapsed = true;
|
||||
}
|
||||
},
|
||||
5000);
|
||||
}
|
||||
|
||||
BluetoothManager.getInstance().initBluetooth();
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
setupActivityMonitor();
|
||||
|
||||
// Needed in order for the two next calls to succeed, libraries must have been loaded first
|
||||
LinphonePreferences.instance().setContext(getBaseContext());
|
||||
Factory.instance().setLogCollectionPath(getFilesDir().getAbsolutePath());
|
||||
boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled();
|
||||
LinphoneUtils.configureLoggingService(isDebugEnabled, getString(R.string.app_name));
|
||||
// LinphoneService isn't ready yet so we have to manually set up the Java logging service
|
||||
if (LinphonePreferences.instance().useJavaLogger()) {
|
||||
Factory.instance().getLoggingService().addListener(mJavaLoggingService);
|
||||
}
|
||||
|
||||
// Dump some debugging information to the logs
|
||||
Log.i(START_LINPHONE_LOGS);
|
||||
dumpDeviceInformation();
|
||||
dumpInstalledLinphoneInformation();
|
||||
|
||||
mIncomingReceivedActivityName =
|
||||
LinphonePreferences.instance().getActivityToLaunchOnIncomingReceived();
|
||||
try {
|
||||
mIncomingReceivedActivity =
|
||||
(Class<? extends Activity>) Class.forName(mIncomingReceivedActivityName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
}
|
||||
|
||||
public void createOverlay() {
|
||||
if (mOverlay != null) destroyOverlay();
|
||||
|
||||
Core core = LinphoneManager.getLc();
|
||||
Call call = core.getCurrentCall();
|
||||
if (call == null || !call.getCurrentParams().videoEnabled()) return;
|
||||
|
||||
if ("MSAndroidOpenGLDisplay".equals(core.getVideoDisplayFilter())) {
|
||||
mOverlay = new LinphoneGL2JNIViewOverlay(this);
|
||||
} else {
|
||||
mOverlay = new LinphoneTextureViewOverlay(this);
|
||||
}
|
||||
WindowManager.LayoutParams params = mOverlay.getWindowManagerLayoutParams();
|
||||
params.x = 0;
|
||||
params.y = 0;
|
||||
mOverlay.addToWindowManager(mWindowManager, params);
|
||||
}
|
||||
|
||||
public void destroyOverlay() {
|
||||
if (mOverlay != null) {
|
||||
mOverlay.removeFromWindowManager(mWindowManager);
|
||||
mOverlay.destroy();
|
||||
}
|
||||
mOverlay = null;
|
||||
}
|
||||
|
||||
private void dumpDeviceInformation() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("DEVICE=").append(Build.DEVICE).append("\n");
|
||||
sb.append("MODEL=").append(Build.MODEL).append("\n");
|
||||
sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
|
||||
sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n");
|
||||
sb.append("Supported ABIs=");
|
||||
for (String abi : Version.getCpuAbis()) {
|
||||
sb.append(abi).append(", ");
|
||||
}
|
||||
sb.append("\n");
|
||||
Log.i(sb.toString());
|
||||
}
|
||||
|
||||
private void dumpInstalledLinphoneInformation() {
|
||||
PackageInfo info = null;
|
||||
try {
|
||||
info = getPackageManager().getPackageInfo(getPackageName(), 0);
|
||||
} catch (NameNotFoundException nnfe) {
|
||||
Log.e(nnfe);
|
||||
}
|
||||
|
||||
if (info != null) {
|
||||
Log.i(
|
||||
"[Service] Linphone version is ",
|
||||
info.versionName + " (" + info.versionCode + ")");
|
||||
} else {
|
||||
Log.i("[Service] Linphone version is unknown");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(Intent rootIntent) {
|
||||
boolean serviceNotif = LinphonePreferences.instance().getServiceNotificationVisibility();
|
||||
if (serviceNotif) {
|
||||
Log.i("[Service] Service is running in foreground, don't stop it");
|
||||
} else if (getResources().getBoolean(R.bool.kill_service_with_task_manager)) {
|
||||
Log.i("[Service] Task removed, stop service");
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.terminateAllCalls();
|
||||
}
|
||||
|
||||
// If push is enabled, don't unregister account, otherwise do unregister
|
||||
if (LinphonePreferences.instance().isPushNotificationEnabled()) {
|
||||
if (lc != null) lc.setNetworkReachable(false);
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
super.onTaskRemoved(rootIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onDestroy() {
|
||||
if (mActivityCallbacks != null) {
|
||||
getApplication().unregisterActivityLifecycleCallbacks(mActivityCallbacks);
|
||||
mActivityCallbacks = null;
|
||||
}
|
||||
destroyOverlay();
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
lc = null; // To allow the gc calls below to free the Core
|
||||
}
|
||||
|
||||
sInstance = null;
|
||||
LinphoneManager.destroy();
|
||||
|
||||
// Make sure our notification is gone.
|
||||
if (mNotificationManager != null) {
|
||||
mNotificationManager.destroy();
|
||||
}
|
||||
|
||||
// This will prevent the app from crashing if the service gets killed in background mode
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
Log.w("[Service] Service is getting destroyed, finish LinphoneActivity");
|
||||
LinphoneActivity.instance().finish();
|
||||
}
|
||||
|
||||
if (LinphonePreferences.instance().useJavaLogger()) {
|
||||
Factory.instance().getLoggingService().removeListener(mJavaLoggingService);
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setActivityToLaunchOnIncomingReceived(String activityName) {
|
||||
try {
|
||||
mIncomingReceivedActivity = (Class<? extends Activity>) Class.forName(activityName);
|
||||
mIncomingReceivedActivityName = activityName;
|
||||
LinphonePreferences.instance()
|
||||
.setActivityToLaunchOnIncomingReceived(mIncomingReceivedActivityName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onIncomingReceived() {
|
||||
Intent intent = new Intent().setClass(this, mIncomingReceivedActivity);
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().startActivity(intent);
|
||||
} else {
|
||||
// This flag is required to start an Activity from a Service context
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/*Believe me or not, but knowing the application visibility state on Android is a nightmare.
|
||||
After two days of hard work I ended with the following class, that does the job more or less reliabily.
|
||||
*/
|
||||
class ActivityMonitor implements Application.ActivityLifecycleCallbacks {
|
||||
private final ArrayList<Activity> activities = new ArrayList<>();
|
||||
private boolean mActive = false;
|
||||
private int mRunningActivities = 0;
|
||||
private InactivityChecker mLastChecker;
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
Log.i("[Service] Activity created:" + activity);
|
||||
if (!activities.contains(activity)) activities.add(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
Log.i("Activity started:" + activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityResumed(Activity activity) {
|
||||
Log.i("[Service] Activity resumed:" + activity);
|
||||
if (activities.contains(activity)) {
|
||||
mRunningActivities++;
|
||||
Log.i("[Service] runningActivities=" + mRunningActivities);
|
||||
checkActivity();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityPaused(Activity activity) {
|
||||
Log.i("[Service] Activity paused:" + activity);
|
||||
if (activities.contains(activity)) {
|
||||
mRunningActivities--;
|
||||
Log.i("[Service] runningActivities=" + mRunningActivities);
|
||||
checkActivity();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
Log.i("[Service] Activity stopped:" + activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityDestroyed(Activity activity) {
|
||||
Log.i("[Service] Activity destroyed:" + activity);
|
||||
activities.remove(activity);
|
||||
}
|
||||
|
||||
void startInactivityChecker() {
|
||||
if (mLastChecker != null) mLastChecker.cancel();
|
||||
LinphoneService.this.handler.postDelayed(
|
||||
(mLastChecker = new InactivityChecker()), 2000);
|
||||
}
|
||||
|
||||
void checkActivity() {
|
||||
if (mRunningActivities == 0) {
|
||||
if (mActive) startInactivityChecker();
|
||||
} else if (mRunningActivities > 0) {
|
||||
if (!mActive) {
|
||||
mActive = true;
|
||||
LinphoneService.this.onForegroundMode();
|
||||
}
|
||||
if (mLastChecker != null) {
|
||||
mLastChecker.cancel();
|
||||
mLastChecker = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InactivityChecker implements Runnable {
|
||||
private boolean isCanceled;
|
||||
|
||||
void cancel() {
|
||||
isCanceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (LinphoneService.this) {
|
||||
if (!isCanceled) {
|
||||
if (ActivityMonitor.this.mRunningActivities == 0 && mActive) {
|
||||
mActive = false;
|
||||
LinphoneService.this.onBackgroundMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
958
app/src/main/java/org/linphone/assistant/AssistantActivity.java
Normal file
958
app/src/main/java/org/linphone/assistant/AssistantActivity.java
Normal file
|
|
@ -0,0 +1,958 @@
|
|||
package org.linphone.assistant;
|
||||
/*
|
||||
AssistantActivity.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneLauncherActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.AccountCreator;
|
||||
import org.linphone.core.AccountCreatorListener;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.AuthInfo;
|
||||
import org.linphone.core.ConfiguringState;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.DialPlan;
|
||||
import org.linphone.core.Factory;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.RegistrationState;
|
||||
import org.linphone.core.TransportType;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.core.tools.OpenH264DownloadHelper;
|
||||
import org.linphone.fragments.StatusFragment;
|
||||
import org.linphone.mediastream.Version;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.utils.ThemableActivity;
|
||||
|
||||
public class AssistantActivity extends ThemableActivity
|
||||
implements OnClickListener,
|
||||
ActivityCompat.OnRequestPermissionsResultCallback,
|
||||
AccountCreatorListener {
|
||||
private static final int PERMISSIONS_REQUEST_RECORD_AUDIO = 201;
|
||||
private static final int PERMISSIONS_REQUEST_CAMERA = 202;
|
||||
|
||||
private static AssistantActivity sInstance;
|
||||
|
||||
public DialPlan country;
|
||||
|
||||
private ImageView mBack /*, mCancel*/;
|
||||
private AssistantFragmentsEnum mCurrentFragment;
|
||||
private AssistantFragmentsEnum mLastFragment;
|
||||
private AssistantFragmentsEnum mFirstFragment;
|
||||
private Fragment mFragment;
|
||||
private LinphonePreferences mPrefs;
|
||||
private boolean mAccountCreated = false,
|
||||
mNewAccount = false,
|
||||
mIsLink = false,
|
||||
mFromPref = false;
|
||||
private CoreListenerStub mListener;
|
||||
private Address mAddress;
|
||||
private StatusFragment mStatus;
|
||||
private ProgressDialog mProgress;
|
||||
private Dialog mDialog;
|
||||
private boolean mRemoteProvisioningInProgress;
|
||||
private boolean mEchoCancellerAlreadyDone;
|
||||
private AccountCreator mAccountCreator;
|
||||
private CountryListAdapter mCountryListAdapter;
|
||||
private LinearLayout mTopBar;
|
||||
|
||||
public static AssistantActivity instance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getResources().getBoolean(R.bool.orientation_portrait_only)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
setContentView(R.layout.assistant);
|
||||
initUI();
|
||||
|
||||
if (getIntent().getBooleanExtra("LinkPhoneNumber", false)) {
|
||||
mIsLink = true;
|
||||
if (getIntent().getBooleanExtra("FromPref", false)) mFromPref = true;
|
||||
displayCreateAccount();
|
||||
} else {
|
||||
mFirstFragment =
|
||||
getResources().getBoolean(R.bool.assistant_use_linphone_login_as_first_fragment)
|
||||
? AssistantFragmentsEnum.LINPHONE_LOGIN
|
||||
: AssistantFragmentsEnum.WELCOME;
|
||||
if (mFirstFragment == AssistantFragmentsEnum.WELCOME) {
|
||||
mFirstFragment =
|
||||
getResources()
|
||||
.getBoolean(
|
||||
R.bool.assistant_use_create_linphone_account_as_first_fragment)
|
||||
? AssistantFragmentsEnum.CREATE_ACCOUNT
|
||||
: AssistantFragmentsEnum.WELCOME;
|
||||
}
|
||||
|
||||
if (findViewById(R.id.fragment_container) != null) {
|
||||
if (savedInstanceState == null) {
|
||||
display(mFirstFragment);
|
||||
} else {
|
||||
mCurrentFragment =
|
||||
(AssistantFragmentsEnum)
|
||||
savedInstanceState.getSerializable("CurrentFragment");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey("echoCanceller")) {
|
||||
mEchoCancellerAlreadyDone = savedInstanceState.getBoolean("echoCanceller");
|
||||
} else {
|
||||
mEchoCancellerAlreadyDone = false;
|
||||
}
|
||||
mPrefs = LinphonePreferences.instance();
|
||||
mStatus.enableSideMenu(false);
|
||||
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) {
|
||||
mAccountCreator =
|
||||
LinphoneManager.getLc()
|
||||
.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
|
||||
mAccountCreator.setListener(this);
|
||||
}
|
||||
|
||||
mCountryListAdapter = new CountryListAdapter(getApplicationContext());
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
|
||||
@Override
|
||||
public void onConfiguringStatus(
|
||||
Core lc, final ConfiguringState state, String message) {
|
||||
if (mProgress != null) mProgress.dismiss();
|
||||
if (state == ConfiguringState.Successful) {
|
||||
goToLinphoneActivity();
|
||||
} else if (state == ConfiguringState.Failed) {
|
||||
Toast.makeText(
|
||||
AssistantActivity.instance(),
|
||||
getString(R.string.remote_provisioning_failure),
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationStateChanged(
|
||||
Core lc, ProxyConfig cfg, RegistrationState state, String smessage) {
|
||||
if (mRemoteProvisioningInProgress) {
|
||||
if (mProgress != null) mProgress.dismiss();
|
||||
if (state == RegistrationState.Ok) {
|
||||
mRemoteProvisioningInProgress = false;
|
||||
success();
|
||||
}
|
||||
} else if (mAccountCreated && !mNewAccount) {
|
||||
if (mAddress != null
|
||||
&& mAddress.asString()
|
||||
.equals(cfg.getIdentityAddress().asString())) {
|
||||
if (state == RegistrationState.Ok) {
|
||||
if (mProgress != null) mProgress.dismiss();
|
||||
if (getResources()
|
||||
.getBoolean(R.bool.use_phone_number_validation)
|
||||
&& cfg.getDomain()
|
||||
.equals(getString(R.string.default_domain))
|
||||
&& LinphoneManager.getLc().getDefaultProxyConfig()
|
||||
!= null) {
|
||||
loadAccountCreator(cfg).isAccountExist();
|
||||
} else {
|
||||
success();
|
||||
}
|
||||
} else if (state == RegistrationState.Failed) {
|
||||
if (mProgress != null) mProgress.dismiss();
|
||||
if (mDialog == null || !mDialog.isShowing()) {
|
||||
mDialog = createErrorDialog(cfg, smessage);
|
||||
mDialog.setCancelable(false);
|
||||
mDialog.show();
|
||||
}
|
||||
} else if (!(state == RegistrationState.Progress)) {
|
||||
if (mProgress != null) mProgress.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
outState.putSerializable("CurrentFragment", mCurrentFragment);
|
||||
outState.putBoolean("echoCanceller", mEchoCancellerAlreadyDone);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
public void updateStatusFragment(StatusFragment fragment) {
|
||||
mStatus = fragment;
|
||||
}
|
||||
|
||||
private AccountCreator loadAccountCreator(ProxyConfig cfg) {
|
||||
ProxyConfig cfgTab[] = LinphoneManager.getLc().getProxyConfigList();
|
||||
int n = -1;
|
||||
for (int i = 0; i < cfgTab.length; i++) {
|
||||
if (cfgTab[i].equals(cfg)) {
|
||||
n = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n >= 0) {
|
||||
mAccountCreator.setDomain(mPrefs.getAccountDomain(n));
|
||||
mAccountCreator.setUsername(mPrefs.getAccountUsername(n));
|
||||
}
|
||||
return mAccountCreator;
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
mBack = findViewById(R.id.back);
|
||||
mBack.setOnClickListener(this);
|
||||
// mCancel = findViewById(R.id.assistant_cancel);
|
||||
// mCancel.setOnClickListener(this);
|
||||
|
||||
mTopBar = findViewById(R.id.topbar);
|
||||
if (getResources().getBoolean(R.bool.assistant_hide_top_bar)) {
|
||||
mTopBar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void changeFragment(Fragment newFragment) {
|
||||
hideKeyboard();
|
||||
FragmentTransaction transaction = getFragmentManager().beginTransaction();
|
||||
transaction.replace(R.id.fragment_container, newFragment);
|
||||
transaction.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
boolean firstLaunch = LinphonePreferences.instance().isFirstLaunch();
|
||||
|
||||
/*if (id == R.id.assistant_cancel) {
|
||||
hideKeyboard();
|
||||
LinphonePreferences.instance().firstLaunchSuccessful();
|
||||
if (getResources().getBoolean(R.bool.assistant_cancel_move_to_back)) {
|
||||
moveTaskToBack(true);
|
||||
} else {
|
||||
if (firstLaunch) startActivity(new Intent().setClass(this, LinphoneActivity.class));
|
||||
finish();
|
||||
}
|
||||
} else*/
|
||||
if (id == R.id.back) {
|
||||
hideKeyboard();
|
||||
if (mCurrentFragment == AssistantFragmentsEnum.WELCOME) {
|
||||
LinphonePreferences.instance().firstLaunchSuccessful();
|
||||
if (getResources().getBoolean(R.bool.assistant_cancel_move_to_back)) {
|
||||
moveTaskToBack(true);
|
||||
} else {
|
||||
if (firstLaunch)
|
||||
startActivity(new Intent().setClass(this, LinphoneActivity.class));
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
onBackPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mIsLink) {
|
||||
return;
|
||||
}
|
||||
boolean firstLaunch = LinphonePreferences.instance().isFirstLaunch();
|
||||
if (mCurrentFragment == mFirstFragment) {
|
||||
LinphonePreferences.instance().firstLaunchSuccessful();
|
||||
if (getResources().getBoolean(R.bool.assistant_cancel_move_to_back)) {
|
||||
moveTaskToBack(true);
|
||||
} else {
|
||||
LinphonePreferences.instance().firstLaunchSuccessful();
|
||||
if (firstLaunch) startActivity(new Intent().setClass(this, LinphoneActivity.class));
|
||||
finish();
|
||||
}
|
||||
} else if (mCurrentFragment == AssistantFragmentsEnum.LOGIN
|
||||
|| mCurrentFragment == AssistantFragmentsEnum.LINPHONE_LOGIN
|
||||
|| mCurrentFragment == AssistantFragmentsEnum.CREATE_ACCOUNT
|
||||
|| mCurrentFragment == AssistantFragmentsEnum.REMOTE_PROVISIONING) {
|
||||
displayMenu();
|
||||
} else if (mCurrentFragment == AssistantFragmentsEnum.WELCOME) {
|
||||
if (firstLaunch) startActivity(new Intent().setClass(this, LinphoneActivity.class));
|
||||
finish();
|
||||
} else if (mCurrentFragment == AssistantFragmentsEnum.COUNTRY_CHOOSER) {
|
||||
if (mLastFragment.equals(AssistantFragmentsEnum.LINPHONE_LOGIN)) {
|
||||
displayLoginLinphone(null, null);
|
||||
} else {
|
||||
displayCreateAccount();
|
||||
}
|
||||
} else if (mCurrentFragment == AssistantFragmentsEnum.QRCODE_READER) {
|
||||
displayRemoteProvisioning("");
|
||||
}
|
||||
}
|
||||
|
||||
public void hideKeyboard() {
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
View view = this.getCurrentFocus();
|
||||
if (imm != null && view != null) {
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndRequestAudioPermission() {
|
||||
checkAndRequestPermission(
|
||||
Manifest.permission.RECORD_AUDIO, PERMISSIONS_REQUEST_RECORD_AUDIO);
|
||||
}
|
||||
|
||||
private void checkAndRequestVideoPermission() {
|
||||
checkAndRequestPermission(Manifest.permission.CAMERA, PERMISSIONS_REQUEST_CAMERA);
|
||||
}
|
||||
|
||||
private void checkAndRequestPermission(String permission, int result) {
|
||||
int permissionGranted = getPackageManager().checkPermission(permission, getPackageName());
|
||||
Log.i(
|
||||
"[Permission] "
|
||||
+ permission
|
||||
+ " is "
|
||||
+ (permissionGranted == PackageManager.PERMISSION_GRANTED
|
||||
? "granted"
|
||||
: "denied"));
|
||||
|
||||
if (permissionGranted != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.i("[Permission] Asking for " + permission);
|
||||
ActivityCompat.requestPermissions(this, new String[] {permission}, result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, String[] permissions, final int[] grantResults) {
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
Log.i(
|
||||
"[Permission] "
|
||||
+ permissions[i]
|
||||
+ " is "
|
||||
+ (grantResults[i] == PackageManager.PERMISSION_GRANTED
|
||||
? "granted"
|
||||
: "denied"));
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case PERMISSIONS_REQUEST_CAMERA:
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
displayQRCodeReader();
|
||||
}
|
||||
break;
|
||||
case PERMISSIONS_REQUEST_RECORD_AUDIO:
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
launchEchoCancellerCalibration();
|
||||
} else {
|
||||
isEchoCalibrationFinished();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void launchEchoCancellerCalibration() {
|
||||
int recordAudio =
|
||||
getPackageManager()
|
||||
.checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName());
|
||||
Log.i(
|
||||
"[Permission] Record audio permission is "
|
||||
+ (recordAudio == PackageManager.PERMISSION_GRANTED
|
||||
? "granted"
|
||||
: "denied"));
|
||||
|
||||
if (recordAudio == PackageManager.PERMISSION_GRANTED) {
|
||||
EchoCancellerCalibrationFragment fragment = new EchoCancellerCalibrationFragment();
|
||||
fragment.enableEcCalibrationResultSending(true);
|
||||
changeFragment(fragment);
|
||||
mCurrentFragment = AssistantFragmentsEnum.ECHO_CANCELLER_CALIBRATION;
|
||||
mBack.setVisibility(View.VISIBLE);
|
||||
mBack.setEnabled(false);
|
||||
} else {
|
||||
checkAndRequestAudioPermission();
|
||||
}
|
||||
}
|
||||
|
||||
private void configureProxyConfig(AccountCreator accountCreator) {
|
||||
Core lc = LinphoneManager.getLc();
|
||||
ProxyConfig proxyConfig = lc.createProxyConfig();
|
||||
AuthInfo authInfo;
|
||||
|
||||
String identity = proxyConfig.getIdentityAddress().asStringUriOnly();
|
||||
if (identity == null || accountCreator.getUsername() == null) {
|
||||
LinphoneUtils.displayErrorAlert(getString(R.string.error), this);
|
||||
return;
|
||||
}
|
||||
identity = identity.replace("?", accountCreator.getUsername());
|
||||
Address addr = Factory.instance().createAddress(identity);
|
||||
addr.setDisplayName(accountCreator.getUsername());
|
||||
mAddress = addr;
|
||||
proxyConfig.edit();
|
||||
|
||||
proxyConfig.setIdentityAddress(addr);
|
||||
|
||||
if (accountCreator.getPhoneNumber() != null && accountCreator.getPhoneNumber().length() > 0)
|
||||
proxyConfig.setDialPrefix(
|
||||
org.linphone.core.Utils.getPrefixFromE164(accountCreator.getPhoneNumber()));
|
||||
|
||||
proxyConfig.done();
|
||||
|
||||
authInfo =
|
||||
Factory.instance()
|
||||
.createAuthInfo(
|
||||
accountCreator.getUsername(),
|
||||
null,
|
||||
accountCreator.getPassword(),
|
||||
accountCreator.getHa1(),
|
||||
proxyConfig.getRealm(),
|
||||
proxyConfig.getDomain());
|
||||
|
||||
lc.addProxyConfig(proxyConfig);
|
||||
|
||||
lc.addAuthInfo(authInfo);
|
||||
|
||||
lc.setDefaultProxyConfig(proxyConfig);
|
||||
|
||||
if (LinphonePreferences.instance() != null)
|
||||
LinphonePreferences.instance().setPushNotificationEnabled(true);
|
||||
|
||||
if (!mNewAccount) {
|
||||
displayRegistrationInProgressDialog();
|
||||
}
|
||||
mAccountCreated = true;
|
||||
}
|
||||
|
||||
public void linphoneLogIn(AccountCreator accountCreator) {
|
||||
LinphoneManager.getLc()
|
||||
.loadConfigFromXml(LinphoneManager.getInstance().getLinphoneDynamicConfigFile());
|
||||
configureProxyConfig(accountCreator);
|
||||
// Restore default values for proxy config
|
||||
LinphoneManager.getLc()
|
||||
.loadConfigFromXml(LinphoneManager.getInstance().getDefaultDynamicConfigFile());
|
||||
}
|
||||
|
||||
public void genericLogIn(
|
||||
String username,
|
||||
String userid,
|
||||
String password,
|
||||
String displayname,
|
||||
String prefix,
|
||||
String domain,
|
||||
TransportType transport) {
|
||||
Core core = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (core == null) return;
|
||||
|
||||
AuthInfo authInfo =
|
||||
Factory.instance().createAuthInfo(username, userid, password, null, null, domain);
|
||||
core.addAuthInfo(authInfo);
|
||||
|
||||
ProxyConfig proxyConfig = core.createProxyConfig();
|
||||
|
||||
String identity = "sip:" + username + "@" + domain;
|
||||
Address identityAddr = Factory.instance().createAddress(identity);
|
||||
if (identityAddr != null) {
|
||||
identityAddr.setDisplayName(displayname);
|
||||
proxyConfig.setIdentityAddress(identityAddr);
|
||||
}
|
||||
String proxy = "<sip:" + domain + ";transport=" + transport.name().toLowerCase() + ">";
|
||||
proxyConfig.setServerAddr(proxy);
|
||||
|
||||
proxyConfig.setDialPrefix(prefix);
|
||||
|
||||
core.addProxyConfig(proxyConfig);
|
||||
core.setDefaultProxyConfig(proxyConfig);
|
||||
|
||||
mAccountCreated = true;
|
||||
success();
|
||||
}
|
||||
|
||||
private void display(AssistantFragmentsEnum fragment) {
|
||||
switch (fragment) {
|
||||
case WELCOME:
|
||||
displayMenu();
|
||||
break;
|
||||
case LINPHONE_LOGIN:
|
||||
displayLoginLinphone(null, null);
|
||||
break;
|
||||
case CREATE_ACCOUNT:
|
||||
displayCreateAccount();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Can't handle " + fragment);
|
||||
}
|
||||
}
|
||||
|
||||
private void displayMenu() {
|
||||
mFragment = new WelcomeFragment();
|
||||
changeFragment(mFragment);
|
||||
country = null;
|
||||
mCurrentFragment = AssistantFragmentsEnum.WELCOME;
|
||||
}
|
||||
|
||||
public void displayLoginGeneric() {
|
||||
mFragment = new LoginFragment();
|
||||
changeFragment(mFragment);
|
||||
mCurrentFragment = AssistantFragmentsEnum.LOGIN;
|
||||
}
|
||||
|
||||
public void displayLoginLinphone(String username, String password) {
|
||||
mFragment = new LinphoneLoginFragment();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("Phone", null);
|
||||
extras.putString("Dialcode", null);
|
||||
extras.putString("Username", username);
|
||||
extras.putString("Password", password);
|
||||
mFragment.setArguments(extras);
|
||||
changeFragment(mFragment);
|
||||
mCurrentFragment = AssistantFragmentsEnum.LINPHONE_LOGIN;
|
||||
}
|
||||
|
||||
public void displayCreateAccount() {
|
||||
mFragment = new CreateAccountFragment();
|
||||
Bundle extra = new Bundle();
|
||||
extra.putBoolean("LinkPhoneNumber", mIsLink);
|
||||
extra.putBoolean("LinkFromPref", mFromPref);
|
||||
mFragment.setArguments(extra);
|
||||
changeFragment(mFragment);
|
||||
mCurrentFragment = AssistantFragmentsEnum.CREATE_ACCOUNT;
|
||||
}
|
||||
|
||||
public void displayRemoteProvisioning(String url) {
|
||||
mFragment = new RemoteProvisioningFragment();
|
||||
Bundle extra = new Bundle();
|
||||
extra.putString("RemoteUrl", url);
|
||||
mFragment.setArguments(extra);
|
||||
changeFragment(mFragment);
|
||||
mCurrentFragment = AssistantFragmentsEnum.REMOTE_PROVISIONING;
|
||||
}
|
||||
|
||||
public void displayQRCodeReader() {
|
||||
if (getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName())
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
checkAndRequestVideoPermission();
|
||||
} else {
|
||||
mFragment = new QrCodeFragment();
|
||||
changeFragment(mFragment);
|
||||
mCurrentFragment = AssistantFragmentsEnum.QRCODE_READER;
|
||||
}
|
||||
}
|
||||
|
||||
public void displayCountryChooser() {
|
||||
mFragment = new CountryListFragment();
|
||||
changeFragment(mFragment);
|
||||
mLastFragment = mCurrentFragment;
|
||||
mCurrentFragment = AssistantFragmentsEnum.COUNTRY_CHOOSER;
|
||||
}
|
||||
|
||||
private void launchDownloadCodec() {
|
||||
if (OpenH264DownloadHelper.isOpenH264DownloadEnabled()) {
|
||||
OpenH264DownloadHelper downloadHelper =
|
||||
Factory.instance().createOpenH264DownloadHelper(this);
|
||||
if (Version.getCpuAbis().contains("armeabi-v7a")
|
||||
&& !Version.getCpuAbis().contains("x86")
|
||||
&& !downloadHelper.isCodecFound()) {
|
||||
CodecDownloaderFragment codecFragment = new CodecDownloaderFragment();
|
||||
changeFragment(codecFragment);
|
||||
mCurrentFragment = AssistantFragmentsEnum.DOWNLOAD_CODEC;
|
||||
mBack.setEnabled(false);
|
||||
} else goToLinphoneActivity();
|
||||
} else {
|
||||
goToLinphoneActivity();
|
||||
}
|
||||
}
|
||||
|
||||
public void endDownloadCodec() {
|
||||
goToLinphoneActivity();
|
||||
}
|
||||
|
||||
private void displayRegistrationInProgressDialog() {
|
||||
if (LinphoneManager.getLc().isNetworkReachable()) {
|
||||
mProgress = ProgressDialog.show(this, null, null);
|
||||
Drawable d = new ColorDrawable(ContextCompat.getColor(this, R.color.light_grey_color));
|
||||
d.setAlpha(200);
|
||||
mProgress
|
||||
.getWindow()
|
||||
.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT);
|
||||
mProgress.getWindow().setBackgroundDrawable(d);
|
||||
mProgress.setContentView(R.layout.wait_layout);
|
||||
mProgress.show();
|
||||
}
|
||||
}
|
||||
|
||||
public void displayRemoteProvisioningInProgressDialog() {
|
||||
mRemoteProvisioningInProgress = true;
|
||||
|
||||
mProgress = ProgressDialog.show(this, null, null);
|
||||
Drawable d = new ColorDrawable(ContextCompat.getColor(this, R.color.light_grey_color));
|
||||
d.setAlpha(200);
|
||||
mProgress
|
||||
.getWindow()
|
||||
.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT);
|
||||
mProgress.getWindow().setBackgroundDrawable(d);
|
||||
mProgress.setContentView(R.layout.wait_layout);
|
||||
mProgress.show();
|
||||
}
|
||||
|
||||
public void displayAssistantConfirm(String username, String password, String email) {
|
||||
CreateAccountActivationFragment fragment = new CreateAccountActivationFragment();
|
||||
mNewAccount = true;
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("Username", username);
|
||||
extras.putString("Password", password);
|
||||
extras.putString("Email", email);
|
||||
fragment.setArguments(extras);
|
||||
changeFragment(fragment);
|
||||
|
||||
mCurrentFragment = AssistantFragmentsEnum.CREATE_ACCOUNT_ACTIVATION;
|
||||
}
|
||||
|
||||
public void displayAssistantCodeConfirm(
|
||||
String username, String phone, String dialcode, boolean recoverAccount) {
|
||||
CreateAccountCodeActivationFragment fragment = new CreateAccountCodeActivationFragment();
|
||||
mNewAccount = true;
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("Username", username);
|
||||
extras.putString("Phone", phone);
|
||||
extras.putString("Dialcode", dialcode);
|
||||
extras.putBoolean("RecoverAccount", recoverAccount);
|
||||
extras.putBoolean("LinkAccount", mIsLink);
|
||||
fragment.setArguments(extras);
|
||||
changeFragment(fragment);
|
||||
|
||||
mCurrentFragment = AssistantFragmentsEnum.CREATE_ACCOUNT_CODE_ACTIVATION;
|
||||
}
|
||||
|
||||
public void displayAssistantLinphoneLogin(String phone, String dialcode) {
|
||||
LinphoneLoginFragment fragment = new LinphoneLoginFragment();
|
||||
mNewAccount = true;
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("Phone", phone);
|
||||
extras.putString("Dialcode", dialcode);
|
||||
fragment.setArguments(extras);
|
||||
changeFragment(fragment);
|
||||
|
||||
mCurrentFragment = AssistantFragmentsEnum.LINPHONE_LOGIN;
|
||||
}
|
||||
|
||||
public void isAccountVerified() {
|
||||
Toast.makeText(this, getString(R.string.assistant_account_validated), Toast.LENGTH_LONG)
|
||||
.show();
|
||||
hideKeyboard();
|
||||
success();
|
||||
}
|
||||
|
||||
public void isEchoCalibrationFinished() {
|
||||
launchDownloadCodec();
|
||||
}
|
||||
|
||||
private Dialog createErrorDialog(ProxyConfig proxy, String message) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
if (message.equals("Forbidden")) {
|
||||
message = getString(R.string.assistant_error_bad_credentials);
|
||||
}
|
||||
builder.setMessage(message)
|
||||
.setTitle(proxy.getState().toString())
|
||||
.setPositiveButton(
|
||||
getString(R.string.continue_text),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
success();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
getString(R.string.cancel),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
LinphoneManager.getLc()
|
||||
.removeProxyConfig(
|
||||
LinphoneManager.getLc().getDefaultProxyConfig());
|
||||
LinphonePreferences.instance().resetDefaultProxyConfig();
|
||||
LinphoneManager.getLc().refreshRegisters();
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
return builder.show();
|
||||
}
|
||||
|
||||
public void success() {
|
||||
boolean needsEchoCalibration = LinphoneManager.getLc().isEchoCancellerCalibrationRequired();
|
||||
if (needsEchoCalibration && mPrefs.isFirstLaunch()) {
|
||||
launchEchoCancellerCalibration();
|
||||
} else {
|
||||
launchDownloadCodec();
|
||||
}
|
||||
}
|
||||
|
||||
private void goToLinphoneActivity() {
|
||||
mPrefs.firstLaunchSuccessful();
|
||||
startActivity(
|
||||
new Intent()
|
||||
.setClass(this, LinphoneActivity.class)
|
||||
.putExtra("isNewProxyConfig", true));
|
||||
finish();
|
||||
}
|
||||
|
||||
public void setCoreListener() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
if (mStatus != null) {
|
||||
mStatus.setCoreListener();
|
||||
}
|
||||
}
|
||||
|
||||
public void restartApplication() {
|
||||
mPrefs.firstLaunchSuccessful();
|
||||
|
||||
Intent mStartActivity = new Intent(this, LinphoneLauncherActivity.class);
|
||||
PendingIntent mPendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
(int) System.currentTimeMillis(),
|
||||
mStartActivity,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
AlarmManager mgr = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
|
||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, mPendingIntent);
|
||||
|
||||
finish();
|
||||
stopService(new Intent(Intent.ACTION_MAIN).setClass(this, LinphoneService.class));
|
||||
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
am.killBackgroundProcesses(getString(R.string.sync_account_type));
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountExist(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {
|
||||
if (status.equals(AccountCreator.Status.AccountExistWithAlias)) {
|
||||
success();
|
||||
} else {
|
||||
mIsLink = true;
|
||||
displayCreateAccount();
|
||||
}
|
||||
if (mAccountCreator != null) mAccountCreator.setListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onLinkAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAlias(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAccountActivated(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onRecoverAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAccountLinked(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAliasUsed(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onUpdateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
public CountryListAdapter getCountryListAdapter() {
|
||||
return mCountryListAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (mCurrentFragment == AssistantFragmentsEnum.QRCODE_READER) {
|
||||
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class reads a JSON file containing Country-specific phone number description, and allows
|
||||
* to present them into a ListView
|
||||
*/
|
||||
public class CountryListAdapter extends BaseAdapter implements Filterable {
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
private final DialPlan[] allCountries;
|
||||
private List<DialPlan> filteredCountries;
|
||||
private final Context context;
|
||||
|
||||
CountryListAdapter(Context ctx) {
|
||||
context = ctx;
|
||||
allCountries = Factory.instance().getDialPlans();
|
||||
filteredCountries = new ArrayList<>(Arrays.asList(allCountries));
|
||||
}
|
||||
|
||||
public void setInflater(LayoutInflater inf) {
|
||||
mInflater = inf;
|
||||
}
|
||||
|
||||
public DialPlan getCountryFromCountryCode(String countryCode) {
|
||||
countryCode = (countryCode.startsWith("+")) ? countryCode.substring(1) : countryCode;
|
||||
for (DialPlan c : allCountries) {
|
||||
if (c.getCountryCallingCode().compareTo(countryCode) == 0) return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return filteredCountries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialPlan getItem(int position) {
|
||||
return filteredCountries.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View view;
|
||||
|
||||
if (convertView != null) {
|
||||
view = convertView;
|
||||
} else {
|
||||
view = mInflater.inflate(R.layout.country_cell, parent, false);
|
||||
}
|
||||
|
||||
DialPlan c = filteredCountries.get(position);
|
||||
|
||||
TextView name = view.findViewById(R.id.country_name);
|
||||
name.setText(c.getCountry());
|
||||
|
||||
TextView dial_code = view.findViewById(R.id.country_prefix);
|
||||
if (context != null)
|
||||
dial_code.setText(
|
||||
String.format(
|
||||
context.getString(R.string.country_code),
|
||||
c.getCountryCallingCode()));
|
||||
|
||||
view.setTag(c);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
ArrayList<DialPlan> filteredCountries = new ArrayList<>();
|
||||
for (DialPlan c : allCountries) {
|
||||
if (c.getCountry().toLowerCase().contains(constraint)
|
||||
|| c.getCountryCallingCode().contains(constraint)) {
|
||||
filteredCountries.add(c);
|
||||
}
|
||||
}
|
||||
FilterResults filterResults = new FilterResults();
|
||||
filterResults.values = filteredCountries;
|
||||
return filterResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
filteredCountries = (List<DialPlan>) results.values;
|
||||
CountryListAdapter.this.notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,5 +28,6 @@ public enum AssistantFragmentsEnum {
|
|||
LOGIN,
|
||||
REMOTE_PROVISIONING,
|
||||
ECHO_CANCELLER_CALIBRATION,
|
||||
DOWNLOAD_CODEC;
|
||||
DOWNLOAD_CODEC,
|
||||
QRCODE_READER
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
package org.linphone.assistant;
|
||||
|
||||
/*
|
||||
CodecDownloaderFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.PayloadType;
|
||||
import org.linphone.core.tools.OpenH264DownloadHelper;
|
||||
import org.linphone.core.tools.OpenH264DownloadHelperListener;
|
||||
|
||||
public class CodecDownloaderFragment extends Fragment {
|
||||
private final Handler mHandler = new Handler();
|
||||
private TextView mQuestion;
|
||||
private TextView mDownloading;
|
||||
private TextView mDownloaded;
|
||||
private Button mYes;
|
||||
private Button mNo;
|
||||
private Button mOk;
|
||||
private ProgressBar mProgressBar;
|
||||
private TextView mDownloadingInfo;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.assistant_codec_downloader, container, false);
|
||||
|
||||
mQuestion = view.findViewById(R.id.question);
|
||||
mDownloading = view.findViewById(R.id.downloading);
|
||||
mDownloaded = view.findViewById(R.id.downloaded);
|
||||
mYes = view.findViewById(R.id.answerYes);
|
||||
mNo = view.findViewById(R.id.answerNo);
|
||||
mOk = view.findViewById(R.id.answerOk);
|
||||
mProgressBar = view.findViewById(R.id.progressBar);
|
||||
mDownloadingInfo = view.findViewById(R.id.downloadingInfo);
|
||||
|
||||
final OpenH264DownloadHelper codecDownloader =
|
||||
LinphoneManager.getInstance().getOpenH264DownloadHelper();
|
||||
final OpenH264DownloadHelperListener codecListener =
|
||||
new OpenH264DownloadHelperListener() {
|
||||
|
||||
@Override
|
||||
public void OnProgress(final int current, final int max) {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (current <= max) {
|
||||
hideAllItems();
|
||||
mDownloadingInfo.setText(current + " / " + max);
|
||||
mDownloadingInfo.setVisibility(View.VISIBLE);
|
||||
mDownloading.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setMax(max);
|
||||
mProgressBar.setProgress(current);
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
hideAllItems();
|
||||
mDownloaded.setVisibility(View.VISIBLE);
|
||||
if (Build.VERSION.SDK_INT
|
||||
>= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
enabledH264(true);
|
||||
LinphoneManager.getLc()
|
||||
.reloadMsPlugins(
|
||||
AssistantActivity.instance()
|
||||
.getApplicationInfo()
|
||||
.nativeLibraryDir);
|
||||
AssistantActivity.instance().endDownloadCodec();
|
||||
} else {
|
||||
// We need to restart due to bad android linker
|
||||
AssistantActivity.instance().restartApplication();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void OnError(final String error) {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hideAllItems();
|
||||
mDownloaded.setText("Sorry an error has occurred.");
|
||||
mDownloaded.setVisibility(View.VISIBLE);
|
||||
mOk.setVisibility(View.VISIBLE);
|
||||
enabledH264(false);
|
||||
AssistantActivity.instance().endDownloadCodec();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
codecDownloader.setOpenH264HelperListener(codecListener);
|
||||
|
||||
mYes.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hideAllItems();
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
codecDownloader.downloadCodec();
|
||||
}
|
||||
});
|
||||
|
||||
mNo.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
enabledH264(false);
|
||||
AssistantActivity.instance().endDownloadCodec();
|
||||
}
|
||||
});
|
||||
hideAllItems();
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
if (savedInstanceState.containsKey("mQuestion"))
|
||||
mQuestion.setVisibility((Integer) savedInstanceState.getSerializable("mQuestion"));
|
||||
else mQuestion.setVisibility(View.VISIBLE);
|
||||
|
||||
if (savedInstanceState.containsKey("mYes"))
|
||||
mYes.setVisibility((Integer) savedInstanceState.getSerializable("mYes"));
|
||||
else mYes.setVisibility(View.VISIBLE);
|
||||
|
||||
if (savedInstanceState.containsKey("mNo"))
|
||||
mNo.setVisibility((Integer) savedInstanceState.getSerializable("mNo"));
|
||||
else mNo.setVisibility(View.VISIBLE);
|
||||
|
||||
if (savedInstanceState.containsKey("mDownloading"))
|
||||
mDownloading.setVisibility(
|
||||
(Integer) savedInstanceState.getSerializable("mDownloading"));
|
||||
|
||||
if (savedInstanceState.containsKey("mDownloaded"))
|
||||
mDownloaded.setVisibility(
|
||||
(Integer) savedInstanceState.getSerializable("mDownloaded"));
|
||||
|
||||
if (savedInstanceState.containsKey("context_bar"))
|
||||
mProgressBar.setVisibility(
|
||||
(Integer) savedInstanceState.getSerializable("context_bar"));
|
||||
|
||||
if (savedInstanceState.containsKey("mDownloadingInfo"))
|
||||
mDownloadingInfo.setVisibility(
|
||||
(Integer) savedInstanceState.getSerializable("mDownloadingInfo"));
|
||||
|
||||
if (savedInstanceState.containsKey("mOk"))
|
||||
mOk.setVisibility((Integer) savedInstanceState.getSerializable("mOk"));
|
||||
} else {
|
||||
mYes.setVisibility(View.VISIBLE);
|
||||
mQuestion.setVisibility(View.VISIBLE);
|
||||
mNo.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
if (mQuestion != null) outState.putSerializable("mQuestion", mQuestion.getVisibility());
|
||||
if (mDownloading != null)
|
||||
outState.putSerializable("mDownloading", mDownloading.getVisibility());
|
||||
if (mDownloaded != null)
|
||||
outState.putSerializable("mDownloaded", mDownloaded.getVisibility());
|
||||
if (mYes != null) outState.putSerializable("mYes", mYes.getVisibility());
|
||||
if (mNo != null) outState.putSerializable("mNo", mNo.getVisibility());
|
||||
if (mOk != null) outState.putSerializable("mOk", mOk.getVisibility());
|
||||
if (mProgressBar != null)
|
||||
outState.putSerializable("context_bar", mProgressBar.getVisibility());
|
||||
if (mDownloadingInfo != null)
|
||||
outState.putSerializable("mDownloadingInfo", mDownloadingInfo.getVisibility());
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
private void hideAllItems() {
|
||||
if (mQuestion != null) mQuestion.setVisibility(View.INVISIBLE);
|
||||
if (mDownloading != null) mDownloading.setVisibility(View.INVISIBLE);
|
||||
if (mDownloaded != null) mDownloaded.setVisibility(View.INVISIBLE);
|
||||
if (mYes != null) mYes.setVisibility(View.INVISIBLE);
|
||||
if (mNo != null) mNo.setVisibility(View.INVISIBLE);
|
||||
if (mOk != null) mOk.setVisibility(View.INVISIBLE);
|
||||
if (mProgressBar != null) mProgressBar.setVisibility(View.INVISIBLE);
|
||||
if (mDownloadingInfo != null) mDownloadingInfo.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void enabledH264(boolean enable) {
|
||||
PayloadType h264 = null;
|
||||
for (PayloadType pt : LinphoneManager.getLc().getVideoPayloadTypes()) {
|
||||
if (pt.getMimeType().equals("H264")) h264 = pt;
|
||||
}
|
||||
|
||||
if (h264 != null) {
|
||||
h264.enable(enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,63 +30,61 @@ import android.widget.AdapterView;
|
|||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.DialPlan;
|
||||
|
||||
public class CountryListFragment extends Fragment implements AdapterView.OnItemClickListener, View.OnClickListener {
|
||||
private ListView list;
|
||||
private EditText search;
|
||||
private ImageView clearSearchField;
|
||||
private AssistantActivity.CountryListAdapter adapter;
|
||||
public class CountryListFragment extends Fragment
|
||||
implements AdapterView.OnItemClickListener, View.OnClickListener {
|
||||
private ListView mList;
|
||||
private EditText mSearch;
|
||||
private ImageView mClearSearchField;
|
||||
private AssistantActivity.CountryListAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
|
||||
View view = inflater.inflate(R.layout.assistant_country_list, container, false);
|
||||
adapter = AssistantActivity.instance().getCountryListAdapter();
|
||||
adapter.setInflater(inflater);
|
||||
mAdapter = AssistantActivity.instance().getCountryListAdapter();
|
||||
mAdapter.setInflater(inflater);
|
||||
|
||||
search = view.findViewById(R.id.search_country);
|
||||
clearSearchField = view.findViewById(R.id.clearSearchField);
|
||||
clearSearchField.setOnClickListener(this);
|
||||
mSearch = view.findViewById(R.id.search_country);
|
||||
mClearSearchField = view.findViewById(R.id.clearSearchField);
|
||||
mClearSearchField.setOnClickListener(this);
|
||||
|
||||
list = view.findViewById(R.id.countryList);
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(this);
|
||||
mList = view.findViewById(R.id.countryList);
|
||||
mList.setAdapter(mAdapter);
|
||||
mList.setOnItemClickListener(this);
|
||||
|
||||
search.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
mSearch.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
adapter.getFilter().filter(s);
|
||||
}
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
mAdapter.getFilter().filter(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
});
|
||||
search.setText("");
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
mSearch.setText("");
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
DialPlan c = (DialPlan) view.getTag();
|
||||
AssistantActivity.instance().country = c;
|
||||
AssistantActivity.instance().country = (DialPlan) view.getTag();
|
||||
AssistantActivity.instance().onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.clearSearchField) {
|
||||
search.setText("");
|
||||
mSearch.setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
package org.linphone.assistant;
|
||||
/*
|
||||
CreateAccountActivationFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.AccountCreator;
|
||||
import org.linphone.core.AccountCreatorListener;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
public class CreateAccountActivationFragment extends Fragment
|
||||
implements OnClickListener, AccountCreatorListener {
|
||||
private String mUsername, mPassword;
|
||||
private Button mCheckAccount;
|
||||
private TextView mEmail;
|
||||
private AccountCreator mAccountCreator;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view =
|
||||
inflater.inflate(
|
||||
R.layout.assistant_account_creation_email_activation, container, false);
|
||||
|
||||
mAccountCreator =
|
||||
LinphoneManager.getLc()
|
||||
.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
|
||||
mAccountCreator.setListener(this);
|
||||
|
||||
mUsername = getArguments().getString("Username");
|
||||
mPassword = getArguments().getString("Password");
|
||||
|
||||
mAccountCreator.setUsername(mUsername);
|
||||
mAccountCreator.setPassword(mPassword);
|
||||
|
||||
mEmail = view.findViewById(R.id.send_email);
|
||||
mEmail.setText(getArguments().getString("Email"));
|
||||
|
||||
mCheckAccount = view.findViewById(R.id.assistant_check);
|
||||
mCheckAccount.setOnClickListener(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.assistant_check) {
|
||||
mCheckAccount.setEnabled(false);
|
||||
mAccountCreator.isAccountActivated();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountExist(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onCreateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onLinkAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAlias(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAccountActivated(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(AccountCreator.Status.AccountNotActivated)) {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.assistant_account_not_validated),
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
} else if (status.equals(AccountCreator.Status.AccountActivated)) {
|
||||
AssistantActivity.instance().linphoneLogIn(accountCreator);
|
||||
AssistantActivity.instance().isAccountVerified();
|
||||
} else {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.wizard_server_unavailable),
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
mCheckAccount.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecoverAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAccountLinked(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAliasUsed(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onUpdateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
package org.linphone.assistant;
|
||||
/*
|
||||
CreateAccountCodeActivationFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.AccountCreator;
|
||||
import org.linphone.core.AccountCreatorListener;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
public class CreateAccountCodeActivationFragment extends Fragment
|
||||
implements AccountCreatorListener {
|
||||
private String mUsername, mPhone, mDialcode;
|
||||
private TextView mTitle, mPhonenumber;
|
||||
private EditText mCode;
|
||||
private boolean mRecoverAccount = false, mLinkAccount = false;
|
||||
private int mCodeLength, mAccountNumber;
|
||||
private ImageView mBack;
|
||||
private Button mCheckAccount;
|
||||
private AccountCreator mAccountCreator;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view =
|
||||
inflater.inflate(
|
||||
R.layout.assistant_account_creation_code_activation, container, false);
|
||||
|
||||
mUsername = getArguments().getString("Username");
|
||||
mPhone = getArguments().getString("Phone");
|
||||
mDialcode = getArguments().getString("Dialcode");
|
||||
mRecoverAccount = getArguments().getBoolean("RecoverAccount");
|
||||
mLinkAccount = getArguments().getBoolean("LinkAccount");
|
||||
mAccountNumber = getArguments().getInt("AccountNumber");
|
||||
|
||||
mCodeLength = LinphonePreferences.instance().getCodeLength();
|
||||
mAccountCreator =
|
||||
LinphoneManager.getLc()
|
||||
.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
|
||||
mAccountCreator.setListener(this);
|
||||
mAccountCreator.setUsername(mUsername);
|
||||
mAccountCreator.setPhoneNumber(mPhone, mDialcode);
|
||||
|
||||
mBack = view.findViewById(R.id.back);
|
||||
if (mBack != null) mBack.setVisibility(Button.INVISIBLE);
|
||||
|
||||
mTitle = view.findViewById(R.id.title_account_activation);
|
||||
if (mLinkAccount) {
|
||||
mTitle.setText(getString(R.string.assistant_link_account));
|
||||
} else if (mRecoverAccount) {
|
||||
mTitle.setText(getString(R.string.assistant_linphone_account));
|
||||
}
|
||||
|
||||
mPhonenumber = view.findViewById(R.id.send_phone_number);
|
||||
mPhonenumber.setText(mAccountCreator.getPhoneNumber());
|
||||
|
||||
mCode = view.findViewById(R.id.assistant_code);
|
||||
mCode.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (s.length() == mCodeLength) {
|
||||
mCheckAccount.setEnabled(true);
|
||||
} else {
|
||||
mCheckAccount.setEnabled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mCheckAccount = view.findViewById(R.id.assistant_check);
|
||||
mCheckAccount.setEnabled(false);
|
||||
mCheckAccount.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mCheckAccount.setEnabled(false);
|
||||
mAccountCreator.setActivationCode(mCode.getText().toString());
|
||||
if (mLinkAccount) {
|
||||
linkAccount();
|
||||
} else {
|
||||
activateAccount();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void linkAccount() {
|
||||
mAccountCreator.setUsername(
|
||||
LinphonePreferences.instance().getAccountUsername(mAccountNumber));
|
||||
mAccountCreator.setHa1(LinphonePreferences.instance().getAccountHa1(mAccountNumber));
|
||||
mAccountCreator.activateAlias();
|
||||
}
|
||||
|
||||
private void activateAccount() {
|
||||
if (mAccountCreator.getUsername() == null) {
|
||||
mAccountCreator.setUsername(mAccountCreator.getPhoneNumber());
|
||||
}
|
||||
mAccountCreator.activateAccount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountExist(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onCreateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(AccountCreator.Status.AccountActivated)) {
|
||||
mCheckAccount.setEnabled(true);
|
||||
if (accountCreator.getUsername() != null) {
|
||||
AssistantActivity.instance().linphoneLogIn(accountCreator);
|
||||
if (!mRecoverAccount) {
|
||||
AssistantActivity.instance().isAccountVerified();
|
||||
} else {
|
||||
AssistantActivity.instance().success();
|
||||
}
|
||||
} else {
|
||||
AssistantActivity.instance().linphoneLogIn(accountCreator);
|
||||
if (!mRecoverAccount) {
|
||||
AssistantActivity.instance().isAccountVerified();
|
||||
} else {
|
||||
AssistantActivity.instance().success();
|
||||
}
|
||||
}
|
||||
} else if (status.equals(AccountCreator.Status.RequestFailed)) {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.wizard_server_unavailable),
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
} else {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.assistant_error_confirmation_code),
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
AssistantActivity.instance().displayAssistantLinphoneLogin(mPhone, mDialcode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinkAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAlias(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(AccountCreator.Status.AccountActivated)) {
|
||||
LinphonePreferences.instance()
|
||||
.setPrefix(
|
||||
mAccountNumber,
|
||||
org.linphone.core.Utils.getPrefixFromE164(
|
||||
accountCreator.getPhoneNumber()));
|
||||
LinphonePreferences.instance().setLinkPopupTime("");
|
||||
AssistantActivity.instance().hideKeyboard();
|
||||
AssistantActivity.instance().success();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountActivated(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onRecoverAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAccountLinked(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAliasUsed(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onUpdateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,804 @@
|
|||
package org.linphone.assistant;
|
||||
/*
|
||||
CreateAccountFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Patterns;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.AccountCreator;
|
||||
import org.linphone.core.AccountCreator.Status;
|
||||
import org.linphone.core.AccountCreatorListener;
|
||||
import org.linphone.core.DialPlan;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
public class CreateAccountFragment extends Fragment
|
||||
implements CompoundButton.OnCheckedChangeListener, OnClickListener, AccountCreatorListener {
|
||||
private final Pattern UPPER_CASE_REGEX = Pattern.compile("[A-Z]");
|
||||
|
||||
private EditText mPhoneNumberEdit,
|
||||
mUsernameEdit,
|
||||
mPasswordEdit,
|
||||
mPasswordConfirmEdit,
|
||||
mEmailEdit,
|
||||
mDialCode;
|
||||
private TextView mPhoneNumberError,
|
||||
mPasswordError,
|
||||
mPasswordConfirmError,
|
||||
mEmailError,
|
||||
mAssisstantTitle,
|
||||
mSipUri,
|
||||
mSkip,
|
||||
mInstruction;
|
||||
private ImageView mPhoneNumberInfo;
|
||||
private boolean mPasswordOk = false;
|
||||
private boolean mEmailOk = false;
|
||||
private boolean mConfirmPasswordOk = false;
|
||||
private boolean mLinkAccount = false;
|
||||
private Button mCreateAccount, mSelectCountry;
|
||||
private CheckBox mUseUsername, mUseEmail;
|
||||
private String mAddressSip = "";
|
||||
private int mCountryCode;
|
||||
private LinearLayout mPhoneNumberLayout,
|
||||
mUsernameLayout,
|
||||
mEmailLayout,
|
||||
mPasswordLayout,
|
||||
mPasswordConfirmLayout;
|
||||
private AccountCreator mAccountCreator;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.assistant_account_creation, container, false);
|
||||
|
||||
// Initialize mAccountCreator
|
||||
mAccountCreator =
|
||||
LinphoneManager.getLc()
|
||||
.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
|
||||
mAccountCreator.setListener(this);
|
||||
mAccountCreator.setDomain(getString(R.string.default_domain));
|
||||
|
||||
mInstruction = view.findViewById(R.id.message_create_account);
|
||||
|
||||
mCreateAccount = view.findViewById(R.id.assistant_create);
|
||||
|
||||
mPhoneNumberLayout = view.findViewById(R.id.phone_number_layout);
|
||||
mUsernameLayout = view.findViewById(R.id.username_layout);
|
||||
mEmailLayout = view.findViewById(R.id.email_layout);
|
||||
mPasswordLayout = view.findViewById(R.id.password_layout);
|
||||
mPasswordConfirmLayout = view.findViewById(R.id.password_confirm_layout);
|
||||
|
||||
mUseUsername = view.findViewById(R.id.use_username);
|
||||
mUseEmail = view.findViewById(R.id.use_email);
|
||||
|
||||
mUsernameEdit = view.findViewById(R.id.username);
|
||||
|
||||
mPhoneNumberError = view.findViewById(R.id.phone_number_error);
|
||||
mPhoneNumberEdit = view.findViewById(R.id.phone_number);
|
||||
mSipUri = view.findViewById(R.id.sip_uri);
|
||||
|
||||
mPhoneNumberInfo = view.findViewById(R.id.info_phone_number);
|
||||
|
||||
mSelectCountry = view.findViewById(R.id.select_country);
|
||||
mDialCode = view.findViewById(R.id.dial_code);
|
||||
mAssisstantTitle = view.findViewById(R.id.assistant_title);
|
||||
|
||||
mPasswordError = view.findViewById(R.id.password_error);
|
||||
mPasswordEdit = view.findViewById(R.id.password);
|
||||
|
||||
mPasswordConfirmError = view.findViewById(R.id.confirm_password_error);
|
||||
mPasswordConfirmEdit = view.findViewById(R.id.confirm_password);
|
||||
|
||||
mEmailError = view.findViewById(R.id.email_error);
|
||||
mEmailEdit = view.findViewById(R.id.email);
|
||||
|
||||
mSkip = view.findViewById(R.id.assistant_skip);
|
||||
|
||||
// Phone number
|
||||
if (getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
getActivity().getApplicationContext();
|
||||
// Automatically get the country code from the phone
|
||||
TelephonyManager tm =
|
||||
(TelephonyManager)
|
||||
getActivity()
|
||||
.getApplicationContext()
|
||||
.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
String countryIso = tm.getNetworkCountryIso();
|
||||
mCountryCode = org.linphone.core.Utils.getCccFromIso(countryIso.toUpperCase());
|
||||
|
||||
mPhoneNumberLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
mPhoneNumberInfo.setOnClickListener(this);
|
||||
mSelectCountry.setOnClickListener(this);
|
||||
|
||||
DialPlan c = AssistantActivity.instance().country;
|
||||
if (c != null) {
|
||||
mSelectCountry.setText(c.getCountry());
|
||||
mDialCode.setText(
|
||||
c.getCountryCallingCode().contains("+")
|
||||
? c.getCountryCallingCode()
|
||||
: "+" + c.getCountryCallingCode());
|
||||
} else {
|
||||
c =
|
||||
AssistantActivity.instance()
|
||||
.getCountryListAdapter()
|
||||
.getCountryFromCountryCode(String.valueOf(mCountryCode));
|
||||
if (c != null) {
|
||||
mSelectCountry.setText(c.getCountry());
|
||||
mDialCode.setText(
|
||||
c.getCountryCallingCode().contains("+")
|
||||
? c.getCountryCallingCode()
|
||||
: "+" + c.getCountryCallingCode());
|
||||
}
|
||||
}
|
||||
|
||||
// Allow user to enter a username instead use the phone number as username
|
||||
if (getResources().getBoolean(R.bool.assistant_allow_username)) {
|
||||
mUseUsername.setVisibility(View.VISIBLE);
|
||||
mUseUsername.setOnCheckedChangeListener(this);
|
||||
}
|
||||
addPhoneNumberHandler(mPhoneNumberEdit);
|
||||
addPhoneNumberHandler(mDialCode);
|
||||
}
|
||||
|
||||
// Password & email address
|
||||
if (getResources().getBoolean(R.bool.isTablet)
|
||||
|| !getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
mUseEmail.setVisibility(View.VISIBLE);
|
||||
mUseEmail.setOnCheckedChangeListener(this);
|
||||
|
||||
if (getResources().getBoolean(R.bool.pre_fill_email_in_assistant)) {
|
||||
Account[] accounts =
|
||||
AccountManager.get(getActivity()).getAccountsByType("com.google");
|
||||
|
||||
for (Account account : accounts) {
|
||||
if (isEmailCorrect(account.name)) {
|
||||
String possibleEmail = account.name;
|
||||
mEmailEdit.setText(possibleEmail);
|
||||
mAccountCreator.setEmail(possibleEmail);
|
||||
mEmailOk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addPasswordHandler(mPasswordEdit);
|
||||
addConfirmPasswordHandler(mPasswordEdit, mPasswordConfirmEdit);
|
||||
addEmailHandler(mEmailEdit);
|
||||
}
|
||||
|
||||
// Hide phone number and display username/email/password
|
||||
if (!getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
mUseEmail.setVisibility(View.GONE);
|
||||
mUseUsername.setVisibility(View.GONE);
|
||||
|
||||
mUsernameLayout.setVisibility(View.VISIBLE);
|
||||
mPasswordLayout.setVisibility(View.VISIBLE);
|
||||
mPasswordConfirmLayout.setVisibility(View.VISIBLE);
|
||||
mEmailLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// Link account with phone number
|
||||
if (getArguments().getBoolean("LinkPhoneNumber")) {
|
||||
mLinkAccount = true;
|
||||
mUseEmail.setVisibility(View.GONE);
|
||||
mUseUsername.setVisibility(View.GONE);
|
||||
|
||||
mUsernameLayout.setVisibility(View.GONE);
|
||||
mPasswordLayout.setVisibility(View.GONE);
|
||||
mPasswordConfirmLayout.setVisibility(View.GONE);
|
||||
mEmailLayout.setVisibility(View.GONE);
|
||||
|
||||
mSkip.setVisibility(View.VISIBLE);
|
||||
mSkip.setOnClickListener(this);
|
||||
|
||||
mCreateAccount.setText(getResources().getString(R.string.link_account));
|
||||
mAssisstantTitle.setText(getResources().getString(R.string.link_account));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
mAccountCreator.setLanguage(Locale.getDefault().toLanguageTag());
|
||||
}
|
||||
|
||||
addUsernameHandler(mUsernameEdit);
|
||||
|
||||
mCreateAccount.setEnabled(true);
|
||||
mCreateAccount.setOnClickListener(this);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mAccountCreator.setListener(null);
|
||||
}
|
||||
|
||||
private String getUsername() {
|
||||
if (mUsernameEdit != null) {
|
||||
String username = mUsernameEdit.getText().toString();
|
||||
return username.toLowerCase(Locale.getDefault());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
if (buttonView.getId() == R.id.use_username) {
|
||||
if (isChecked) {
|
||||
mUsernameLayout.setVisibility(View.VISIBLE);
|
||||
onTextChanged2();
|
||||
} else {
|
||||
mUsernameLayout.setVisibility(View.GONE);
|
||||
mAccountCreator.setUsername(null);
|
||||
onTextChanged2();
|
||||
}
|
||||
} else if (buttonView.getId() == R.id.use_email) {
|
||||
if (isChecked) {
|
||||
mDialCode.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
mPhoneNumberEdit.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
mUseUsername.setEnabled(false);
|
||||
mDialCode.setEnabled(false);
|
||||
mSelectCountry.setEnabled(false);
|
||||
mPhoneNumberEdit.setEnabled(false);
|
||||
mEmailLayout.setVisibility(View.VISIBLE);
|
||||
mPasswordLayout.setVisibility(View.VISIBLE);
|
||||
mPasswordConfirmLayout.setVisibility(View.VISIBLE);
|
||||
mUsernameLayout.setVisibility(View.VISIBLE);
|
||||
mUseUsername.setVisibility(CheckBox.GONE);
|
||||
mPhoneNumberLayout.setVisibility(LinearLayout.GONE);
|
||||
mInstruction.setText(getString(R.string.assistant_create_account_part_email));
|
||||
} else {
|
||||
if (!mUseUsername.isChecked()) {
|
||||
mUsernameLayout.setVisibility(View.GONE);
|
||||
}
|
||||
mUseUsername.setEnabled(true);
|
||||
mDialCode.setEnabled(true);
|
||||
mSelectCountry.setEnabled(true);
|
||||
mPhoneNumberEdit.setEnabled(true);
|
||||
mEmailLayout.setVisibility(View.GONE);
|
||||
mPasswordLayout.setVisibility(View.GONE);
|
||||
mPasswordConfirmLayout.setVisibility(View.GONE);
|
||||
mUseUsername.setVisibility(CheckBox.VISIBLE);
|
||||
mPhoneNumberLayout.setVisibility(LinearLayout.VISIBLE);
|
||||
mInstruction.setText(getString(R.string.assistant_create_account_part_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.select_country) {
|
||||
AssistantActivity.instance().displayCountryChooser();
|
||||
} else if (id == R.id.assistant_skip) {
|
||||
if (getArguments().getBoolean("LinkFromPref")) {
|
||||
AssistantActivity.instance().finish();
|
||||
} else {
|
||||
AssistantActivity.instance().success();
|
||||
}
|
||||
} else if (id == R.id.info_phone_number) {
|
||||
if (mLinkAccount) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.phone_number_info_title))
|
||||
.setMessage(
|
||||
getString(R.string.phone_number_link_info_content)
|
||||
+ "\n"
|
||||
+ getString(
|
||||
R.string
|
||||
.phone_number_link_info_content_already_account))
|
||||
.show();
|
||||
} else {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.phone_number_info_title))
|
||||
.setMessage(getString(R.string.phone_number_info_content))
|
||||
.show();
|
||||
}
|
||||
} else if (id == R.id.assistant_create) {
|
||||
mCreateAccount.setEnabled(false);
|
||||
if (mLinkAccount) {
|
||||
addAlias();
|
||||
} else {
|
||||
if (mUseEmail.isChecked()) mAccountCreator.setPhoneNumber(null, null);
|
||||
if (!getResources().getBoolean(R.bool.isTablet) || getUsername().length() > 0) {
|
||||
mAccountCreator.isAccountExist();
|
||||
} else {
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForUsernameStatus(
|
||||
AccountCreator.UsernameStatus.TooShort),
|
||||
AssistantActivity.instance());
|
||||
mCreateAccount.setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEmailCorrect(String email) {
|
||||
Pattern emailPattern = Patterns.EMAIL_ADDRESS;
|
||||
return emailPattern.matcher(email).matches();
|
||||
}
|
||||
|
||||
private boolean isPasswordCorrect(String password) {
|
||||
return password.length() >= 1;
|
||||
}
|
||||
|
||||
private void addAlias() {
|
||||
mAccountCreator.setUsername(
|
||||
LinphonePreferences.instance()
|
||||
.getAccountUsername(
|
||||
LinphonePreferences.instance().getDefaultAccountIndex()));
|
||||
int status =
|
||||
mAccountCreator.setPhoneNumber(
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
LinphoneUtils.getCountryCode(mDialCode));
|
||||
boolean isOk = status == AccountCreator.PhoneNumberStatus.Ok.toInt();
|
||||
if (isOk) {
|
||||
mAccountCreator.linkAccount();
|
||||
} else {
|
||||
mCreateAccount.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForPhoneNumberStatus(status), AssistantActivity.instance());
|
||||
LinphoneUtils.displayError(
|
||||
isOk, mPhoneNumberError, LinphoneUtils.errorForPhoneNumberStatus(status));
|
||||
}
|
||||
}
|
||||
|
||||
private void createAccount() {
|
||||
if ((getResources().getBoolean(R.bool.isTablet)
|
||||
|| !getResources().getBoolean(R.bool.use_phone_number_validation))
|
||||
&& mUseEmail.isChecked()) {
|
||||
AccountCreator.EmailStatus emailStatus;
|
||||
AccountCreator.PasswordStatus passwordStatus;
|
||||
|
||||
passwordStatus = mAccountCreator.setPassword(mPasswordEdit.getText().toString());
|
||||
emailStatus = mAccountCreator.setEmail(mEmailEdit.getText().toString());
|
||||
|
||||
if (!mEmailOk) {
|
||||
LinphoneUtils.displayError(
|
||||
false, mEmailError, LinphoneUtils.errorForEmailStatus(emailStatus));
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForEmailStatus(emailStatus),
|
||||
AssistantActivity.instance());
|
||||
} else if (!mPasswordOk) {
|
||||
LinphoneUtils.displayError(
|
||||
false,
|
||||
mPasswordError,
|
||||
LinphoneUtils.errorForPasswordStatus(passwordStatus));
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForPasswordStatus(passwordStatus),
|
||||
AssistantActivity.instance());
|
||||
} else if (!mConfirmPasswordOk) {
|
||||
String msg;
|
||||
if (mPasswordConfirmEdit
|
||||
.getText()
|
||||
.toString()
|
||||
.equals(mPasswordEdit.getText().toString())) {
|
||||
msg = getString(R.string.wizard_password_incorrect);
|
||||
} else {
|
||||
msg = getString(R.string.wizard_passwords_unmatched);
|
||||
}
|
||||
LinphoneUtils.displayError(false, mPasswordError, msg);
|
||||
LinphoneUtils.displayErrorAlert(msg, AssistantActivity.instance());
|
||||
} else {
|
||||
mAccountCreator.createAccount();
|
||||
}
|
||||
} else {
|
||||
if (mPhoneNumberEdit.length() > 0 || mDialCode.length() > 1) {
|
||||
int phoneStatus;
|
||||
boolean isOk;
|
||||
phoneStatus =
|
||||
mAccountCreator.setPhoneNumber(
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
LinphoneUtils.getCountryCode(mDialCode));
|
||||
isOk = phoneStatus == AccountCreator.PhoneNumberStatus.Ok.toInt();
|
||||
if (!mUseUsername.isChecked() && mAccountCreator.getUsername() == null) {
|
||||
mAccountCreator.setUsername(mAccountCreator.getPhoneNumber());
|
||||
} else {
|
||||
mAccountCreator.setUsername(mUsernameEdit.getText().toString());
|
||||
mAccountCreator.setPhoneNumber(
|
||||
mPhoneNumberEdit.getText().toString(), mDialCode.getText().toString());
|
||||
}
|
||||
if (isOk) {
|
||||
mAccountCreator.createAccount();
|
||||
} else {
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForPhoneNumberStatus(phoneStatus),
|
||||
AssistantActivity.instance());
|
||||
LinphoneUtils.displayError(
|
||||
isOk,
|
||||
mPhoneNumberError,
|
||||
LinphoneUtils.errorForPhoneNumberStatus(phoneStatus));
|
||||
}
|
||||
} else {
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
getString(R.string.assistant_create_account_part_1),
|
||||
AssistantActivity.instance());
|
||||
}
|
||||
}
|
||||
mCreateAccount.setEnabled(true);
|
||||
}
|
||||
|
||||
private int getPhoneNumberStatus() {
|
||||
int status =
|
||||
mAccountCreator.setPhoneNumber(
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
LinphoneUtils.getCountryCode(mDialCode));
|
||||
mAddressSip = mAccountCreator.getPhoneNumber();
|
||||
return status;
|
||||
}
|
||||
|
||||
private void onTextChanged2() {
|
||||
String msg = "";
|
||||
mAccountCreator.setUsername(getUsername());
|
||||
|
||||
if (!mUseEmail.isChecked()
|
||||
&& getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
int status = getPhoneNumberStatus();
|
||||
boolean isOk = (status == AccountCreator.PhoneNumberStatus.Ok.toInt());
|
||||
LinphoneUtils.displayError(
|
||||
isOk, mPhoneNumberError, LinphoneUtils.errorForPhoneNumberStatus(status));
|
||||
|
||||
// Username or phone number
|
||||
if (getResources().getBoolean(R.bool.assistant_allow_username)
|
||||
&& mUseUsername.isChecked()) {
|
||||
mAddressSip = getUsername();
|
||||
}
|
||||
|
||||
if (!isOk) {
|
||||
if (status == AccountCreator.PhoneNumberStatus.InvalidCountryCode.toInt()) {
|
||||
mDialCode.setBackgroundResource(R.drawable.resizable_textfield_error);
|
||||
mPhoneNumberEdit.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
} else {
|
||||
mDialCode.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
mPhoneNumberEdit.setBackgroundResource(R.drawable.resizable_textfield_error);
|
||||
}
|
||||
|
||||
} else {
|
||||
mDialCode.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
mPhoneNumberEdit.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
if (!mLinkAccount && mAddressSip.length() > 0) {
|
||||
msg =
|
||||
getResources()
|
||||
.getString(
|
||||
R.string
|
||||
.assistant_create_account_phone_number_address)
|
||||
+ " <"
|
||||
+ mAddressSip
|
||||
+ "@"
|
||||
+ getResources().getString(R.string.default_domain)
|
||||
+ ">";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mAddressSip = getUsername();
|
||||
if (mAddressSip.length() > 0) {
|
||||
msg =
|
||||
getResources()
|
||||
.getString(
|
||||
R.string
|
||||
.assistant_create_account_phone_number_address)
|
||||
+ " <sip:"
|
||||
+ mAddressSip
|
||||
+ "@"
|
||||
+ getResources().getString(R.string.default_domain)
|
||||
+ ">";
|
||||
}
|
||||
}
|
||||
mSipUri.setText(msg);
|
||||
}
|
||||
|
||||
private void addPhoneNumberHandler(final EditText field) {
|
||||
field.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (field.equals(mDialCode)) {
|
||||
DialPlan c =
|
||||
AssistantActivity.instance()
|
||||
.getCountryListAdapter()
|
||||
.getCountryFromCountryCode(
|
||||
mDialCode.getText().toString());
|
||||
if (c != null) {
|
||||
AssistantActivity.instance().country = c;
|
||||
mSelectCountry.setText(c.getCountry());
|
||||
} else {
|
||||
mSelectCountry.setText(R.string.select_your_country);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {
|
||||
onTextChanged2();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addUsernameHandler(final EditText field) {
|
||||
field.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
public void afterTextChanged(Editable s) {
|
||||
Matcher matcher = UPPER_CASE_REGEX.matcher(s);
|
||||
while (matcher.find()) {
|
||||
CharSequence upperCaseRegion =
|
||||
s.subSequence(matcher.start(), matcher.end());
|
||||
s.replace(
|
||||
matcher.start(),
|
||||
matcher.end(),
|
||||
upperCaseRegion.toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {
|
||||
onTextChanged2();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addEmailHandler(final EditText field) {
|
||||
field.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
public void afterTextChanged(Editable s) {
|
||||
mEmailOk = false;
|
||||
AccountCreator.EmailStatus status =
|
||||
mAccountCreator.setEmail(field.getText().toString());
|
||||
if (status.equals(AccountCreator.EmailStatus.Ok)) {
|
||||
mEmailOk = true;
|
||||
LinphoneUtils.displayError(mEmailOk, mEmailError, "");
|
||||
} else {
|
||||
LinphoneUtils.displayError(
|
||||
mEmailOk,
|
||||
mEmailError,
|
||||
LinphoneUtils.errorForEmailStatus(status));
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
});
|
||||
}
|
||||
|
||||
private void addPasswordHandler(final EditText field1) {
|
||||
TextWatcher passwordListener =
|
||||
new TextWatcher() {
|
||||
public void afterTextChanged(Editable s) {
|
||||
mPasswordOk = false;
|
||||
AccountCreator.PasswordStatus status =
|
||||
mAccountCreator.setPassword(field1.getText().toString());
|
||||
if (isPasswordCorrect(field1.getText().toString())) {
|
||||
mPasswordOk = true;
|
||||
LinphoneUtils.displayError(mPasswordOk, mPasswordError, "");
|
||||
} else {
|
||||
LinphoneUtils.displayError(
|
||||
mPasswordOk,
|
||||
mPasswordError,
|
||||
LinphoneUtils.errorForPasswordStatus(status));
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
};
|
||||
|
||||
field1.addTextChangedListener(passwordListener);
|
||||
}
|
||||
|
||||
private void addConfirmPasswordHandler(final EditText field1, final EditText field2) {
|
||||
TextWatcher passwordListener =
|
||||
new TextWatcher() {
|
||||
public void afterTextChanged(Editable s) {
|
||||
mConfirmPasswordOk = false;
|
||||
if (field1.getText().toString().equals(field2.getText().toString())) {
|
||||
mConfirmPasswordOk = true;
|
||||
if (!isPasswordCorrect(field1.getText().toString())) {
|
||||
LinphoneUtils.displayError(
|
||||
mPasswordOk,
|
||||
mPasswordError,
|
||||
getString(R.string.wizard_password_incorrect));
|
||||
} else {
|
||||
LinphoneUtils.displayError(
|
||||
mConfirmPasswordOk, mPasswordConfirmError, "");
|
||||
}
|
||||
} else {
|
||||
LinphoneUtils.displayError(
|
||||
mConfirmPasswordOk,
|
||||
mPasswordConfirmError,
|
||||
getString(R.string.wizard_passwords_unmatched));
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
};
|
||||
field1.addTextChangedListener(passwordListener);
|
||||
field2.addTextChangedListener(passwordListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountExist(AccountCreator accountCreator, final Status status, String resp) {
|
||||
if (status.equals(Status.AccountExist) || status.equals(Status.AccountExistWithAlias)) {
|
||||
if (mUseEmail.isChecked()) {
|
||||
mCreateAccount.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForStatus(status), AssistantActivity.instance());
|
||||
} else {
|
||||
accountCreator.isAliasUsed();
|
||||
}
|
||||
} else {
|
||||
createAccount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateAccount(AccountCreator accountCreator, Status status, String resp) {
|
||||
if (status.equals(Status.AccountCreated)) {
|
||||
if (mUseEmail.isChecked()
|
||||
|| !getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
AssistantActivity.instance()
|
||||
.displayAssistantConfirm(
|
||||
getUsername(),
|
||||
mPasswordEdit.getText().toString(),
|
||||
mEmailEdit.getText().toString());
|
||||
} else {
|
||||
AssistantActivity.instance()
|
||||
.displayAssistantCodeConfirm(
|
||||
getUsername(),
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
LinphoneUtils.getCountryCode(mDialCode),
|
||||
false);
|
||||
}
|
||||
} else {
|
||||
mCreateAccount.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForStatus(status), AssistantActivity.instance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivateAccount(AccountCreator accountCreator, Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onLinkAccount(AccountCreator accountCreator, Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(Status.RequestOk)) {
|
||||
AssistantActivity.instance()
|
||||
.displayAssistantCodeConfirm(
|
||||
getUsername(),
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
LinphoneUtils.getCountryCode(mDialCode),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivateAlias(AccountCreator accountCreator, Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(Status.RequestOk)) {
|
||||
AssistantActivity.instance()
|
||||
.displayAssistantCodeConfirm(
|
||||
getUsername(),
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
LinphoneUtils.getCountryCode(mDialCode),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountActivated(AccountCreator accountCreator, Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(Status.AccountNotActivated)) {
|
||||
if (getResources().getBoolean(R.bool.isTablet)
|
||||
|| !getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
// mAccountCreator.activateAccount(); // Resend email TODO
|
||||
} else {
|
||||
accountCreator.recoverAccount(); // Resend SMS
|
||||
}
|
||||
} else {
|
||||
mCreateAccount.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForStatus(status), AssistantActivity.instance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecoverAccount(AccountCreator accountCreator, Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(Status.RequestOk)) {
|
||||
AssistantActivity.instance()
|
||||
.displayAssistantCodeConfirm(
|
||||
getUsername(),
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
mDialCode.getText().toString(),
|
||||
false);
|
||||
} else {
|
||||
mCreateAccount.setEnabled(true);
|
||||
// SMS error
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
getString(R.string.request_failed), AssistantActivity.instance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountLinked(AccountCreator accountCreator, Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAliasUsed(AccountCreator ac, Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
return;
|
||||
}
|
||||
if (status.equals(Status.AliasIsAccount) || status.equals(Status.AliasExist)) {
|
||||
if (mAccountCreator.getPhoneNumber() != null
|
||||
&& mAccountCreator.getUsername() != null
|
||||
&& mAccountCreator.getPhoneNumber().compareTo(mAccountCreator.getUsername())
|
||||
== 0) {
|
||||
mAccountCreator.isAccountActivated();
|
||||
} else {
|
||||
mCreateAccount.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForStatus(status), AssistantActivity.instance());
|
||||
}
|
||||
} else {
|
||||
mAccountCreator.isAccountActivated();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateAccount(AccountCreator accountCreator, Status status, String resp) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package org.linphone.assistant;
|
||||
|
||||
/*
|
||||
EchoCancellerCalibrationFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.EcCalibratorStatus;
|
||||
import org.linphone.core.XmlRpcArgType;
|
||||
import org.linphone.core.XmlRpcRequest;
|
||||
import org.linphone.core.XmlRpcRequestListener;
|
||||
import org.linphone.core.XmlRpcSession;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
public class EchoCancellerCalibrationFragment extends Fragment implements XmlRpcRequestListener {
|
||||
private final Handler mHandler = new Handler();
|
||||
private boolean mSendEcCalibrationResult = false;
|
||||
private CoreListenerStub mListener;
|
||||
private XmlRpcSession mXmlRpcSession;
|
||||
private XmlRpcRequest mXmlRpcRequest;
|
||||
private Runnable mRunFinished;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.assistant_ec_calibration, container, false);
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onEcCalibrationResult(
|
||||
Core lc, EcCalibratorStatus status, int delay_ms) {
|
||||
lc.removeListener(mListener);
|
||||
LinphoneManager.getInstance().routeAudioToReceiver();
|
||||
if (mSendEcCalibrationResult) {
|
||||
sendEcCalibrationResult(status, delay_ms);
|
||||
} else {
|
||||
AssistantActivity.instance().isEchoCalibrationFinished();
|
||||
}
|
||||
}
|
||||
};
|
||||
mRunFinished =
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
AssistantActivity.instance().isEchoCalibrationFinished();
|
||||
}
|
||||
};
|
||||
|
||||
mXmlRpcSession =
|
||||
LinphoneManager.getLcIfManagerNotDestroyedOrNull()
|
||||
.createXmlRpcSession(LinphonePreferences.instance().getXmlrpcUrl());
|
||||
mXmlRpcRequest =
|
||||
mXmlRpcSession.createRequest(XmlRpcArgType.None, "add_ec_calibration_result");
|
||||
mXmlRpcRequest.setListener(this);
|
||||
|
||||
LinphoneManager.getLc().addListener(mListener);
|
||||
LinphoneManager.getInstance().startEcCalibration();
|
||||
return view;
|
||||
}
|
||||
|
||||
public void enableEcCalibrationResultSending(boolean enabled) {
|
||||
mSendEcCalibrationResult = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(XmlRpcRequest request) {
|
||||
mHandler.post(mRunFinished);
|
||||
}
|
||||
|
||||
private void sendEcCalibrationResult(EcCalibratorStatus status, int delayMs) {
|
||||
Boolean hasBuiltInEchoCanceler = LinphoneManager.getLc().hasBuiltinEchoCanceller();
|
||||
Log.i(
|
||||
"Add echo canceller calibration result: manufacturer="
|
||||
+ Build.MANUFACTURER
|
||||
+ " model="
|
||||
+ Build.MODEL
|
||||
+ " status="
|
||||
+ status
|
||||
+ " delay="
|
||||
+ delayMs
|
||||
+ "ms"
|
||||
+ " hasBuiltInEchoCanceler "
|
||||
+ hasBuiltInEchoCanceler);
|
||||
mXmlRpcRequest.addStringArg(Build.MANUFACTURER);
|
||||
mXmlRpcRequest.addStringArg(Build.MODEL);
|
||||
mXmlRpcRequest.addStringArg(status.toString());
|
||||
mXmlRpcRequest.addIntArg(delayMs);
|
||||
mXmlRpcRequest.addIntArg(hasBuiltInEchoCanceler ? 1 : 0);
|
||||
mXmlRpcSession.sendRequest(mXmlRpcRequest);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,431 @@
|
|||
package org.linphone.assistant;
|
||||
/*
|
||||
LinphoneLoginFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import java.util.Locale;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.AccountCreator;
|
||||
import org.linphone.core.AccountCreatorListener;
|
||||
import org.linphone.core.DialPlan;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
public class LinphoneLoginFragment extends Fragment
|
||||
implements CompoundButton.OnCheckedChangeListener,
|
||||
OnClickListener,
|
||||
TextWatcher,
|
||||
AccountCreatorListener {
|
||||
private EditText mLogin, mPassword, mPhoneNumberEdit, mDialCode;
|
||||
private Button mApply, mSelectCountry;
|
||||
private CheckBox mUseUsername;
|
||||
private LinearLayout mPhoneNumberLayout, mUsernameLayout, mPasswordLayout;
|
||||
private TextView mForgotPassword, mMessagePhoneNumber, mPhoneNumberError;
|
||||
private Boolean mRecoverAccount;
|
||||
private AccountCreator mAccountCreator;
|
||||
private int mCountryCode;
|
||||
private String mPhone, mDialcode, mUsername, mPwd;
|
||||
private ImageView mPhoneNumberInfo;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.assistant_linphone_login, container, false);
|
||||
|
||||
mAccountCreator =
|
||||
LinphoneManager.getLc()
|
||||
.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
|
||||
mAccountCreator.setListener(this);
|
||||
|
||||
mLogin = view.findViewById(R.id.assistant_username);
|
||||
mLogin.addTextChangedListener(this);
|
||||
|
||||
mRecoverAccount = true;
|
||||
|
||||
mDialCode = view.findViewById(R.id.dial_code);
|
||||
|
||||
mPhoneNumberEdit = view.findViewById(R.id.phone_number);
|
||||
mPhoneNumberLayout = view.findViewById(R.id.phone_number_layout);
|
||||
mPhoneNumberError = view.findViewById(R.id.phone_number_error_2);
|
||||
|
||||
mPhoneNumberInfo = view.findViewById(R.id.info_phone_number);
|
||||
|
||||
mUseUsername = view.findViewById(R.id.use_username);
|
||||
mUsernameLayout = view.findViewById(R.id.username_layout);
|
||||
mPasswordLayout = view.findViewById(R.id.password_layout);
|
||||
mPassword = view.findViewById(R.id.assistant_password);
|
||||
mMessagePhoneNumber = view.findViewById(R.id.message_phone_number);
|
||||
|
||||
mForgotPassword = view.findViewById(R.id.forgot_password);
|
||||
mSelectCountry = view.findViewById(R.id.select_country);
|
||||
|
||||
mApply = view.findViewById(R.id.assistant_apply);
|
||||
mApply.setEnabled(true);
|
||||
mApply.setOnClickListener(this);
|
||||
|
||||
// Phone number
|
||||
if (getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
mMessagePhoneNumber.setText(getString(R.string.assistant_create_account_part_1));
|
||||
mPhone = getArguments().getString("Phone");
|
||||
mDialcode = getArguments().getString("Dialcode");
|
||||
|
||||
getActivity().getApplicationContext();
|
||||
// Automatically get the country code from the mPhone
|
||||
TelephonyManager tm =
|
||||
(TelephonyManager)
|
||||
getActivity()
|
||||
.getApplicationContext()
|
||||
.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
String countryIso = tm.getNetworkCountryIso();
|
||||
mCountryCode = org.linphone.core.Utils.getCccFromIso(countryIso.toUpperCase());
|
||||
|
||||
DialPlan c = AssistantActivity.instance().country;
|
||||
if (c != null) {
|
||||
mSelectCountry.setText(c.getCountry());
|
||||
mDialCode.setText(
|
||||
c.getCountryCallingCode().contains("+")
|
||||
? c.getCountryCallingCode()
|
||||
: "+" + c.getCountryCallingCode());
|
||||
} else {
|
||||
c =
|
||||
AssistantActivity.instance()
|
||||
.getCountryListAdapter()
|
||||
.getCountryFromCountryCode(String.valueOf(mCountryCode));
|
||||
if (c != null) {
|
||||
mSelectCountry.setText(c.getCountry());
|
||||
mDialCode.setText(
|
||||
c.getCountryCallingCode().contains("+")
|
||||
? c.getCountryCallingCode()
|
||||
: "+" + c.getCountryCallingCode());
|
||||
}
|
||||
}
|
||||
|
||||
mPhoneNumberLayout.setVisibility(View.VISIBLE);
|
||||
mSelectCountry.setOnClickListener(this);
|
||||
mPhoneNumberInfo.setOnClickListener(this);
|
||||
|
||||
// Allow user to enter a mUsername instead use the mPhone number as mUsername
|
||||
if (getResources().getBoolean(R.bool.assistant_allow_username)) {
|
||||
mUseUsername.setVisibility(View.VISIBLE);
|
||||
mUseUsername.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
if (mPhone != null) mPhoneNumberEdit.setText(mPhone);
|
||||
if (mDialcode != null) mDialCode.setText("+" + mDialcode);
|
||||
}
|
||||
|
||||
if (getResources().getBoolean(R.bool.assistant_allow_username)) {
|
||||
mUseUsername.setVisibility(View.VISIBLE);
|
||||
mUseUsername.setOnCheckedChangeListener(this);
|
||||
mPassword.addTextChangedListener(this);
|
||||
mForgotPassword.setText(
|
||||
Html.fromHtml(
|
||||
"<a href=\""
|
||||
+ getString(R.string.recover_password_link)
|
||||
+ "\"'>"
|
||||
+ getString(R.string.forgot_password)
|
||||
+ "</a>"));
|
||||
mForgotPassword.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
// Hide mPhone number and display mUsername/email/mPassword
|
||||
if (!getResources().getBoolean(R.bool.use_phone_number_validation)) {
|
||||
mPhoneNumberLayout.setVisibility(View.GONE);
|
||||
mUseUsername.setVisibility(View.GONE);
|
||||
|
||||
mUsernameLayout.setVisibility(View.VISIBLE);
|
||||
mPasswordLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// When we come from generic mLogin fragment
|
||||
mUsername = getArguments().getString("Username");
|
||||
mPwd = getArguments().getString("Password");
|
||||
if (mUsername != null && mPwd != null) {
|
||||
mUseUsername.setChecked(true);
|
||||
onCheckedChanged(mUseUsername, true);
|
||||
mLogin.setText(mUsername);
|
||||
mPassword.setText(mPwd);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
mAccountCreator.setLanguage(Locale.getDefault().toLanguageTag());
|
||||
}
|
||||
|
||||
addPhoneNumberHandler(mDialCode);
|
||||
addPhoneNumberHandler(mPhoneNumberEdit);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void linphoneLogIn() {
|
||||
if (mLogin.getText() == null
|
||||
|| mLogin.length() == 0
|
||||
|| mPassword.getText() == null
|
||||
|| mPassword.length() == 0) {
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
getString(R.string.first_launch_no_login_password),
|
||||
AssistantActivity.instance());
|
||||
mApply.setEnabled(true);
|
||||
return;
|
||||
}
|
||||
mAccountCreator.setUsername(mLogin.getText().toString());
|
||||
mAccountCreator.setPassword(mPassword.getText().toString());
|
||||
mAccountCreator.setDomain(getString(R.string.default_domain));
|
||||
mAccountCreator.isAccountExist();
|
||||
}
|
||||
|
||||
private int getPhoneNumberStatus() {
|
||||
return mAccountCreator.setPhoneNumber(
|
||||
mPhoneNumberEdit.getText().toString(), LinphoneUtils.getCountryCode(mDialCode));
|
||||
}
|
||||
|
||||
private void addPhoneNumberHandler(final EditText field) {
|
||||
field.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (field.equals(mDialCode)) {
|
||||
DialPlan c =
|
||||
AssistantActivity.instance()
|
||||
.getCountryListAdapter()
|
||||
.getCountryFromCountryCode(
|
||||
mDialCode.getText().toString());
|
||||
if (c != null) {
|
||||
AssistantActivity.instance().country = c;
|
||||
mSelectCountry.setText(c.getCountry());
|
||||
} else {
|
||||
mSelectCountry.setText(R.string.select_your_country);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {
|
||||
onTextChanged2();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mRecoverAccount = mUseUsername == null || !mUseUsername.isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.assistant_apply) {
|
||||
mApply.setEnabled(false);
|
||||
if (mRecoverAccount) {
|
||||
recoverAccount();
|
||||
} else {
|
||||
linphoneLogIn();
|
||||
}
|
||||
} else if (id == R.id.info_phone_number) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.phone_number_info_title))
|
||||
.setMessage(getString(R.string.phone_number_link_info_content))
|
||||
.show();
|
||||
} else if (id == R.id.select_country) {
|
||||
AssistantActivity.instance().displayCountryChooser();
|
||||
}
|
||||
}
|
||||
|
||||
private void recoverAccount() {
|
||||
if (mPhoneNumberEdit.length() > 0 || mDialCode.length() > 1) {
|
||||
int status = getPhoneNumberStatus();
|
||||
boolean isOk = status == AccountCreator.PhoneNumberStatus.Ok.toInt();
|
||||
if (isOk) {
|
||||
mAccountCreator.isAliasUsed();
|
||||
} else {
|
||||
mApply.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForPhoneNumberStatus(status),
|
||||
AssistantActivity.instance());
|
||||
LinphoneUtils.displayError(
|
||||
isOk, mPhoneNumberError, LinphoneUtils.errorForPhoneNumberStatus(status));
|
||||
}
|
||||
} else {
|
||||
mApply.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
getString(R.string.assistant_create_account_part_1),
|
||||
AssistantActivity.instance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
private void onTextChanged2() {
|
||||
int status = getPhoneNumberStatus();
|
||||
boolean isOk = status == AccountCreator.PhoneNumberStatus.Ok.toInt();
|
||||
LinphoneUtils.displayError(
|
||||
isOk, mPhoneNumberError, LinphoneUtils.errorForPhoneNumberStatus(status));
|
||||
if (!isOk) {
|
||||
if ((1 == (status & AccountCreator.PhoneNumberStatus.InvalidCountryCode.toInt()))) {
|
||||
mDialCode.setBackgroundResource(R.drawable.resizable_textfield_error);
|
||||
mPhoneNumberEdit.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
} else {
|
||||
mDialCode.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
mPhoneNumberEdit.setBackgroundResource(R.drawable.resizable_textfield_error);
|
||||
}
|
||||
} else {
|
||||
mAccountCreator.setPhoneNumber(
|
||||
mPhoneNumberEdit.getText().toString(), mDialCode.getText().toString());
|
||||
mDialCode.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
mPhoneNumberEdit.setBackgroundResource(R.drawable.resizable_textfield);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
onTextChanged2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
if (buttonView.getId() == R.id.use_username) {
|
||||
if (isChecked) {
|
||||
mUsernameLayout.setVisibility(View.VISIBLE);
|
||||
mPasswordLayout.setVisibility(View.VISIBLE);
|
||||
mPhoneNumberEdit.setVisibility(EditText.GONE);
|
||||
mPhoneNumberLayout.setVisibility(LinearLayout.GONE);
|
||||
mMessagePhoneNumber.setText(getString(R.string.assistant_linphone_login_desc));
|
||||
mRecoverAccount = false;
|
||||
} else {
|
||||
mUsernameLayout.setVisibility(View.GONE);
|
||||
mPasswordLayout.setVisibility(View.GONE);
|
||||
mPhoneNumberEdit.setVisibility(EditText.VISIBLE);
|
||||
mPhoneNumberLayout.setVisibility(LinearLayout.VISIBLE);
|
||||
mMessagePhoneNumber.setText(getString(R.string.assistant_create_account_part_1));
|
||||
mRecoverAccount = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountExist(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
mApply.setEnabled(true);
|
||||
return;
|
||||
}
|
||||
if (status.equals(AccountCreator.Status.AccountExist)
|
||||
|| status.equals(AccountCreator.Status.AccountExistWithAlias)) {
|
||||
AssistantActivity.instance().linphoneLogIn(accountCreator);
|
||||
} else {
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForStatus(status), AssistantActivity.instance());
|
||||
}
|
||||
mApply.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onLinkAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onActivateAlias(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAccountActivated(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onRecoverAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
mApply.setEnabled(true);
|
||||
return;
|
||||
}
|
||||
if (status.equals(AccountCreator.Status.ServerError)) {
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForStatus(AccountCreator.Status.RequestFailed),
|
||||
AssistantActivity.instance());
|
||||
mApply.setEnabled(true);
|
||||
} else {
|
||||
AssistantActivity.instance()
|
||||
.displayAssistantCodeConfirm(
|
||||
accountCreator.getUsername(),
|
||||
mPhoneNumberEdit.getText().toString(),
|
||||
LinphoneUtils.getCountryCode(mDialCode),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsAccountLinked(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
|
||||
@Override
|
||||
public void onIsAliasUsed(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {
|
||||
if (AssistantActivity.instance() == null) {
|
||||
mApply.setEnabled(true);
|
||||
return;
|
||||
}
|
||||
if (status.equals(AccountCreator.Status.AliasIsAccount)
|
||||
|| status.equals(AccountCreator.Status.AliasExist)) {
|
||||
accountCreator.recoverAccount();
|
||||
} else {
|
||||
mApply.setEnabled(true);
|
||||
LinphoneUtils.displayErrorAlert(
|
||||
LinphoneUtils.errorForStatus(status), AssistantActivity.instance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateAccount(
|
||||
AccountCreator accountCreator, AccountCreator.Status status, String resp) {}
|
||||
}
|
||||
125
app/src/main/java/org/linphone/assistant/LoginFragment.java
Normal file
125
app/src/main/java/org/linphone/assistant/LoginFragment.java
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
package org.linphone.assistant;
|
||||
/*
|
||||
LoginFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.Toast;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.TransportType;
|
||||
|
||||
public class LoginFragment extends Fragment implements OnClickListener, TextWatcher {
|
||||
private EditText mLogin, mUserid, mPassword, mDomain, mDisplayName;
|
||||
private RadioGroup mTransports;
|
||||
private Button mApply;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.assistant_login, container, false);
|
||||
|
||||
mLogin = view.findViewById(R.id.assistant_username);
|
||||
mLogin.addTextChangedListener(this);
|
||||
mDisplayName = view.findViewById(R.id.assistant_display_name);
|
||||
mDisplayName.addTextChangedListener(this);
|
||||
mUserid = view.findViewById(R.id.assistant_userid);
|
||||
mUserid.addTextChangedListener(this);
|
||||
mPassword = view.findViewById(R.id.assistant_password);
|
||||
mPassword.addTextChangedListener(this);
|
||||
mDomain = view.findViewById(R.id.assistant_domain);
|
||||
mDomain.addTextChangedListener(this);
|
||||
mTransports = view.findViewById(R.id.assistant_transports);
|
||||
mApply = view.findViewById(R.id.assistant_apply);
|
||||
mApply.setEnabled(false);
|
||||
mApply.setOnClickListener(this);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
|
||||
if (id == R.id.assistant_apply) {
|
||||
if (mLogin.getText() == null
|
||||
|| mLogin.length() == 0
|
||||
|| mPassword.getText() == null
|
||||
|| mPassword.length() == 0
|
||||
|| mDomain.getText() == null
|
||||
|| mDomain.length() == 0) {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.first_launch_no_login_password),
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
|
||||
TransportType transport;
|
||||
if (mTransports.getCheckedRadioButtonId() == R.id.transport_udp) {
|
||||
transport = TransportType.Udp;
|
||||
} else {
|
||||
if (mTransports.getCheckedRadioButtonId() == R.id.transport_tcp) {
|
||||
transport = TransportType.Tcp;
|
||||
} else {
|
||||
transport = TransportType.Tls;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDomain.getText().toString().compareTo(getString(R.string.default_domain)) == 0) {
|
||||
AssistantActivity.instance()
|
||||
.displayLoginLinphone(
|
||||
mLogin.getText().toString(), mPassword.getText().toString());
|
||||
} else {
|
||||
AssistantActivity.instance()
|
||||
.genericLogIn(
|
||||
mLogin.getText().toString(),
|
||||
mUserid.getText().toString(),
|
||||
mPassword.getText().toString(),
|
||||
mDisplayName.getText().toString(),
|
||||
null,
|
||||
mDomain.getText().toString(),
|
||||
transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
mApply.setEnabled(
|
||||
!mLogin.getText().toString().isEmpty()
|
||||
&& !mPassword.getText().toString().isEmpty()
|
||||
&& !mDomain.getText().toString().isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
}
|
||||
99
app/src/main/java/org/linphone/assistant/QrCodeFragment.java
Normal file
99
app/src/main/java/org/linphone/assistant/QrCodeFragment.java
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package org.linphone.assistant;
|
||||
|
||||
/*
|
||||
QrCodeFragment.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
|
||||
|
||||
public class QrCodeFragment extends Fragment {
|
||||
private TextureView mQrcodeView;
|
||||
private CoreListenerStub mListener;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.qrcode, container, false);
|
||||
|
||||
mQrcodeView = view.findViewById(R.id.qrcodeCaptureSurface);
|
||||
|
||||
LinphoneManager.getLc().setNativePreviewWindowId(mQrcodeView);
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onQrcodeFound(Core lc, String result) {
|
||||
enableQrcodeReader(false);
|
||||
AssistantActivity.instance().displayRemoteProvisioning(result);
|
||||
}
|
||||
};
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void enableQrcodeReader(boolean enable) {
|
||||
LinphoneManager.getLc().enableQrcodeVideoPreview(enable);
|
||||
LinphoneManager.getLc().enableVideoPreview(enable);
|
||||
if (enable) {
|
||||
LinphoneManager.getLc().addListener(mListener);
|
||||
} else {
|
||||
LinphoneManager.getLc().removeListener(mListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackCamera() {
|
||||
int camId = 0;
|
||||
AndroidCameraConfiguration.AndroidCamera[] cameras =
|
||||
AndroidCameraConfiguration.retrieveCameras();
|
||||
for (AndroidCameraConfiguration.AndroidCamera androidCamera : cameras) {
|
||||
if (!androidCamera.frontFacing) camId = androidCamera.id;
|
||||
}
|
||||
String[] devices = LinphoneManager.getLc().getVideoDevicesList();
|
||||
String newDevice = devices[camId];
|
||||
LinphoneManager.getLc().setVideoDevice(newDevice);
|
||||
}
|
||||
|
||||
private void launchQrcodeReader() {
|
||||
setBackCamera();
|
||||
|
||||
enableQrcodeReader(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
launchQrcodeReader();
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
enableQrcodeReader(false);
|
||||
// setBackCamera(false);
|
||||
super.onPause();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
package org.linphone.assistant;
|
||||
/*
|
||||
RemoteProvisioningActivity.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import org.linphone.LinphoneLauncherActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.ConfiguringState;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.ThemableActivity;
|
||||
|
||||
public class RemoteProvisioningActivity extends ThemableActivity {
|
||||
private final Handler mHandler = new Handler();
|
||||
private String mConfigUriParam = null;
|
||||
private ProgressBar mSpinner;
|
||||
private CoreListenerStub mListener;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.remote_provisioning);
|
||||
mSpinner = findViewById(R.id.spinner);
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onConfiguringStatus(
|
||||
Core lc, final ConfiguringState state, String message) {
|
||||
if (mSpinner != null) mSpinner.setVisibility(View.GONE);
|
||||
if (state == ConfiguringState.Successful) {
|
||||
goToLinphoneActivity();
|
||||
} else if (state == ConfiguringState.Failed) {
|
||||
Toast.makeText(
|
||||
RemoteProvisioningActivity.this,
|
||||
R.string.remote_provisioning_failure,
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
LinphonePreferences.instance().setContext(this);
|
||||
|
||||
checkIntentForConfigUri(getIntent());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
checkIntentForConfigUri(intent);
|
||||
}
|
||||
|
||||
private void checkIntentForConfigUri(final Intent intent) {
|
||||
new Thread(
|
||||
new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Uri openUri = intent.getData();
|
||||
if (openUri != null) {
|
||||
// We expect something like
|
||||
// linphone-config://http://linphone.org/config.xml
|
||||
mConfigUriParam =
|
||||
openUri.getEncodedSchemeSpecificPart()
|
||||
.substring(2); // Removes the linphone-config://
|
||||
try {
|
||||
mConfigUriParam =
|
||||
URLDecoder.decode(mConfigUriParam, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
Log.d("Using config uri: " + mConfigUriParam);
|
||||
}
|
||||
|
||||
if (mConfigUriParam == null) {
|
||||
if (!LinphonePreferences.instance()
|
||||
.isFirstRemoteProvisioning()) {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
goToLinphoneActivity();
|
||||
}
|
||||
});
|
||||
} else if (!getResources()
|
||||
.getBoolean(
|
||||
R.bool.forbid_app_usage_until_remote_provisioning_completed)) {
|
||||
// Show this view for a few seconds then go to the dialer
|
||||
mHandler.postDelayed(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
goToLinphoneActivity();
|
||||
}
|
||||
},
|
||||
1500);
|
||||
} // else we do nothing if there is no config uri parameter and
|
||||
// if user not allowed to leave this screen
|
||||
} else {
|
||||
if (getResources()
|
||||
.getBoolean(
|
||||
R.bool.display_confirmation_popup_after_first_configuration)
|
||||
&& !LinphonePreferences.instance()
|
||||
.isFirstRemoteProvisioning()) {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
displayDialogConfirmation();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setRemoteProvisioningAddressAndRestart(
|
||||
mConfigUriParam);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private void displayDialogConfirmation() {
|
||||
new AlertDialog.Builder(RemoteProvisioningActivity.this)
|
||||
.setTitle(getString(R.string.remote_provisioning_again_title))
|
||||
.setMessage(getString(R.string.remote_provisioning_again_message))
|
||||
.setPositiveButton(
|
||||
R.string.accept,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setRemoteProvisioningAddressAndRestart(mConfigUriParam);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
goToLinphoneActivity();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void setRemoteProvisioningAddressAndRestart(final String configUri) {
|
||||
if (mSpinner != null) mSpinner.setVisibility(View.VISIBLE);
|
||||
|
||||
LinphonePreferences.instance().setContext(this); // Needed, else the next call will crash
|
||||
LinphonePreferences.instance().setRemoteProvisioningUrl(configUri);
|
||||
|
||||
LinphoneManager.getLc().getConfig().sync();
|
||||
LinphoneManager.getInstance().restartCore();
|
||||
}
|
||||
|
||||
private void goToLinphoneActivity() {
|
||||
if (LinphoneService.isReady()) {
|
||||
LinphoneService.instance()
|
||||
.setActivityToLaunchOnIncomingReceived("org.linphone.LinphoneLauncherActivity");
|
||||
// finish(); // To prevent the user to come back to this page using back button
|
||||
startActivity(new Intent().setClass(this, LinphoneLauncherActivity.class));
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,25 +28,30 @@ import android.view.View.OnClickListener;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphonePreferences;
|
||||
import org.linphone.R;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
public class RemoteProvisioningFragment extends Fragment implements OnClickListener, TextWatcher {
|
||||
private EditText remoteProvisioningUrl;
|
||||
private Button apply;
|
||||
private EditText mRemoteProvisioningUrl;
|
||||
private Button mApply, mQrcode;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.assistant_remote_provisioning, container, false);
|
||||
|
||||
remoteProvisioningUrl = view.findViewById(R.id.assistant_remote_provisioning_url);
|
||||
remoteProvisioningUrl.addTextChangedListener(this);
|
||||
apply = view.findViewById(R.id.assistant_apply);
|
||||
apply.setEnabled(false);
|
||||
apply.setOnClickListener(this);
|
||||
mRemoteProvisioningUrl = view.findViewById(R.id.assistant_remote_provisioning_url);
|
||||
mRemoteProvisioningUrl.addTextChangedListener(this);
|
||||
mQrcode = view.findViewById(R.id.assistant_qrcode);
|
||||
mQrcode.setOnClickListener(this);
|
||||
mApply = view.findViewById(R.id.assistant_apply);
|
||||
mApply.setEnabled(false);
|
||||
mApply.setOnClickListener(this);
|
||||
|
||||
if (getArguments() != null && !getArguments().getString("RemoteUrl").isEmpty()) {
|
||||
mRemoteProvisioningUrl.setText(getArguments().getString("RemoteUrl"));
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
|
@ -56,27 +61,25 @@ public class RemoteProvisioningFragment extends Fragment implements OnClickListe
|
|||
int id = v.getId();
|
||||
|
||||
if (id == R.id.assistant_apply) {
|
||||
String url = remoteProvisioningUrl.getText().toString();
|
||||
String url = mRemoteProvisioningUrl.getText().toString();
|
||||
AssistantActivity.instance().displayRemoteProvisioningInProgressDialog();
|
||||
LinphonePreferences.instance().setRemoteProvisioningUrl(url);
|
||||
LinphoneManager.getLc().getConfig().sync();
|
||||
LinphoneManager.getInstance().restartCore();
|
||||
AssistantActivity.instance().setCoreListener();
|
||||
} else if (id == R.id.assistant_qrcode) {
|
||||
AssistantActivity.instance().displayQRCodeReader();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
apply.setEnabled(!remoteProvisioningUrl.getText().toString().isEmpty());
|
||||
mApply.setEnabled(!mRemoteProvisioningUrl.getText().toString().isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
public void afterTextChanged(Editable s) {}
|
||||
}
|
||||
|
|
@ -25,19 +25,19 @@ import android.view.View.OnClickListener;
|
|||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphonePreferences;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.ConfiguringState;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.ThemableActivity;
|
||||
import org.linphone.xmlrpc.XmlRpcHelper;
|
||||
import org.linphone.xmlrpc.XmlRpcListenerBase;
|
||||
|
||||
public class RemoteProvisioningLoginActivity extends Activity implements OnClickListener {
|
||||
private EditText login, password, domain;
|
||||
private Button connect;
|
||||
public class RemoteProvisioningLoginActivity extends ThemableActivity implements OnClickListener {
|
||||
private EditText mLogin, mPassword, mDomain;
|
||||
private Button mConnect;
|
||||
private CoreListenerStub mListener;
|
||||
|
||||
@Override
|
||||
|
|
@ -45,53 +45,62 @@ public class RemoteProvisioningLoginActivity extends Activity implements OnClick
|
|||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.assistant_remote_provisioning_login);
|
||||
|
||||
login = findViewById(R.id.assistant_username);
|
||||
password = findViewById(R.id.assistant_password);
|
||||
domain = findViewById(R.id.assistant_domain);
|
||||
mLogin = findViewById(R.id.assistant_username);
|
||||
mPassword = findViewById(R.id.assistant_password);
|
||||
mDomain = findViewById(R.id.assistant_domain);
|
||||
|
||||
connect = findViewById(R.id.assistant_connect);
|
||||
connect.setOnClickListener(this);
|
||||
mConnect = findViewById(R.id.assistant_connect);
|
||||
mConnect.setOnClickListener(this);
|
||||
|
||||
String defaultDomain = getIntent().getStringExtra("Domain");
|
||||
if (defaultDomain != null) {
|
||||
domain.setText(defaultDomain);
|
||||
domain.setEnabled(false);
|
||||
mDomain.setText(defaultDomain);
|
||||
mDomain.setEnabled(false);
|
||||
}
|
||||
|
||||
mListener = new CoreListenerStub() {
|
||||
@Override
|
||||
public void onConfiguringStatus(Core lc, final ConfiguringState state, String message) {
|
||||
if (state == ConfiguringState.Successful) {
|
||||
//TODO
|
||||
} else if (state == ConfiguringState.Failed) {
|
||||
Toast.makeText(RemoteProvisioningLoginActivity.this, R.string.remote_provisioning_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onConfiguringStatus(
|
||||
Core lc, final ConfiguringState state, String message) {
|
||||
if (state == ConfiguringState.Successful) {
|
||||
// TODO
|
||||
} else if (state == ConfiguringState.Failed) {
|
||||
Toast.makeText(
|
||||
RemoteProvisioningLoginActivity.this,
|
||||
R.string.remote_provisioning_failure,
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void cancelWizard(boolean bypassCheck) {
|
||||
if (bypassCheck || getResources().getBoolean(R.bool.allow_cancel_remote_provisioning_login_activity)) {
|
||||
private void cancelWizard() {
|
||||
if (getResources().getBoolean(R.bool.allow_cancel_remote_provisioning_login_activity)) {
|
||||
LinphonePreferences.instance().disableProvisioningLoginView();
|
||||
setResult(bypassCheck ? Activity.RESULT_OK : Activity.RESULT_CANCELED);
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean storeAccount(String username, String password, String domain) {
|
||||
private void storeAccount(String username, String password, String domain) {
|
||||
XmlRpcHelper xmlRpcHelper = new XmlRpcHelper();
|
||||
xmlRpcHelper.getRemoteProvisioningFilenameAsync(new XmlRpcListenerBase() {
|
||||
@Override
|
||||
public void onRemoteProvisioningFilenameSent(String result) {
|
||||
LinphonePreferences.instance().setRemoteProvisioningUrl(result);
|
||||
LinphoneManager.getInstance().restartCore();
|
||||
}
|
||||
}, username.toString(), password.toString(), domain.toString());
|
||||
xmlRpcHelper.getRemoteProvisioningFilenameAsync(
|
||||
new XmlRpcListenerBase() {
|
||||
@Override
|
||||
public void onRemoteProvisioningFilenameSent(String result) {
|
||||
LinphonePreferences.instance().setRemoteProvisioningUrl(result);
|
||||
LinphoneManager.getInstance().restartCore();
|
||||
}
|
||||
},
|
||||
username,
|
||||
password,
|
||||
domain);
|
||||
|
||||
LinphonePreferences.instance().firstLaunchSuccessful();
|
||||
setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -117,15 +126,18 @@ public class RemoteProvisioningLoginActivity extends Activity implements OnClick
|
|||
int id = v.getId();
|
||||
|
||||
if (id == R.id.cancel) {
|
||||
cancelWizard(false);
|
||||
cancelWizard();
|
||||
}
|
||||
if (id == R.id.assistant_connect) {
|
||||
storeAccount(login.getText().toString(), password.getText().toString(), domain.getText().toString());
|
||||
storeAccount(
|
||||
mLogin.getText().toString(),
|
||||
mPassword.getText().toString(),
|
||||
mDomain.getText().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
cancelWizard(false);
|
||||
cancelWizard();
|
||||
}
|
||||
}
|
||||
|
|
@ -25,39 +25,38 @@ import android.view.View;
|
|||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.linphone.R;
|
||||
|
||||
public class WelcomeFragment extends Fragment implements OnClickListener {
|
||||
private Button createAccount, logLinphoneAccount, logGenericAccount, remoteProvisioning;
|
||||
private Button mCreateAccount, mLogLinphoneAccount, mLogGenericAccount, mRemoteProvisioning;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.assistant_welcome, container, false);
|
||||
|
||||
createAccount = view.findViewById(R.id.create_account);
|
||||
createAccount.setOnClickListener(this);
|
||||
mCreateAccount = view.findViewById(R.id.create_account);
|
||||
mCreateAccount.setOnClickListener(this);
|
||||
|
||||
logLinphoneAccount = view.findViewById(R.id.login_linphone);
|
||||
mLogLinphoneAccount = view.findViewById(R.id.login_linphone);
|
||||
if (getResources().getBoolean(R.bool.hide_linphone_accounts_in_assistant)) {
|
||||
logLinphoneAccount.setVisibility(View.GONE);
|
||||
mLogLinphoneAccount.setVisibility(View.GONE);
|
||||
} else {
|
||||
logLinphoneAccount.setOnClickListener(this);
|
||||
mLogLinphoneAccount.setOnClickListener(this);
|
||||
}
|
||||
|
||||
logGenericAccount = view.findViewById(R.id.login_generic);
|
||||
mLogGenericAccount = view.findViewById(R.id.login_generic);
|
||||
if (getResources().getBoolean(R.bool.hide_generic_accounts_in_assistant)) {
|
||||
logGenericAccount.setVisibility(View.GONE);
|
||||
mLogGenericAccount.setVisibility(View.GONE);
|
||||
} else {
|
||||
logGenericAccount.setOnClickListener(this);
|
||||
mLogGenericAccount.setOnClickListener(this);
|
||||
}
|
||||
|
||||
remoteProvisioning = view.findViewById(R.id.remote_provisioning);
|
||||
mRemoteProvisioning = view.findViewById(R.id.remote_provisioning);
|
||||
if (getResources().getBoolean(R.bool.hide_remote_provisioning_in_assistant)) {
|
||||
remoteProvisioning.setVisibility(View.GONE);
|
||||
mRemoteProvisioning.setVisibility(View.GONE);
|
||||
} else {
|
||||
remoteProvisioning.setOnClickListener(this);
|
||||
mRemoteProvisioning.setOnClickListener(this);
|
||||
}
|
||||
|
||||
return view;
|
||||
|
|
@ -73,7 +72,7 @@ public class WelcomeFragment extends Fragment implements OnClickListener {
|
|||
} else if (id == R.id.create_account) {
|
||||
AssistantActivity.instance().displayCreateAccount();
|
||||
} else if (id == R.id.remote_provisioning) {
|
||||
AssistantActivity.instance().displayRemoteProvisioning();
|
||||
AssistantActivity.instance().displayRemoteProvisioning("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.linphone;
|
||||
package org.linphone.call;
|
||||
|
||||
/*
|
||||
BandwithManager.java
|
||||
|
|
@ -20,23 +20,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
*/
|
||||
|
||||
import org.linphone.core.CallParams;
|
||||
import org.linphone.core.Core;
|
||||
|
||||
public class BandwidthManager {
|
||||
|
||||
public static final int HIGH_RESOLUTION = 0;
|
||||
public static final int LOW_RESOLUTION = 1;
|
||||
public static final int LOW_BANDWIDTH = 2;
|
||||
private static final int HIGH_RESOLUTION = 0;
|
||||
private static final int LOW_RESOLUTION = 1;
|
||||
private static final int LOW_BANDWIDTH = 2;
|
||||
|
||||
private static BandwidthManager instance;
|
||||
|
||||
private int currentProfile = HIGH_RESOLUTION;
|
||||
|
||||
public static final synchronized BandwidthManager getInstance() {
|
||||
if (instance == null) instance = new BandwidthManager();
|
||||
return instance;
|
||||
}
|
||||
private static BandwidthManager sInstance;
|
||||
|
||||
private final int currentProfile = HIGH_RESOLUTION;
|
||||
|
||||
private BandwidthManager() {
|
||||
// FIXME register a listener on NetworkManager to get notified of network state
|
||||
|
|
@ -45,8 +38,12 @@ public class BandwidthManager {
|
|||
// FIXME initially get those values
|
||||
}
|
||||
|
||||
public static synchronized BandwidthManager getInstance() {
|
||||
if (sInstance == null) sInstance = new BandwidthManager();
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void updateWithProfileSettings(Core lc, CallParams callParams) {
|
||||
public void updateWithProfileSettings(CallParams callParams) {
|
||||
if (callParams != null) { // in call
|
||||
// Update video parm if
|
||||
if (!isVideoPossible()) { // NO VIDEO
|
||||
|
|
@ -59,7 +56,7 @@ public class BandwidthManager {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isVideoPossible() {
|
||||
private boolean isVideoPossible() {
|
||||
return currentProfile != LOW_BANDWIDTH;
|
||||
}
|
||||
}
|
||||
2019
app/src/main/java/org/linphone/call/CallActivity.java
Normal file
2019
app/src/main/java/org/linphone/call/CallActivity.java
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -24,30 +24,23 @@ import android.os.Bundle;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.linphone.R;
|
||||
|
||||
public class CallAudioFragment extends Fragment {
|
||||
private CallActivity incallActvityInstance;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.audio, container, false);
|
||||
return view;
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.audio, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
incallActvityInstance = (CallActivity) getActivity();
|
||||
CallActivity incallActvityInstance = (CallActivity) getActivity();
|
||||
|
||||
if (incallActvityInstance != null) {
|
||||
incallActvityInstance.bindAudioFragment(this);
|
||||
}
|
||||
|
||||
// Just to be sure we have incall controls
|
||||
if (incallActvityInstance != null) {
|
||||
// Just to be sure we have incall controls
|
||||
incallActvityInstance.removeCallbacks();
|
||||
}
|
||||
}
|
||||
324
app/src/main/java/org/linphone/call/CallIncomingActivity.java
Normal file
324
app/src/main/java/org/linphone/call/CallIncomingActivity.java
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
package org.linphone.call;
|
||||
|
||||
/*
|
||||
CallIncomingActivity.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import java.util.ArrayList;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.compatibility.Compatibility;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.Call.State;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneGenericActivity;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.CallIncomingAnswerButton;
|
||||
import org.linphone.views.CallIncomingButtonListener;
|
||||
import org.linphone.views.CallIncomingDeclineButton;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class CallIncomingActivity extends LinphoneGenericActivity {
|
||||
private static CallIncomingActivity sInstance;
|
||||
|
||||
private TextView mName, mNumber;
|
||||
private ImageView mAcceptIcon;
|
||||
private CallIncomingAnswerButton mAccept;
|
||||
private CallIncomingDeclineButton mDecline;
|
||||
private Call mCall;
|
||||
private CoreListenerStub mListener;
|
||||
private boolean mAlreadyAcceptedOrDeniedCall;
|
||||
private KeyguardManager mKeyguardManager;
|
||||
private TextureView mVideoDisplay;
|
||||
|
||||
public static CallIncomingActivity instance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public static boolean isInstanciated() {
|
||||
return sInstance != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getResources().getBoolean(R.bool.orientation_portrait_only)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
Compatibility.setShowWhenLocked(this, true);
|
||||
Compatibility.setTurnScreenOn(this, true);
|
||||
|
||||
setContentView(R.layout.call_incoming);
|
||||
|
||||
mName = findViewById(R.id.contact_name);
|
||||
mNumber = findViewById(R.id.contact_number);
|
||||
mVideoDisplay = findViewById(R.id.videoSurface);
|
||||
|
||||
mAccept = findViewById(R.id.answer_button);
|
||||
mDecline = findViewById(R.id.decline_button);
|
||||
mAcceptIcon = findViewById(R.id.acceptIcon);
|
||||
lookupCurrentCall();
|
||||
|
||||
if (LinphonePreferences.instance() != null
|
||||
&& mCall != null
|
||||
&& mCall.getRemoteParams() != null
|
||||
&& LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()
|
||||
&& mCall.getRemoteParams().videoEnabled()) {
|
||||
mAcceptIcon.setImageResource(R.drawable.call_video_start);
|
||||
}
|
||||
|
||||
mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
|
||||
boolean doNotUseSliders =
|
||||
getResources()
|
||||
.getBoolean(
|
||||
R.bool.do_not_use_sliders_to_answer_hangup_call_if_phone_unlocked);
|
||||
if (doNotUseSliders && !mKeyguardManager.inKeyguardRestrictedInputMode()) {
|
||||
mAccept.setSliderMode(false);
|
||||
mDecline.setSliderMode(false);
|
||||
} else {
|
||||
mAccept.setSliderMode(true);
|
||||
mDecline.setSliderMode(true);
|
||||
mAccept.setDeclineButton(mDecline);
|
||||
mDecline.setAnswerButton(mAccept);
|
||||
}
|
||||
mAccept.setListener(
|
||||
new CallIncomingButtonListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
answer();
|
||||
}
|
||||
});
|
||||
mDecline.setListener(
|
||||
new CallIncomingButtonListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
decline();
|
||||
}
|
||||
});
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onCallStateChanged(
|
||||
Core lc, Call call, State state, String message) {
|
||||
if (call == mCall && State.End == state) {
|
||||
finish();
|
||||
} else if (state == State.Connected) {
|
||||
startActivity(
|
||||
new Intent(CallIncomingActivity.this, CallActivity.class));
|
||||
} else if (state == State.StreamsRunning) {
|
||||
Log.e(
|
||||
"CallIncommingActivity - onCreate - State.StreamsRunning - speaker = "
|
||||
+ LinphoneManager.getInstance().isSpeakerEnabled());
|
||||
// The following should not be needed except some devices need it (e.g.
|
||||
// Galaxy S).
|
||||
LinphoneManager.getInstance()
|
||||
.enableSpeaker(
|
||||
LinphoneManager.getInstance().isSpeakerEnabled());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
sInstance = this;
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
|
||||
mAlreadyAcceptedOrDeniedCall = false;
|
||||
mCall = null;
|
||||
|
||||
// Only one call ringing at a time is allowed
|
||||
lookupCurrentCall();
|
||||
if (mCall == null) {
|
||||
// The incoming call no longer exists.
|
||||
Log.d("Couldn't find incoming call");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Address address = mCall.getRemoteAddress();
|
||||
LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(address);
|
||||
if (contact != null) {
|
||||
ContactAvatar.displayAvatar(contact, findViewById(R.id.avatar_layout), true);
|
||||
mName.setText(contact.getFullName());
|
||||
} else {
|
||||
String displayName = LinphoneUtils.getAddressDisplayName(address);
|
||||
ContactAvatar.displayAvatar(displayName, findViewById(R.id.avatar_layout), true);
|
||||
mName.setText(displayName);
|
||||
}
|
||||
mNumber.setText(address.asStringUriOnly());
|
||||
|
||||
if (LinphonePreferences.instance().acceptIncomingEarlyMedia()) {
|
||||
if (mCall.getCurrentParams().videoEnabled()) {
|
||||
findViewById(R.id.avatar_layout).setVisibility(View.GONE);
|
||||
mCall.getCore().setNativeVideoWindowId(mVideoDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
checkAndRequestCallPermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
sInstance = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (LinphoneManager.isInstanciated()
|
||||
&& (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME)) {
|
||||
LinphoneManager.getLc().terminateCall(mCall);
|
||||
finish();
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private void lookupCurrentCall() {
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) {
|
||||
for (Call call : LinphoneManager.getLc().getCalls()) {
|
||||
if (State.IncomingReceived == call.getState()
|
||||
|| State.IncomingEarlyMedia == call.getState()) {
|
||||
mCall = call;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void decline() {
|
||||
if (mAlreadyAcceptedOrDeniedCall) {
|
||||
return;
|
||||
}
|
||||
mAlreadyAcceptedOrDeniedCall = true;
|
||||
|
||||
LinphoneManager.getLc().terminateCall(mCall);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void answer() {
|
||||
if (mAlreadyAcceptedOrDeniedCall) {
|
||||
return;
|
||||
}
|
||||
mAlreadyAcceptedOrDeniedCall = true;
|
||||
|
||||
if (!LinphoneManager.getInstance().acceptCall(mCall)) {
|
||||
// the above method takes care of Samsung Galaxy S
|
||||
Toast.makeText(this, R.string.couldnt_accept_call, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
if (!LinphoneActivity.isInstanciated()) {
|
||||
return;
|
||||
}
|
||||
LinphoneManager.getInstance().routeAudioToReceiver();
|
||||
LinphoneActivity.instance().startIncallActivity();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndRequestCallPermissions() {
|
||||
ArrayList<String> permissionsList = new ArrayList<>();
|
||||
|
||||
int recordAudio =
|
||||
getPackageManager()
|
||||
.checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName());
|
||||
Log.i(
|
||||
"[Permission] Record audio permission is "
|
||||
+ (recordAudio == PackageManager.PERMISSION_GRANTED
|
||||
? "granted"
|
||||
: "denied"));
|
||||
int camera =
|
||||
getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName());
|
||||
Log.i(
|
||||
"[Permission] Camera permission is "
|
||||
+ (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
|
||||
|
||||
if (recordAudio != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.i("[Permission] Asking for record audio");
|
||||
permissionsList.add(Manifest.permission.RECORD_AUDIO);
|
||||
}
|
||||
if (LinphonePreferences.instance().shouldInitiateVideoCall()
|
||||
|| LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) {
|
||||
if (camera != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.i("[Permission] Asking for camera");
|
||||
permissionsList.add(Manifest.permission.CAMERA);
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionsList.size() > 0) {
|
||||
String[] permissions = new String[permissionsList.size()];
|
||||
permissions = permissionsList.toArray(permissions);
|
||||
ActivityCompat.requestPermissions(this, permissions, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, String[] permissions, int[] grantResults) {
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
Log.i(
|
||||
"[Permission] "
|
||||
+ permissions[i]
|
||||
+ " is "
|
||||
+ (grantResults[i] == PackageManager.PERMISSION_GRANTED
|
||||
? "granted"
|
||||
: "denied"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,39 +19,51 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import org.linphone.BandwidthManager;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.CallParams;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreException;
|
||||
import org.linphone.mediastream.Log;
|
||||
import org.linphone.core.MediaEncryption;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.utils.FileUtils;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
/**
|
||||
* Handle call updating, reinvites.
|
||||
*/
|
||||
/** Handle call updating, reinvites. */
|
||||
public class CallManager {
|
||||
|
||||
private static CallManager instance;
|
||||
private static CallManager sInstance;
|
||||
|
||||
private CallManager() {
|
||||
public static synchronized CallManager getInstance() {
|
||||
if (sInstance == null) sInstance = new CallManager();
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public static final synchronized CallManager getInstance() {
|
||||
if (instance == null) instance = new CallManager();
|
||||
return instance;
|
||||
}
|
||||
private CallManager() {}
|
||||
|
||||
private BandwidthManager bm() {
|
||||
private BandwidthManager getBandwidthManager() {
|
||||
return BandwidthManager.getInstance();
|
||||
}
|
||||
|
||||
public void inviteAddress(Address lAddress, boolean videoEnabled, boolean lowBandwidth) throws CoreException {
|
||||
public void inviteAddress(Address lAddress, boolean forceZRTP) {
|
||||
boolean isLowBandwidthConnection =
|
||||
!LinphoneUtils.isHighBandwidthConnection(
|
||||
LinphoneService.instance().getApplicationContext());
|
||||
|
||||
inviteAddress(lAddress, false, isLowBandwidthConnection, forceZRTP);
|
||||
}
|
||||
|
||||
public void inviteAddress(Address lAddress) {
|
||||
inviteAddress(lAddress, false);
|
||||
}
|
||||
|
||||
public void inviteAddress(
|
||||
Address lAddress, boolean videoEnabled, boolean lowBandwidth, boolean forceZRTP) {
|
||||
Core lc = LinphoneManager.getLc();
|
||||
|
||||
CallParams params = lc.createCallParams(null);
|
||||
bm().updateWithProfileSettings(lc, params);
|
||||
getBandwidthManager().updateWithProfileSettings(params);
|
||||
|
||||
if (videoEnabled && params.videoEnabled()) {
|
||||
params.enableVideo(true);
|
||||
|
|
@ -64,13 +76,25 @@ public class CallManager {
|
|||
Log.d("Low bandwidth enabled in call params");
|
||||
}
|
||||
|
||||
if (forceZRTP) {
|
||||
params.setMediaEncryption(MediaEncryption.ZRTP);
|
||||
}
|
||||
|
||||
String recordFile =
|
||||
FileUtils.getCallRecordingFilename(
|
||||
LinphoneManager.getInstance().getContext(), lAddress);
|
||||
params.setRecordFile(recordFile);
|
||||
|
||||
lc.inviteAddressWithParams(lAddress, params);
|
||||
}
|
||||
|
||||
public void inviteAddress(Address lAddress, boolean videoEnabled, boolean lowBandwidth) {
|
||||
inviteAddress(lAddress, videoEnabled, lowBandwidth, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add video to a currently running voice only call.
|
||||
* No re-invite is sent if the current call is already video
|
||||
* or if the bandwidth settings are too low.
|
||||
* Add video to a currently running voice only call. No re-invite is sent if the current call is
|
||||
* already video or if the bandwidth settings are too low.
|
||||
*
|
||||
* @return if updateCall called
|
||||
*/
|
||||
|
|
@ -85,9 +109,8 @@ public class CallManager {
|
|||
|
||||
if (params.videoEnabled()) return false;
|
||||
|
||||
|
||||
// Check if video possible regarding bandwidth limitations
|
||||
bm().updateWithProfileSettings(lc, params);
|
||||
getBandwidthManager().updateWithProfileSettings(params);
|
||||
|
||||
// Abort if not enough bandwidth...
|
||||
if (!params.videoEnabled()) {
|
||||
|
|
@ -101,8 +124,8 @@ public class CallManager {
|
|||
|
||||
/**
|
||||
* Change the preferred video size used by linphone core. (impact landscape/portrait buffer).
|
||||
* Update current call, without reinvite.
|
||||
* The camera will be restarted when mediastreamer chain is recreated and setParameters is called.
|
||||
* Update current call, without reinvite. The camera will be restarted when mediastreamer chain
|
||||
* is recreated and setParameters is called.
|
||||
*/
|
||||
public void updateCall() {
|
||||
Core lc = LinphoneManager.getLc();
|
||||
|
|
@ -112,8 +135,7 @@ public class CallManager {
|
|||
return;
|
||||
}
|
||||
CallParams params = lc.createCallParams(lCall);
|
||||
bm().updateWithProfileSettings(lc, params);
|
||||
getBandwidthManager().updateWithProfileSettings(params);
|
||||
lc.updateCall(lCall, null);
|
||||
}
|
||||
|
||||
}
|
||||
303
app/src/main/java/org/linphone/call/CallOutgoingActivity.java
Normal file
303
app/src/main/java/org/linphone/call/CallOutgoingActivity.java
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
package org.linphone.call;
|
||||
|
||||
/*
|
||||
CallOutgoingActivity.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import java.util.ArrayList;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.Call.State;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.Reason;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneGenericActivity;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class CallOutgoingActivity extends LinphoneGenericActivity implements OnClickListener {
|
||||
private TextView mName, mNumber;
|
||||
private ImageView mMicro, mSpeaker, mHangUp;
|
||||
private Call mCall;
|
||||
private CoreListenerStub mListener;
|
||||
private boolean mIsMicMuted, mIsSpeakerEnabled;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getResources().getBoolean(R.bool.orientation_portrait_only)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
setContentView(R.layout.call_outgoing);
|
||||
|
||||
mName = findViewById(R.id.contact_name);
|
||||
mNumber = findViewById(R.id.contact_number);
|
||||
|
||||
mIsMicMuted = false;
|
||||
mIsSpeakerEnabled = false;
|
||||
|
||||
mMicro = findViewById(R.id.micro);
|
||||
mMicro.setOnClickListener(this);
|
||||
mSpeaker = findViewById(R.id.speaker);
|
||||
mSpeaker.setOnClickListener(this);
|
||||
|
||||
mHangUp = findViewById(R.id.outgoing_hang_up);
|
||||
mHangUp.setOnClickListener(this);
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onCallStateChanged(
|
||||
Core lc, Call call, Call.State state, String message) {
|
||||
if (call == mCall && State.Connected == state) {
|
||||
if (!LinphoneActivity.isInstanciated()) {
|
||||
return;
|
||||
}
|
||||
LinphoneActivity.instance().startIncallActivity();
|
||||
return;
|
||||
} else if (state == State.Error) {
|
||||
// Convert Core message for internalization
|
||||
if (call.getErrorInfo().getReason() == Reason.Declined) {
|
||||
displayCustomToast(
|
||||
getString(R.string.error_call_declined),
|
||||
Toast.LENGTH_SHORT);
|
||||
decline();
|
||||
} else if (call.getErrorInfo().getReason() == Reason.NotFound) {
|
||||
displayCustomToast(
|
||||
getString(R.string.error_user_not_found),
|
||||
Toast.LENGTH_SHORT);
|
||||
decline();
|
||||
} else if (call.getErrorInfo().getReason() == Reason.NotAcceptable) {
|
||||
displayCustomToast(
|
||||
getString(R.string.error_incompatible_media),
|
||||
Toast.LENGTH_SHORT);
|
||||
decline();
|
||||
} else if (call.getErrorInfo().getReason() == Reason.Busy) {
|
||||
displayCustomToast(
|
||||
getString(R.string.error_user_busy), Toast.LENGTH_SHORT);
|
||||
decline();
|
||||
} else if (message != null) {
|
||||
displayCustomToast(
|
||||
getString(R.string.error_unknown) + " - " + message,
|
||||
Toast.LENGTH_SHORT);
|
||||
decline();
|
||||
}
|
||||
} else if (state == State.End) {
|
||||
// Convert Core message for internalization
|
||||
if (call.getErrorInfo().getReason() == Reason.Declined) {
|
||||
displayCustomToast(
|
||||
getString(R.string.error_call_declined),
|
||||
Toast.LENGTH_SHORT);
|
||||
decline();
|
||||
}
|
||||
}
|
||||
|
||||
if (LinphoneManager.getLc().getCallsNb() == 0) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
|
||||
mCall = null;
|
||||
|
||||
// Only one call ringing at a time is allowed
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) {
|
||||
for (Call call : LinphoneManager.getLc().getCalls()) {
|
||||
State cstate = call.getState();
|
||||
if (State.OutgoingInit == cstate
|
||||
|| State.OutgoingProgress == cstate
|
||||
|| State.OutgoingRinging == cstate
|
||||
|| State.OutgoingEarlyMedia == cstate) {
|
||||
mCall = call;
|
||||
break;
|
||||
}
|
||||
if (State.StreamsRunning == cstate) {
|
||||
if (!LinphoneActivity.isInstanciated()) {
|
||||
return;
|
||||
}
|
||||
LinphoneActivity.instance().startIncallActivity();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mCall == null) {
|
||||
Log.e("Couldn't find outgoing call");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Address address = mCall.getRemoteAddress();
|
||||
LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(address);
|
||||
if (contact != null) {
|
||||
ContactAvatar.displayAvatar(contact, findViewById(R.id.avatar_layout), true);
|
||||
mName.setText(contact.getFullName());
|
||||
} else {
|
||||
String displayName = LinphoneUtils.getAddressDisplayName(address);
|
||||
ContactAvatar.displayAvatar(displayName, findViewById(R.id.avatar_layout), true);
|
||||
mName.setText(displayName);
|
||||
}
|
||||
mNumber.setText(LinphoneUtils.getDisplayableAddress(address));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
checkAndRequestCallPermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
|
||||
if (id == R.id.micro) {
|
||||
mIsMicMuted = !mIsMicMuted;
|
||||
mMicro.setSelected(mIsMicMuted);
|
||||
LinphoneManager.getLc().enableMic(!mIsMicMuted);
|
||||
}
|
||||
if (id == R.id.speaker) {
|
||||
mIsSpeakerEnabled = !mIsSpeakerEnabled;
|
||||
mSpeaker.setSelected(mIsSpeakerEnabled);
|
||||
LinphoneManager.getInstance().enableSpeaker(mIsSpeakerEnabled);
|
||||
}
|
||||
if (id == R.id.outgoing_hang_up) {
|
||||
decline();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (LinphoneManager.isInstanciated()
|
||||
&& (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME)) {
|
||||
LinphoneManager.getLc().terminateCall(mCall);
|
||||
finish();
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private void displayCustomToast(final String message, final int duration) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View layout = inflater.inflate(R.layout.toast, (ViewGroup) findViewById(R.id.toastRoot));
|
||||
|
||||
TextView toastText = layout.findViewById(R.id.toastMessage);
|
||||
toastText.setText(message);
|
||||
|
||||
final Toast toast = new Toast(getApplicationContext());
|
||||
toast.setGravity(Gravity.CENTER, 0, 0);
|
||||
toast.setDuration(duration);
|
||||
toast.setView(layout);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
private void decline() {
|
||||
LinphoneManager.getLc().terminateCall(mCall);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void checkAndRequestCallPermissions() {
|
||||
ArrayList<String> permissionsList = new ArrayList<>();
|
||||
|
||||
int recordAudio =
|
||||
getPackageManager()
|
||||
.checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName());
|
||||
Log.i(
|
||||
"[Permission] Record audio permission is "
|
||||
+ (recordAudio == PackageManager.PERMISSION_GRANTED
|
||||
? "granted"
|
||||
: "denied"));
|
||||
int camera =
|
||||
getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName());
|
||||
Log.i(
|
||||
"[Permission] Camera permission is "
|
||||
+ (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
|
||||
|
||||
if (recordAudio != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.i("[Permission] Asking for record audio");
|
||||
permissionsList.add(Manifest.permission.RECORD_AUDIO);
|
||||
}
|
||||
if (LinphonePreferences.instance().shouldInitiateVideoCall()
|
||||
|| LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) {
|
||||
if (camera != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.i("[Permission] Asking for camera");
|
||||
permissionsList.add(Manifest.permission.CAMERA);
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionsList.size() > 0) {
|
||||
String[] permissions = new String[permissionsList.size()];
|
||||
permissions = permissionsList.toArray(permissions);
|
||||
ActivityCompat.requestPermissions(this, permissions, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, String[] permissions, int[] grantResults) {
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
Log.i(
|
||||
"[Permission] "
|
||||
+ permissions[i]
|
||||
+ " is "
|
||||
+ (grantResults[i] == PackageManager.PERMISSION_GRANTED
|
||||
? "granted"
|
||||
: "denied"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,41 +27,39 @@ import android.view.GestureDetector.OnDoubleTapListener;
|
|||
import android.view.GestureDetector.OnGestureListener;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphonePreferences;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.LinphoneUtils;
|
||||
import org.linphone.R;
|
||||
import org.linphone.compatibility.Compatibility;
|
||||
import org.linphone.compatibility.CompatibilityScaleGestureDetector;
|
||||
import org.linphone.compatibility.CompatibilityScaleGestureListener;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.VideoDefinition;
|
||||
import org.linphone.mediastream.Log;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
public class CallVideoFragment extends Fragment implements OnGestureListener, OnDoubleTapListener, CompatibilityScaleGestureListener {
|
||||
public class CallVideoFragment extends Fragment
|
||||
implements OnGestureListener, OnDoubleTapListener, CompatibilityScaleGestureListener {
|
||||
private TextureView mVideoView;
|
||||
private TextureView mCaptureView;
|
||||
private GestureDetector mGestureDetector;
|
||||
private float mZoomFactor = 1.f;
|
||||
private float mZoomCenterX, mZoomCenterY;
|
||||
private CompatibilityScaleGestureDetector mScaleDetector;
|
||||
private CallActivity inCallActivity;
|
||||
private int previewX, previewY;
|
||||
private CallActivity mInCallActivity;
|
||||
private int mPreviewX, mPreviewY;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
// Warning useless because value is ignored and automatically set by new APIs.
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view;
|
||||
if (LinphoneManager.getLc().hasCrappyOpengl()) {
|
||||
view = inflater.inflate(R.layout.video_no_opengl, container, false);
|
||||
|
|
@ -71,57 +69,64 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
|
||||
mVideoView = view.findViewById(R.id.videoSurface);
|
||||
mCaptureView = view.findViewById(R.id.videoCaptureSurface);
|
||||
|
||||
|
||||
LinphoneManager.getLc().setNativeVideoWindowId(mVideoView);
|
||||
LinphoneManager.getLc().setNativePreviewWindowId(mCaptureView);
|
||||
|
||||
mVideoView.setOnTouchListener(new OnTouchListener() {
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (mScaleDetector != null) {
|
||||
mScaleDetector.onTouchEvent(event);
|
||||
}
|
||||
mVideoView.setOnTouchListener(
|
||||
new OnTouchListener() {
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (mScaleDetector != null) {
|
||||
mScaleDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
if (inCallActivity != null) {
|
||||
inCallActivity.displayVideoCallControlsIfHidden();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
if (mInCallActivity != null) {
|
||||
mInCallActivity.displayVideoCallControlsIfHidden();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mCaptureView.setOnTouchListener(new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
switch (motionEvent.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
previewX = (int) motionEvent.getX();
|
||||
previewY = (int) motionEvent.getY();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int x = (int) motionEvent.getX();
|
||||
int y = (int) motionEvent.getY();
|
||||
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mCaptureView.getLayoutParams();
|
||||
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0); // Clears the rule, as there is no removeRule until API 17.
|
||||
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
|
||||
int left = lp.leftMargin + (x - previewX);
|
||||
int top = lp.topMargin + (y - previewY);
|
||||
lp.leftMargin = left;
|
||||
lp.topMargin = top;
|
||||
view.setLayoutParams(lp);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mCaptureView.setOnTouchListener(
|
||||
new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
switch (motionEvent.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mPreviewX = (int) motionEvent.getX();
|
||||
mPreviewY = (int) motionEvent.getY();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int x = (int) motionEvent.getX();
|
||||
int y = (int) motionEvent.getY();
|
||||
RelativeLayout.LayoutParams lp =
|
||||
(RelativeLayout.LayoutParams)
|
||||
mCaptureView.getLayoutParams();
|
||||
lp.addRule(
|
||||
RelativeLayout.ALIGN_PARENT_BOTTOM,
|
||||
0); // Clears the rule, as there is no removeRule until API
|
||||
// 17.
|
||||
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
|
||||
int left = lp.leftMargin + (x - mPreviewX);
|
||||
int top = lp.topMargin + (y - mPreviewY);
|
||||
lp.leftMargin = left;
|
||||
lp.topMargin = top;
|
||||
view.setLayoutParams(lp);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
inCallActivity = (CallActivity) getActivity();
|
||||
if (inCallActivity != null) {
|
||||
inCallActivity.bindVideoFragment(this);
|
||||
mInCallActivity = (CallActivity) getActivity();
|
||||
if (mInCallActivity != null) {
|
||||
mInCallActivity.bindVideoFragment(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,30 +142,36 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
int screenHeight = metrics.heightPixels;
|
||||
int maxHeight = screenHeight / 4; // Let's take at most 1/4 of the screen for the camera preview
|
||||
int maxHeight =
|
||||
screenHeight / 4; // Let's take at most 1/4 of the screen for the camera preview
|
||||
|
||||
VideoDefinition videoSize = call.getCurrentParams().getSentVideoDefinition(); // It already takes care of rotation
|
||||
VideoDefinition videoSize =
|
||||
call.getCurrentParams()
|
||||
.getSentVideoDefinition(); // It already takes care of rotation
|
||||
if (videoSize.getWidth() == 0 || videoSize.getHeight() == 0) {
|
||||
Log.w("Couldn't get sent video definition, using default video definition");
|
||||
Log.w(
|
||||
"[Video Fragment] Couldn't get sent video definition, using default video definition");
|
||||
videoSize = lc.getPreferredVideoDefinition();
|
||||
}
|
||||
int width = videoSize.getWidth();
|
||||
int height = videoSize.getHeight();
|
||||
|
||||
Log.d("Video height is " + height + ", width is " + width);
|
||||
Log.d("[Video Fragment] Video height is " + height + ", width is " + width);
|
||||
width = width * maxHeight / height;
|
||||
height = maxHeight;
|
||||
|
||||
if (mCaptureView == null) {
|
||||
Log.e("mCaptureView is null !");
|
||||
Log.e("[Video Fragment] mCaptureView is null !");
|
||||
return;
|
||||
}
|
||||
|
||||
RelativeLayout.LayoutParams newLp = new RelativeLayout.LayoutParams(width, height);
|
||||
newLp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 1); // Clears the rule, as there is no removeRule until API 17.
|
||||
newLp.addRule(
|
||||
RelativeLayout.ALIGN_PARENT_BOTTOM,
|
||||
1); // Clears the rule, as there is no removeRule until API 17.
|
||||
newLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 1);
|
||||
mCaptureView.setLayoutParams(newLp);
|
||||
Log.d("Video preview size set to " + width + "x" + height);
|
||||
Log.d("[Video Fragment] Video preview size set to " + width + "x" + height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,17 +188,14 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
}
|
||||
|
||||
String newDevice;
|
||||
if (index == 1)
|
||||
newDevice = devices[0];
|
||||
else if (devices.length > 1)
|
||||
newDevice = devices[1];
|
||||
else
|
||||
newDevice = devices[index];
|
||||
if (index == 1) newDevice = devices[0];
|
||||
else if (devices.length > 1) newDevice = devices[1];
|
||||
else newDevice = devices[index];
|
||||
LinphoneManager.getLc().setVideoDevice(newDevice);
|
||||
|
||||
CallManager.getInstance().updateCall();
|
||||
} catch (ArithmeticException ae) {
|
||||
Log.e("Cannot swtich camera : no camera");
|
||||
Log.e("[Video Fragment] Cannot swtich camera : no camera");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,16 +207,24 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
LinphoneService.instance().destroyOverlay();
|
||||
}
|
||||
|
||||
mGestureDetector = new GestureDetector(inCallActivity, this);
|
||||
mScaleDetector = Compatibility.getScaleGestureDetector(inCallActivity, this);
|
||||
mGestureDetector = new GestureDetector(mInCallActivity, this);
|
||||
mScaleDetector = new CompatibilityScaleGestureDetector(mInCallActivity);
|
||||
mScaleDetector.setOnScaleListener(this);
|
||||
|
||||
resizePreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (LinphonePreferences.instance().isOverlayEnabled()) {
|
||||
LinphoneService.instance().createOverlay();
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (LinphonePreferences.instance().isOverlayEnabled()
|
||||
&& lc != null
|
||||
&& lc.getCurrentCall() != null) {
|
||||
Call call = lc.getCurrentCall();
|
||||
if (call.getState() == Call.State.StreamsRunning) {
|
||||
// Prevent overlay creation if video call is paused by remote
|
||||
LinphoneService.instance().createOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
|
|
@ -218,10 +234,15 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
mZoomFactor *= detector.getScaleFactor();
|
||||
// Don't let the object get too small or too large.
|
||||
// Zoom to make the video fill the screen vertically
|
||||
float portraitZoomFactor = ((float) mVideoView.getHeight()) / (float) ((3 * mVideoView.getWidth()) / 4);
|
||||
float portraitZoomFactor =
|
||||
((float) mVideoView.getHeight()) / (float) ((3 * mVideoView.getWidth()) / 4);
|
||||
// Zoom to make the video fill the screen horizontally
|
||||
float landscapeZoomFactor = ((float) mVideoView.getWidth()) / (float) ((3 * mVideoView.getHeight()) / 4);
|
||||
mZoomFactor = Math.max(0.1f, Math.min(mZoomFactor, Math.max(portraitZoomFactor, landscapeZoomFactor)));
|
||||
float landscapeZoomFactor =
|
||||
((float) mVideoView.getWidth()) / (float) ((3 * mVideoView.getHeight()) / 4);
|
||||
mZoomFactor =
|
||||
Math.max(
|
||||
0.1f,
|
||||
Math.min(mZoomFactor, Math.max(portraitZoomFactor, landscapeZoomFactor)));
|
||||
|
||||
Call currentCall = LinphoneManager.getLc().getCurrentCall();
|
||||
if (currentCall != null) {
|
||||
|
|
@ -247,16 +268,14 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
mZoomCenterY -= 0.01;
|
||||
}
|
||||
|
||||
if (mZoomCenterX > 1)
|
||||
mZoomCenterX = 1;
|
||||
if (mZoomCenterX < 0)
|
||||
mZoomCenterX = 0;
|
||||
if (mZoomCenterY > 1)
|
||||
mZoomCenterY = 1;
|
||||
if (mZoomCenterY < 0)
|
||||
mZoomCenterY = 0;
|
||||
if (mZoomCenterX > 1) mZoomCenterX = 1;
|
||||
if (mZoomCenterX < 0) mZoomCenterX = 0;
|
||||
if (mZoomCenterY > 1) mZoomCenterY = 1;
|
||||
if (mZoomCenterY < 0) mZoomCenterY = 0;
|
||||
|
||||
LinphoneManager.getLc().getCurrentCall().zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
|
||||
LinphoneManager.getLc()
|
||||
.getCurrentCall()
|
||||
.zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -269,9 +288,13 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
if (LinphoneUtils.isCallEstablished(LinphoneManager.getLc().getCurrentCall())) {
|
||||
if (mZoomFactor == 1.f) {
|
||||
// Zoom to make the video fill the screen vertically
|
||||
float portraitZoomFactor = ((float) mVideoView.getHeight()) / (float) ((3 * mVideoView.getWidth()) / 4);
|
||||
float portraitZoomFactor =
|
||||
((float) mVideoView.getHeight())
|
||||
/ (float) ((3 * mVideoView.getWidth()) / 4);
|
||||
// Zoom to make the video fill the screen horizontally
|
||||
float landscapeZoomFactor = ((float) mVideoView.getWidth()) / (float) ((3 * mVideoView.getHeight()) / 4);
|
||||
float landscapeZoomFactor =
|
||||
((float) mVideoView.getWidth())
|
||||
/ (float) ((3 * mVideoView.getHeight()) / 4);
|
||||
|
||||
mZoomFactor = Math.max(portraitZoomFactor, landscapeZoomFactor);
|
||||
} else {
|
||||
|
|
@ -292,7 +315,7 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
inCallActivity = null;
|
||||
mInCallActivity = null;
|
||||
|
||||
mCaptureView = null;
|
||||
if (mVideoView != null) {
|
||||
|
|
@ -327,20 +350,15 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
|
||||
float velocityY) {
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
public void onLongPress(MotionEvent e) {}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
public void onShowPress(MotionEvent e) {}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
ChatMessageViewHolder.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.linphone.R;
|
||||
|
||||
public class ChatMessageOldViewHolder extends RecyclerView.ViewHolder
|
||||
implements View.OnClickListener {
|
||||
public final LinearLayout eventLayout;
|
||||
public final TextView eventMessage;
|
||||
|
||||
public final RelativeLayout bubbleLayout;
|
||||
public final LinearLayout separatorLayout;
|
||||
public final LinearLayout background;
|
||||
public final RelativeLayout avatarLayout;
|
||||
public final TextView contactName;
|
||||
|
||||
public final ImageView messageStatus;
|
||||
public final ProgressBar messageSendingInProgress;
|
||||
public final LinearLayout imdmLayout;
|
||||
public final ImageView imdmIcon;
|
||||
public final TextView imdmLabel;
|
||||
|
||||
public final TextView messageText;
|
||||
public final ImageView messageImage;
|
||||
|
||||
public final RelativeLayout fileTransferLayout;
|
||||
public final ProgressBar fileTransferProgressBar;
|
||||
public final Button fileTransferAction;
|
||||
|
||||
public final TextView fileName;
|
||||
public final Button openFileButton;
|
||||
|
||||
public final CheckBox delete;
|
||||
|
||||
private ChatMessageViewHolderClickListener mListener;
|
||||
|
||||
public ChatMessageOldViewHolder(View view, ChatMessageViewHolderClickListener listener) {
|
||||
this(view);
|
||||
mListener = listener;
|
||||
view.setOnClickListener(this);
|
||||
}
|
||||
|
||||
public ChatMessageOldViewHolder(View view) {
|
||||
super(view);
|
||||
eventLayout = view.findViewById(R.id.event);
|
||||
// eventTime = view.findViewById(R.id.event_date);
|
||||
eventMessage = view.findViewById(R.id.event_text);
|
||||
|
||||
bubbleLayout = view.findViewById(R.id.bubble);
|
||||
background = view.findViewById(R.id.background);
|
||||
avatarLayout = view.findViewById(R.id.avatar_layout);
|
||||
contactName = view.findViewById(R.id.contact_header);
|
||||
|
||||
messageStatus = view.findViewById(R.id.status);
|
||||
messageSendingInProgress = view.findViewById(R.id.inprogress);
|
||||
imdmLayout = view.findViewById(R.id.imdmLayout);
|
||||
imdmIcon = view.findViewById(R.id.imdmIcon);
|
||||
imdmLabel = view.findViewById(R.id.imdmText);
|
||||
|
||||
messageText = view.findViewById(R.id.message);
|
||||
messageImage = view.findViewById(R.id.image);
|
||||
separatorLayout = view.findViewById(R.id.separator);
|
||||
|
||||
fileTransferLayout = view.findViewById(R.id.file_transfer_layout);
|
||||
fileTransferProgressBar = view.findViewById(R.id.progress_bar);
|
||||
fileTransferAction = view.findViewById(R.id.file_transfer_action);
|
||||
|
||||
fileName = view.findViewById(R.id.file_name);
|
||||
openFileButton = view.findViewById(R.id.open_file);
|
||||
|
||||
delete = view.findViewById(R.id.delete_message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mListener != null) {
|
||||
mListener.onItemClicked(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
377
app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java
Normal file
377
app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
ChatMessageViewHolder.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.flexbox.FlexboxLayout;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.Content;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.utils.FileUtils;
|
||||
import org.linphone.utils.ImageUtils;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
public final LinearLayout eventLayout;
|
||||
public final TextView eventMessage;
|
||||
|
||||
public final LinearLayout securityEventLayout;
|
||||
public final TextView securityEventMessage;
|
||||
|
||||
public final View rightAnchor;
|
||||
public final RelativeLayout bubbleLayout;
|
||||
public final LinearLayout background;
|
||||
public final RelativeLayout avatarLayout;
|
||||
|
||||
public final ProgressBar downloadInProgress, sendInProgress;
|
||||
public final TextView timeText;
|
||||
public final ImageView outgoingImdn;
|
||||
public final TextView messageText;
|
||||
|
||||
public final FlexboxLayout multiFileContents;
|
||||
public final RelativeLayout singleFileContent;
|
||||
|
||||
public final CheckBox delete;
|
||||
|
||||
private Context mContext;
|
||||
private ChatMessageViewHolderClickListener mListener;
|
||||
|
||||
public ChatMessageViewHolder(
|
||||
Context context, View view, ChatMessageViewHolderClickListener listener) {
|
||||
this(view);
|
||||
mContext = context;
|
||||
mListener = listener;
|
||||
view.setOnClickListener(this);
|
||||
}
|
||||
|
||||
public ChatMessageViewHolder(View view) {
|
||||
super(view);
|
||||
eventLayout = view.findViewById(R.id.event);
|
||||
eventMessage = view.findViewById(R.id.event_text);
|
||||
|
||||
securityEventLayout = view.findViewById(R.id.security_event);
|
||||
securityEventMessage = view.findViewById(R.id.security_event_text);
|
||||
|
||||
rightAnchor = view.findViewById(R.id.rightAnchor);
|
||||
bubbleLayout = view.findViewById(R.id.bubble);
|
||||
background = view.findViewById(R.id.background);
|
||||
avatarLayout = view.findViewById(R.id.avatar_layout);
|
||||
|
||||
downloadInProgress = view.findViewById(R.id.download_in_progress);
|
||||
sendInProgress = view.findViewById(R.id.send_in_progress);
|
||||
timeText = view.findViewById(R.id.time);
|
||||
outgoingImdn = view.findViewById(R.id.imdn);
|
||||
messageText = view.findViewById(R.id.message);
|
||||
|
||||
singleFileContent = view.findViewById(R.id.single_content);
|
||||
multiFileContents = view.findViewById(R.id.multi_content);
|
||||
|
||||
delete = view.findViewById(R.id.delete_event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mListener != null) {
|
||||
mListener.onItemClicked(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
public void bindMessage(final ChatMessage message, LinphoneContact contact) {
|
||||
eventLayout.setVisibility(View.GONE);
|
||||
securityEventLayout.setVisibility(View.GONE);
|
||||
rightAnchor.setVisibility(View.VISIBLE);
|
||||
bubbleLayout.setVisibility(View.VISIBLE);
|
||||
messageText.setVisibility(View.GONE);
|
||||
timeText.setVisibility(View.VISIBLE);
|
||||
outgoingImdn.setVisibility(View.GONE);
|
||||
avatarLayout.setVisibility(View.GONE);
|
||||
sendInProgress.setVisibility(View.GONE);
|
||||
downloadInProgress.setVisibility(View.GONE);
|
||||
singleFileContent.setVisibility(View.GONE);
|
||||
multiFileContents.setVisibility(View.GONE);
|
||||
|
||||
ChatMessage.State status = message.getState();
|
||||
Address remoteSender = message.getFromAddress();
|
||||
String displayName;
|
||||
String time =
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
mContext, message.getTime(), R.string.messages_date_format);
|
||||
|
||||
if (message.isOutgoing()) {
|
||||
bubbleLayout.setPadding(0, 0, 0, 0); // Reset padding
|
||||
outgoingImdn.setVisibility(View.INVISIBLE); // For anchoring purposes
|
||||
|
||||
if (status == ChatMessage.State.DeliveredToUser) {
|
||||
outgoingImdn.setVisibility(View.VISIBLE);
|
||||
outgoingImdn.setImageResource(R.drawable.imdn_received);
|
||||
} else if (status == ChatMessage.State.Displayed) {
|
||||
outgoingImdn.setVisibility(View.VISIBLE);
|
||||
outgoingImdn.setImageResource(R.drawable.imdn_read);
|
||||
} else if (status == ChatMessage.State.NotDelivered) {
|
||||
outgoingImdn.setVisibility(View.VISIBLE);
|
||||
outgoingImdn.setImageResource(R.drawable.imdn_error);
|
||||
} else if (status == ChatMessage.State.FileTransferError) {
|
||||
outgoingImdn.setVisibility(View.VISIBLE);
|
||||
outgoingImdn.setImageResource(R.drawable.imdn_error);
|
||||
} else if (status == ChatMessage.State.InProgress
|
||||
|| status == ChatMessage.State.FileTransferInProgress) {
|
||||
sendInProgress.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
timeText.setVisibility(View.VISIBLE);
|
||||
background.setBackgroundResource(R.drawable.chat_bubble_outgoing_full);
|
||||
} else {
|
||||
rightAnchor.setVisibility(View.GONE);
|
||||
avatarLayout.setVisibility(View.VISIBLE);
|
||||
background.setBackgroundResource(R.drawable.chat_bubble_incoming_full);
|
||||
|
||||
// Can't anchor incoming messages, setting this to align max width with LIME icon
|
||||
bubbleLayout.setPadding(
|
||||
0, 0, (int) ImageUtils.dpToPixels(LinphoneActivity.instance(), 18), 0);
|
||||
|
||||
if (status == ChatMessage.State.FileTransferInProgress) {
|
||||
downloadInProgress.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
if (contact == null) {
|
||||
contact = ContactsManager.getInstance().findContactFromAddress(remoteSender);
|
||||
}
|
||||
if (contact != null) {
|
||||
if (contact.getFullName() != null) {
|
||||
displayName = contact.getFullName();
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(remoteSender);
|
||||
}
|
||||
ContactAvatar.displayAvatar(contact, avatarLayout);
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(remoteSender);
|
||||
ContactAvatar.displayAvatar(displayName, avatarLayout);
|
||||
}
|
||||
|
||||
if (message.isOutgoing()) {
|
||||
timeText.setText(time);
|
||||
} else {
|
||||
timeText.setText(time + " - " + displayName);
|
||||
}
|
||||
|
||||
if (message.hasTextContent()) {
|
||||
String msg = message.getTextContent();
|
||||
Spanned text = LinphoneUtils.getTextWithHttpLinks(msg);
|
||||
messageText.setText(text);
|
||||
messageText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
messageText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
List<Content> fileContents = new ArrayList<>();
|
||||
for (Content c : message.getContents()) {
|
||||
if (c.isFile() || c.isFileTransfer()) {
|
||||
fileContents.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (fileContents.size() == 1) {
|
||||
singleFileContent.setVisibility(View.VISIBLE);
|
||||
displayContent(message, fileContents.get(0), singleFileContent, false);
|
||||
} else if (fileContents.size() > 1) {
|
||||
multiFileContents.removeAllViews();
|
||||
multiFileContents.setVisibility(View.VISIBLE);
|
||||
|
||||
for (Content c : fileContents) {
|
||||
View content =
|
||||
LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.chat_bubble_content, null, false);
|
||||
|
||||
displayContent(message, c, content, true);
|
||||
|
||||
multiFileContents.addView(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void displayContent(
|
||||
final ChatMessage message, Content c, View content, boolean isMultiContent) {
|
||||
final Button downloadOrCancel = content.findViewById(R.id.download);
|
||||
downloadOrCancel.setVisibility(View.GONE);
|
||||
final ImageView bigImage = content.findViewById(R.id.bigImage);
|
||||
bigImage.setVisibility(View.GONE);
|
||||
final ImageView smallImage = content.findViewById(R.id.image);
|
||||
smallImage.setVisibility(View.GONE);
|
||||
final TextView fileName = content.findViewById(R.id.file);
|
||||
fileName.setVisibility(View.GONE);
|
||||
|
||||
if (c.isFile() || (c.isFileTransfer() && message.isOutgoing())) {
|
||||
// If message is outgoing, even if content
|
||||
// is file transfer we have the file available
|
||||
final String filePath = c.getFilePath();
|
||||
|
||||
View v;
|
||||
if (FileUtils.isExtensionImage(filePath)) {
|
||||
if (!isMultiContent
|
||||
&& mContext.getResources()
|
||||
.getBoolean(
|
||||
R.bool.use_big_pictures_to_preview_images_file_transfers)) {
|
||||
loadBitmap(c.getFilePath(), bigImage);
|
||||
v = bigImage;
|
||||
} else {
|
||||
loadBitmap(c.getFilePath(), smallImage);
|
||||
v = smallImage;
|
||||
}
|
||||
} else {
|
||||
fileName.setText(FileUtils.getNameFromFilePath(filePath));
|
||||
v = fileName;
|
||||
}
|
||||
v.setVisibility(View.VISIBLE);
|
||||
v.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
openFile(filePath);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
downloadOrCancel.setVisibility(View.VISIBLE);
|
||||
|
||||
if (mContext.getPackageManager()
|
||||
.checkPermission(
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
mContext.getPackageName())
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
String filename = c.getName();
|
||||
File file = new File(FileUtils.getStorageDirectory(mContext), filename);
|
||||
|
||||
int prefix = 1;
|
||||
while (file.exists()) {
|
||||
file =
|
||||
new File(
|
||||
FileUtils.getStorageDirectory(mContext),
|
||||
prefix + "_" + filename);
|
||||
Log.w(
|
||||
"File with that name already exists, renamed to "
|
||||
+ prefix
|
||||
+ "_"
|
||||
+ filename);
|
||||
prefix += 1;
|
||||
}
|
||||
c.setFilePath(file.getPath());
|
||||
|
||||
downloadOrCancel.setTag(c);
|
||||
if (!message.isFileTransferInProgress()) {
|
||||
downloadOrCancel.setText(R.string.download_file);
|
||||
} else {
|
||||
downloadOrCancel.setText(R.string.cancel);
|
||||
}
|
||||
|
||||
downloadOrCancel.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Content c = (Content) v.getTag();
|
||||
if (!message.isFileTransferInProgress()) {
|
||||
message.downloadContent(c);
|
||||
} else {
|
||||
message.cancelFileTransfer();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.w(
|
||||
"WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file");
|
||||
LinphoneActivity.instance().checkAndRequestExternalStoragePermission();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void openFile(String path) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
File file;
|
||||
Uri contentUri;
|
||||
if (path.startsWith("file://")) {
|
||||
path = path.substring("file://".length());
|
||||
file = new File(path);
|
||||
contentUri =
|
||||
FileProvider.getUriForFile(
|
||||
mContext,
|
||||
mContext.getResources().getString(R.string.file_provider),
|
||||
file);
|
||||
} else if (path.startsWith("content://")) {
|
||||
contentUri = Uri.parse(path);
|
||||
} else {
|
||||
file = new File(path);
|
||||
try {
|
||||
contentUri =
|
||||
FileProvider.getUriForFile(
|
||||
mContext,
|
||||
mContext.getResources().getString(R.string.file_provider),
|
||||
file);
|
||||
} catch (Exception e) {
|
||||
contentUri = Uri.parse(path);
|
||||
}
|
||||
}
|
||||
|
||||
String type = null;
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(contentUri.toString());
|
||||
if (extension != null) {
|
||||
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
if (type != null) {
|
||||
intent.setDataAndType(contentUri, type);
|
||||
} else {
|
||||
intent.setDataAndType(contentUri, "*/*");
|
||||
}
|
||||
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
|
||||
private void loadBitmap(String path, ImageView imageView) {
|
||||
Glide.with(mContext).load(path).into(imageView);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
ChatMessageViewHolderClickListener.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
interface ChatMessageViewHolderClickListener {
|
||||
void onItemClicked(int position);
|
||||
}
|
||||
366
app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java
Normal file
366
app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
ChatMessagesAdapter.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.ChatMessageListenerStub;
|
||||
import org.linphone.core.EventLog;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.utils.SelectableAdapter;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
|
||||
public class ChatMessagesAdapter extends SelectableAdapter<ChatMessageViewHolder>
|
||||
implements ChatMessagesGenericAdapter {
|
||||
private static final int MAX_TIME_TO_GROUP_MESSAGES = 300; // 5 minutes
|
||||
|
||||
private final Context mContext;
|
||||
private List<EventLog> mHistory;
|
||||
private List<LinphoneContact> mParticipants;
|
||||
private final int mItemResource;
|
||||
private final ChatMessagesFragment mFragment;
|
||||
|
||||
private final List<ChatMessage> mTransientMessages;
|
||||
|
||||
private final ChatMessageViewHolderClickListener mClickListener;
|
||||
private final ChatMessageListenerStub mListener;
|
||||
|
||||
public ChatMessagesAdapter(
|
||||
ChatMessagesFragment fragment,
|
||||
SelectableHelper helper,
|
||||
int itemResource,
|
||||
EventLog[] history,
|
||||
ArrayList<LinphoneContact> participants,
|
||||
ChatMessageViewHolderClickListener clickListener) {
|
||||
super(helper);
|
||||
mFragment = fragment;
|
||||
mContext = mFragment.getActivity();
|
||||
mItemResource = itemResource;
|
||||
mHistory = new ArrayList<>(Arrays.asList(history));
|
||||
Collections.reverse(mHistory);
|
||||
mParticipants = participants;
|
||||
mClickListener = clickListener;
|
||||
mTransientMessages = new ArrayList<>();
|
||||
|
||||
mListener =
|
||||
new ChatMessageListenerStub() {
|
||||
@Override
|
||||
public void onMsgStateChanged(ChatMessage message, ChatMessage.State state) {
|
||||
ChatMessageViewHolder holder =
|
||||
(ChatMessageViewHolder) message.getUserData();
|
||||
if (holder != null) {
|
||||
int position = holder.getAdapterPosition();
|
||||
if (position >= 0) {
|
||||
notifyItemChanged(position);
|
||||
} else {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
} else {
|
||||
// Just in case, better to refresh the whole view than to miss
|
||||
// an update
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
if (state == ChatMessage.State.Displayed) {
|
||||
mTransientMessages.remove(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatMessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(mItemResource, parent, false);
|
||||
ChatMessageViewHolder VH = new ChatMessageViewHolder(mContext, v, mClickListener);
|
||||
|
||||
// Allows onLongClick ContextMenu on bubbles
|
||||
mFragment.registerForContextMenu(v);
|
||||
v.setTag(VH);
|
||||
return VH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChatMessageViewHolder holder, int position) {
|
||||
if (position < 0) return;
|
||||
EventLog event = mHistory.get(position);
|
||||
|
||||
holder.delete.setVisibility(View.GONE);
|
||||
holder.eventLayout.setVisibility(View.GONE);
|
||||
holder.securityEventLayout.setVisibility(View.GONE);
|
||||
holder.rightAnchor.setVisibility(View.GONE);
|
||||
holder.bubbleLayout.setVisibility(View.GONE);
|
||||
holder.sendInProgress.setVisibility(View.GONE);
|
||||
|
||||
if (isEditionEnabled()) {
|
||||
holder.delete.setVisibility(View.VISIBLE);
|
||||
holder.delete.setChecked(isSelected(position));
|
||||
holder.delete.setTag(position);
|
||||
}
|
||||
|
||||
if (event.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
ChatMessage message = event.getChatMessage();
|
||||
|
||||
if ((message.isOutgoing() && message.getState() != ChatMessage.State.Displayed)
|
||||
|| (!message.isOutgoing() && message.isFileTransfer())) {
|
||||
if (!mTransientMessages.contains(message)) {
|
||||
mTransientMessages.add(message);
|
||||
}
|
||||
// This only works if JAVA object is kept, hence the transient list
|
||||
message.setUserData(holder);
|
||||
message.addListener(mListener);
|
||||
}
|
||||
|
||||
LinphoneContact contact = null;
|
||||
Address remoteSender = message.getFromAddress();
|
||||
if (!message.isOutgoing()) {
|
||||
for (LinphoneContact c : mParticipants) {
|
||||
if (c != null && c.hasAddress(remoteSender.asStringUriOnly())) {
|
||||
contact = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.bindMessage(message, contact);
|
||||
changeBackgroundDependingOnPreviousAndNextEvents(message, holder, position);
|
||||
} else { // Event is not chat message
|
||||
Address address = event.getParticipantAddress();
|
||||
if (address == null && event.getType() == EventLog.Type.ConferenceSecurityEvent) {
|
||||
address = event.getSecurityEventFaultyDeviceAddress();
|
||||
}
|
||||
String displayName = "";
|
||||
if (address != null) {
|
||||
LinphoneContact contact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
if (contact != null) {
|
||||
displayName = contact.getFullName();
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(address);
|
||||
}
|
||||
}
|
||||
|
||||
switch (event.getType()) {
|
||||
case ConferenceCreated:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(mContext.getString(R.string.conference_created));
|
||||
break;
|
||||
case ConferenceTerminated:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(mContext.getString(R.string.conference_destroyed));
|
||||
break;
|
||||
case ConferenceParticipantAdded:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.participant_added)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantRemoved:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.participant_removed)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceSubjectChanged:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.subject_changed)
|
||||
.replace("%s", event.getSubject()));
|
||||
break;
|
||||
case ConferenceParticipantSetAdmin:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.admin_set).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantUnsetAdmin:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.admin_unset).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantDeviceAdded:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.device_added).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantDeviceRemoved:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.device_removed).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceSecurityEvent:
|
||||
holder.securityEventLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
switch (event.getSecurityEventType()) {
|
||||
case EncryptionIdentityKeyChanged:
|
||||
holder.securityEventMessage.setText(
|
||||
mContext.getString(R.string.lime_identity_key_changed)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ManInTheMiddleDetected:
|
||||
holder.securityEventMessage.setText(
|
||||
mContext.getString(R.string.man_in_the_middle_detected)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case SecurityLevelDowngraded:
|
||||
holder.securityEventMessage.setText(
|
||||
mContext.getString(R.string.security_level_downgraded)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ParticipantMaxDeviceCountExceeded:
|
||||
holder.securityEventMessage.setText(
|
||||
mContext.getString(R.string.participant_max_count_exceeded)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case None:
|
||||
default:
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.unexpected_event)
|
||||
.replace("%s", displayName)
|
||||
.replace("%i", String.valueOf(event.getType().toInt())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mHistory.size();
|
||||
}
|
||||
|
||||
public void addToHistory(EventLog log) {
|
||||
mHistory.add(0, log);
|
||||
notifyItemInserted(0);
|
||||
notifyItemChanged(1); // Update second to last item just in case for grouping purposes
|
||||
}
|
||||
|
||||
public void addAllToHistory(ArrayList<EventLog> logs) {
|
||||
int currentSize = mHistory.size() - 1;
|
||||
Collections.reverse(logs);
|
||||
mHistory.addAll(logs);
|
||||
notifyItemRangeInserted(currentSize + 1, logs.size());
|
||||
}
|
||||
|
||||
public void setContacts(ArrayList<LinphoneContact> participants) {
|
||||
mParticipants = participants;
|
||||
}
|
||||
|
||||
public void refresh(EventLog[] history) {
|
||||
mHistory = new ArrayList<>(Arrays.asList(history));
|
||||
Collections.reverse(mHistory);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
for (EventLog event : mHistory) {
|
||||
if (event.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
ChatMessage message = event.getChatMessage();
|
||||
message.removeListener(mListener);
|
||||
}
|
||||
}
|
||||
mTransientMessages.clear();
|
||||
mHistory.clear();
|
||||
}
|
||||
|
||||
public Object getItem(int i) {
|
||||
return mHistory.get(i);
|
||||
}
|
||||
|
||||
public void removeItem(int i) {
|
||||
mHistory.remove(i);
|
||||
notifyItemRemoved(i);
|
||||
}
|
||||
|
||||
private void changeBackgroundDependingOnPreviousAndNextEvents(
|
||||
ChatMessage message, ChatMessageViewHolder holder, int position) {
|
||||
boolean hasPrevious = false, hasNext = false;
|
||||
|
||||
// Do not forget history is reversed, so previous in order is next in list display and
|
||||
// chronology !
|
||||
if (position > 0
|
||||
&& mContext.getResources()
|
||||
.getBoolean(R.bool.lower_space_between_chat_bubbles_if_same_person)) {
|
||||
EventLog previousEvent = (EventLog) getItem(position - 1);
|
||||
if (previousEvent.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
ChatMessage previousMessage = previousEvent.getChatMessage();
|
||||
if (previousMessage.getFromAddress().weakEqual(message.getFromAddress())) {
|
||||
if (previousMessage.getTime() - message.getTime()
|
||||
< MAX_TIME_TO_GROUP_MESSAGES) {
|
||||
hasPrevious = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (position >= 0
|
||||
&& position < mHistory.size() - 1
|
||||
&& mContext.getResources()
|
||||
.getBoolean(R.bool.lower_space_between_chat_bubbles_if_same_person)) {
|
||||
EventLog nextEvent = (EventLog) getItem(position + 1);
|
||||
if (nextEvent.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
ChatMessage nextMessage = nextEvent.getChatMessage();
|
||||
if (nextMessage.getFromAddress().weakEqual(message.getFromAddress())) {
|
||||
if (message.getTime() - nextMessage.getTime() < MAX_TIME_TO_GROUP_MESSAGES) {
|
||||
holder.timeText.setVisibility(View.GONE);
|
||||
if (!message.isOutgoing()) {
|
||||
holder.avatarLayout.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
hasNext = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message.isOutgoing()) {
|
||||
if (hasNext && hasPrevious) {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_split_2);
|
||||
} else if (hasNext) {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_split_3);
|
||||
} else if (hasPrevious) {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_split_1);
|
||||
} else {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_outgoing_full);
|
||||
}
|
||||
} else {
|
||||
if (hasNext && hasPrevious) {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_split_2);
|
||||
} else if (hasNext) {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_split_3);
|
||||
} else if (hasPrevious) {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_split_1);
|
||||
} else {
|
||||
holder.background.setBackgroundResource(R.drawable.chat_bubble_incoming_full);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1499
app/src/main/java/org/linphone/chat/ChatMessagesFragment.java
Normal file
1499
app/src/main/java/org/linphone/chat/ChatMessagesFragment.java
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,8 @@
|
|||
package org.linphone;
|
||||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
AinitTestEnv.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
ChatMessagesGenericAdapter.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
|
|
@ -19,22 +19,22 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import junit.framework.Assert;
|
||||
import java.util.ArrayList;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.EventLog;
|
||||
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
interface ChatMessagesGenericAdapter {
|
||||
void addToHistory(EventLog log);
|
||||
|
||||
public class AinitTestEnv extends SampleTest {
|
||||
void addAllToHistory(ArrayList<EventLog> logs);
|
||||
|
||||
@SmallTest
|
||||
@MediumTest
|
||||
@LargeTest
|
||||
public void testAInitLinphoneCore() {
|
||||
LinphoneTestManager.createAndStart(aContext, iContext, 1);
|
||||
void setContacts(ArrayList<LinphoneContact> participants);
|
||||
|
||||
solo.sleep(5000);
|
||||
Assert.assertEquals(1, LinphoneTestManager.getLc().getProxyConfigList().length);
|
||||
waitForRegistration(LinphoneTestManager.getLc().getProxyConfigList()[0]);
|
||||
}
|
||||
void refresh(EventLog[] history);
|
||||
|
||||
void clear();
|
||||
|
||||
Object getItem(int i);
|
||||
|
||||
void removeItem(int i);
|
||||
}
|
||||
662
app/src/main/java/org/linphone/chat/ChatMessagesOldAdapter.java
Normal file
662
app/src/main/java/org/linphone/chat/ChatMessagesOldAdapter.java
Normal file
|
|
@ -0,0 +1,662 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
ChatMessagesAdapter.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.FileProvider;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.compatibility.Compatibility;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.ChatMessageListenerStub;
|
||||
import org.linphone.core.Content;
|
||||
import org.linphone.core.EventLog;
|
||||
import org.linphone.core.LimeState;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.utils.FileUtils;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.utils.SelectableAdapter;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
import org.linphone.views.AsyncBitmap;
|
||||
import org.linphone.views.BitmapWorkerTask;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ChatMessagesOldAdapter extends SelectableAdapter<ChatMessageOldViewHolder>
|
||||
implements ChatMessagesGenericAdapter {
|
||||
|
||||
private static final int MARGIN_BETWEEN_MESSAGES = 10;
|
||||
private static final int SIDE_MARGIN = 100;
|
||||
private final Context mContext;
|
||||
private List<EventLog> mHistory;
|
||||
private List<LinphoneContact> mParticipants;
|
||||
private final int mItemResource;
|
||||
private Bitmap mDefaultBitmap;
|
||||
private final ChatMessagesFragment mFragment;
|
||||
private final ChatMessageListenerStub mListener;
|
||||
|
||||
private final ChatMessageViewHolderClickListener mClickListener;
|
||||
|
||||
public ChatMessagesOldAdapter(
|
||||
ChatMessagesFragment fragment,
|
||||
SelectableHelper helper,
|
||||
int itemResource,
|
||||
EventLog[] history,
|
||||
ArrayList<LinphoneContact> participants,
|
||||
ChatMessageViewHolderClickListener clickListener) {
|
||||
super(helper);
|
||||
mFragment = fragment;
|
||||
mContext = mFragment.getActivity();
|
||||
mItemResource = itemResource;
|
||||
mHistory = new ArrayList<>(Arrays.asList(history));
|
||||
Collections.reverse(mHistory);
|
||||
mParticipants = participants;
|
||||
mClickListener = clickListener;
|
||||
mListener =
|
||||
new ChatMessageListenerStub() {
|
||||
@Override
|
||||
public void onFileTransferProgressIndication(
|
||||
ChatMessage message, Content content, int offset, int total) {
|
||||
ChatMessageOldViewHolder holder =
|
||||
(ChatMessageOldViewHolder) message.getUserData();
|
||||
if (holder == null) return;
|
||||
|
||||
if (offset == total) {
|
||||
holder.fileTransferProgressBar.setVisibility(View.GONE);
|
||||
holder.fileTransferAction.setVisibility(View.GONE);
|
||||
holder.fileTransferLayout.setVisibility(View.GONE);
|
||||
|
||||
displayAttachedFile(message, holder);
|
||||
} else {
|
||||
holder.fileTransferProgressBar.setVisibility(View.VISIBLE);
|
||||
holder.fileTransferProgressBar.setProgress(offset * 100 / total);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMsgStateChanged(ChatMessage message, ChatMessage.State state) {
|
||||
if (state == ChatMessage.State.FileTransferDone) {
|
||||
if (!message.isOutgoing()) {
|
||||
message.setAppdata(message.getFileTransferFilepath());
|
||||
}
|
||||
message.setFileTransferFilepath(
|
||||
null); // Not needed anymore, will help differenciate between
|
||||
// InProgress states for file transfer / message sending
|
||||
}
|
||||
for (int i = 0; i < mHistory.size(); i++) {
|
||||
EventLog log = mHistory.get(i);
|
||||
if (log.getType() == EventLog.Type.ConferenceChatMessage
|
||||
&& log.getChatMessage() == message) {
|
||||
notifyItemChanged(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatMessageOldViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(mItemResource, parent, false);
|
||||
ChatMessageOldViewHolder VH = new ChatMessageOldViewHolder(v, mClickListener);
|
||||
|
||||
// Allows onLongClick ContextMenu on bubbles
|
||||
mFragment.registerForContextMenu(v);
|
||||
v.setTag(VH);
|
||||
return VH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final ChatMessageOldViewHolder holder, int position) {
|
||||
final EventLog event = mHistory.get(position);
|
||||
holder.eventLayout.setVisibility(View.GONE);
|
||||
holder.bubbleLayout.setVisibility(View.GONE);
|
||||
holder.delete.setVisibility(isEditionEnabled() ? View.VISIBLE : View.GONE);
|
||||
holder.messageText.setVisibility(View.GONE);
|
||||
holder.messageImage.setVisibility(View.GONE);
|
||||
holder.fileTransferLayout.setVisibility(View.GONE);
|
||||
holder.fileTransferProgressBar.setProgress(0);
|
||||
holder.fileTransferAction.setEnabled(true);
|
||||
holder.fileName.setVisibility(View.GONE);
|
||||
holder.openFileButton.setVisibility(View.GONE);
|
||||
holder.messageStatus.setVisibility(View.INVISIBLE);
|
||||
holder.messageSendingInProgress.setVisibility(View.GONE);
|
||||
holder.imdmLayout.setVisibility(View.INVISIBLE);
|
||||
|
||||
if (isEditionEnabled()) {
|
||||
holder.delete.setOnCheckedChangeListener(null);
|
||||
holder.delete.setChecked(isSelected(position));
|
||||
holder.delete.setTag(position);
|
||||
}
|
||||
|
||||
if (event.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
holder.bubbleLayout.setVisibility(View.VISIBLE);
|
||||
final ChatMessage message = event.getChatMessage();
|
||||
|
||||
if (position > 0
|
||||
&& mContext.getResources()
|
||||
.getBoolean(R.bool.lower_space_between_chat_bubbles_if_same_person)) {
|
||||
EventLog previousEvent = (EventLog) getItem(position - 1);
|
||||
if (previousEvent.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
ChatMessage previousMessage = previousEvent.getChatMessage();
|
||||
if (previousMessage.getFromAddress().weakEqual(message.getFromAddress())) {
|
||||
holder.separatorLayout.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
// No separator if previous event is not a message
|
||||
holder.separatorLayout.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
message.setUserData(holder);
|
||||
message.addListener(mListener);
|
||||
|
||||
RelativeLayout.LayoutParams layoutParams =
|
||||
new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT,
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
ChatMessage.State status = message.getState();
|
||||
Address remoteSender = message.getFromAddress();
|
||||
String displayName;
|
||||
|
||||
LinphoneContact contact = null;
|
||||
if (message.isOutgoing()) {
|
||||
if (status == ChatMessage.State.InProgress) {
|
||||
holder.messageSendingInProgress.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (!message.isSecured()
|
||||
&& LinphoneManager.getLc().limeEnabled() == LimeState.Mandatory
|
||||
&& status != ChatMessage.State.InProgress) {
|
||||
holder.messageStatus.setVisibility(View.VISIBLE);
|
||||
holder.messageStatus.setImageResource(R.drawable.chat_unsecure);
|
||||
}
|
||||
|
||||
if (status == ChatMessage.State.DeliveredToUser) {
|
||||
holder.imdmLayout.setVisibility(View.VISIBLE);
|
||||
holder.imdmIcon.setImageResource(R.drawable.imdn_received);
|
||||
holder.imdmLabel.setText(R.string.delivered);
|
||||
holder.imdmLabel.setTextColor(
|
||||
mContext.getResources().getColor(R.color.grey_color));
|
||||
} else if (status == ChatMessage.State.Displayed) {
|
||||
holder.imdmLayout.setVisibility(View.VISIBLE);
|
||||
holder.imdmIcon.setImageResource(R.drawable.imdn_read);
|
||||
holder.imdmLabel.setText(R.string.displayed);
|
||||
holder.imdmLabel.setTextColor(
|
||||
mContext.getResources().getColor(R.color.imdn_read_color));
|
||||
} else if (status == ChatMessage.State.NotDelivered) {
|
||||
holder.imdmLayout.setVisibility(View.VISIBLE);
|
||||
holder.imdmIcon.setImageResource(R.drawable.imdn_error);
|
||||
holder.imdmLabel.setText(R.string.error);
|
||||
holder.imdmLabel.setTextColor(
|
||||
mContext.getResources().getColor(R.color.red_color));
|
||||
} else if (status == ChatMessage.State.FileTransferError) {
|
||||
holder.imdmLayout.setVisibility(View.VISIBLE);
|
||||
holder.imdmIcon.setImageResource(R.drawable.imdn_error);
|
||||
holder.imdmLabel.setText(R.string.file_transfer_error);
|
||||
holder.imdmLabel.setTextColor(
|
||||
mContext.getResources().getColor(R.color.red_color));
|
||||
}
|
||||
|
||||
// layoutParams allow bubbles alignment during selection mode
|
||||
if (isEditionEnabled()) {
|
||||
layoutParams.addRule(RelativeLayout.LEFT_OF, holder.delete.getId());
|
||||
layoutParams.setMargins(
|
||||
SIDE_MARGIN,
|
||||
MARGIN_BETWEEN_MESSAGES / 2,
|
||||
0,
|
||||
MARGIN_BETWEEN_MESSAGES / 2);
|
||||
} else {
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
layoutParams.setMargins(
|
||||
SIDE_MARGIN,
|
||||
MARGIN_BETWEEN_MESSAGES / 2,
|
||||
0,
|
||||
MARGIN_BETWEEN_MESSAGES / 2);
|
||||
}
|
||||
|
||||
holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_outgoing);
|
||||
Compatibility.setTextAppearance(holder.contactName, mContext, R.style.font3);
|
||||
Compatibility.setTextAppearance(
|
||||
holder.fileTransferAction, mContext, R.style.font15);
|
||||
holder.fileTransferAction.setBackgroundResource(
|
||||
R.drawable.resizable_confirm_delete_button);
|
||||
} else {
|
||||
for (LinphoneContact c : mParticipants) {
|
||||
if (c != null && c.hasAddress(remoteSender.asStringUriOnly())) {
|
||||
contact = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEditionEnabled()) {
|
||||
layoutParams.addRule(RelativeLayout.LEFT_OF, holder.delete.getId());
|
||||
layoutParams.setMargins(
|
||||
SIDE_MARGIN,
|
||||
MARGIN_BETWEEN_MESSAGES / 2,
|
||||
0,
|
||||
MARGIN_BETWEEN_MESSAGES / 2);
|
||||
} else {
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
|
||||
layoutParams.setMargins(
|
||||
0,
|
||||
MARGIN_BETWEEN_MESSAGES / 2,
|
||||
SIDE_MARGIN,
|
||||
MARGIN_BETWEEN_MESSAGES / 2);
|
||||
}
|
||||
|
||||
holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_incoming);
|
||||
Compatibility.setTextAppearance(
|
||||
holder.contactName, mContext, R.style.contact_organization_font);
|
||||
Compatibility.setTextAppearance(
|
||||
holder.fileTransferAction, mContext, R.style.button_font);
|
||||
holder.fileTransferAction.setBackgroundResource(
|
||||
R.drawable.resizable_assistant_button);
|
||||
}
|
||||
|
||||
if (contact == null) {
|
||||
contact = ContactsManager.getInstance().findContactFromAddress(remoteSender);
|
||||
}
|
||||
if (contact != null) {
|
||||
if (contact.getFullName() != null) {
|
||||
displayName = contact.getFullName();
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(remoteSender);
|
||||
}
|
||||
ContactAvatar.displayAvatar(contact, holder.avatarLayout);
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(remoteSender);
|
||||
ContactAvatar.displayAvatar(displayName, holder.avatarLayout);
|
||||
}
|
||||
holder.contactName.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
mContext, message.getTime(), R.string.messages_date_format)
|
||||
+ " - "
|
||||
+ displayName);
|
||||
|
||||
if (message.hasTextContent()) {
|
||||
String msg = message.getTextContent();
|
||||
Spanned text = LinphoneUtils.getTextWithHttpLinks(msg);
|
||||
holder.messageText.setText(text);
|
||||
holder.messageText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
holder.messageText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
String externalBodyUrl = message.getExternalBodyUrl();
|
||||
Content fileTransferContent = message.getFileTransferInformation();
|
||||
|
||||
boolean hasFile = message.getAppdata() != null;
|
||||
boolean hasFileTransfer = externalBodyUrl != null;
|
||||
for (Content c : message.getContents()) {
|
||||
if (c.isFile()) {
|
||||
hasFile = true;
|
||||
} else if (c.isFileTransfer()) {
|
||||
hasFileTransfer = true;
|
||||
}
|
||||
}
|
||||
if (hasFile) { // Something to display
|
||||
displayAttachedFile(message, holder);
|
||||
}
|
||||
|
||||
if (hasFileTransfer) { // Incoming file transfer not yet downloaded
|
||||
holder.fileName.setVisibility(View.VISIBLE);
|
||||
holder.fileName.setText(fileTransferContent.getName());
|
||||
|
||||
holder.fileTransferLayout.setVisibility(View.VISIBLE);
|
||||
holder.fileTransferProgressBar.setVisibility(View.GONE);
|
||||
if (message.isFileTransferInProgress()) { // Incoming file transfer in progress
|
||||
holder.fileTransferAction.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.fileTransferAction.setText(mContext.getString(R.string.accept));
|
||||
holder.fileTransferAction.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mContext.getPackageManager()
|
||||
.checkPermission(
|
||||
Manifest.permission
|
||||
.WRITE_EXTERNAL_STORAGE,
|
||||
mContext.getPackageName())
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
v.setEnabled(false);
|
||||
String filename =
|
||||
message.getFileTransferInformation().getName();
|
||||
File file =
|
||||
new File(
|
||||
FileUtils.getStorageDirectory(mContext),
|
||||
filename);
|
||||
int prefix = 1;
|
||||
while (file.exists()) {
|
||||
file =
|
||||
new File(
|
||||
FileUtils.getStorageDirectory(mContext),
|
||||
prefix + "_" + filename);
|
||||
Log.w(
|
||||
"File with that name already exists, renamed to "
|
||||
+ prefix
|
||||
+ "_"
|
||||
+ filename);
|
||||
prefix += 1;
|
||||
}
|
||||
message.setFileTransferFilepath(file.getPath());
|
||||
message.downloadFile();
|
||||
|
||||
} else {
|
||||
Log.w(
|
||||
"WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file");
|
||||
LinphoneActivity.instance()
|
||||
.checkAndRequestExternalStoragePermission();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (message.isFileTransferInProgress()) { // Outgoing file transfer in progress
|
||||
holder.messageSendingInProgress.setVisibility(View.GONE);
|
||||
holder.fileTransferLayout.setVisibility(View.VISIBLE);
|
||||
holder.fileTransferAction.setText(mContext.getString(R.string.cancel));
|
||||
holder.fileTransferAction.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
message.cancelFileTransfer();
|
||||
notifyItemChanged(holder.getAdapterPosition());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
holder.bubbleLayout.setLayoutParams(layoutParams);
|
||||
} else { // Event is not chat message
|
||||
holder.eventLayout.setVisibility(View.VISIBLE);
|
||||
holder.eventMessage.setTextColor(
|
||||
mContext.getResources().getColor(R.color.light_grey_color));
|
||||
holder.eventLayout.setBackgroundResource(R.drawable.event_decoration_gray);
|
||||
holder.eventLayout.setBackgroundResource(R.drawable.event_decoration_gray);
|
||||
|
||||
Address address = event.getParticipantAddress();
|
||||
if (address == null && event.getType() == EventLog.Type.ConferenceSecurityEvent) {
|
||||
address = event.getSecurityEventFaultyDeviceAddress();
|
||||
}
|
||||
String displayName = "";
|
||||
if (address != null) {
|
||||
LinphoneContact contact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
if (contact != null) {
|
||||
displayName = contact.getFullName();
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(address);
|
||||
}
|
||||
}
|
||||
|
||||
switch (event.getType()) {
|
||||
case ConferenceCreated:
|
||||
holder.eventMessage.setText(mContext.getString(R.string.conference_created));
|
||||
break;
|
||||
case ConferenceTerminated:
|
||||
holder.eventMessage.setText(mContext.getString(R.string.conference_destroyed));
|
||||
break;
|
||||
case ConferenceParticipantAdded:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.participant_added)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantRemoved:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.participant_removed)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceSubjectChanged:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.subject_changed)
|
||||
.replace("%s", event.getSubject()));
|
||||
break;
|
||||
case ConferenceParticipantSetAdmin:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.admin_set).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantUnsetAdmin:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.admin_unset).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantDeviceAdded:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.device_added).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceParticipantDeviceRemoved:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.device_removed).replace("%s", displayName));
|
||||
break;
|
||||
case ConferenceSecurityEvent:
|
||||
holder.eventMessage.setTextColor(
|
||||
mContext.getResources().getColor(R.color.red_color));
|
||||
holder.eventLayout.setBackgroundResource(R.drawable.event_decoration_red);
|
||||
holder.eventLayout.setBackgroundResource(R.drawable.event_decoration_red);
|
||||
|
||||
switch (event.getSecurityEventType()) {
|
||||
case EncryptionIdentityKeyChanged:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.lime_identity_key_changed)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ManInTheMiddleDetected:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.man_in_the_middle_detected)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case SecurityLevelDowngraded:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.security_level_downgraded)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case ParticipantMaxDeviceCountExceeded:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.participant_max_count_exceeded)
|
||||
.replace("%s", displayName));
|
||||
break;
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case None:
|
||||
default:
|
||||
holder.eventMessage.setText(
|
||||
mContext.getString(R.string.unexpected_event)
|
||||
.replace("%s", displayName)
|
||||
.replace("%i", String.valueOf(event.getType().toInt())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mHistory.size();
|
||||
}
|
||||
|
||||
public void addToHistory(EventLog log) {
|
||||
mHistory.add(0, log);
|
||||
notifyItemInserted(0);
|
||||
}
|
||||
|
||||
public void addAllToHistory(ArrayList<EventLog> logs) {
|
||||
int currentSize = mHistory.size() - 1;
|
||||
Collections.reverse(logs);
|
||||
mHistory.addAll(logs);
|
||||
notifyItemRangeInserted(currentSize + 1, logs.size());
|
||||
}
|
||||
|
||||
public void setContacts(ArrayList<LinphoneContact> participants) {
|
||||
mParticipants = participants;
|
||||
}
|
||||
|
||||
public void refresh(EventLog[] history) {
|
||||
mHistory = new ArrayList<>(Arrays.asList(history));
|
||||
Collections.reverse(mHistory);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
for (EventLog event : mHistory) {
|
||||
if (event.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
ChatMessage message = event.getChatMessage();
|
||||
message.removeListener(mListener);
|
||||
}
|
||||
}
|
||||
mHistory.clear();
|
||||
}
|
||||
|
||||
public Object getItem(int i) {
|
||||
return mHistory.get(i);
|
||||
}
|
||||
|
||||
public void removeItem(int i) {
|
||||
mHistory.remove(i);
|
||||
notifyItemRemoved(i);
|
||||
}
|
||||
|
||||
private void loadBitmap(String path, ImageView imageView) {
|
||||
if (cancelPotentialWork(path, imageView)) {
|
||||
mDefaultBitmap =
|
||||
BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_file);
|
||||
BitmapWorkerTask task = new BitmapWorkerTask(mContext, imageView, mDefaultBitmap);
|
||||
final AsyncBitmap asyncBitmap =
|
||||
new AsyncBitmap(mContext.getResources(), mDefaultBitmap, task);
|
||||
imageView.setImageDrawable(asyncBitmap);
|
||||
task.execute(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void openFile(String path) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
File file;
|
||||
Uri contentUri;
|
||||
if (path.startsWith("file://")) {
|
||||
path = path.substring("file://".length());
|
||||
file = new File(path);
|
||||
contentUri =
|
||||
FileProvider.getUriForFile(
|
||||
mContext,
|
||||
mContext.getResources().getString(R.string.file_provider),
|
||||
file);
|
||||
} else if (path.startsWith("content://")) {
|
||||
contentUri = Uri.parse(path);
|
||||
} else {
|
||||
file = new File(path);
|
||||
try {
|
||||
contentUri =
|
||||
FileProvider.getUriForFile(
|
||||
mContext,
|
||||
mContext.getResources().getString(R.string.file_provider),
|
||||
file);
|
||||
} catch (Exception e) {
|
||||
contentUri = Uri.parse(path);
|
||||
}
|
||||
}
|
||||
String type = null;
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(contentUri.toString());
|
||||
if (extension != null) {
|
||||
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
if (type != null) {
|
||||
intent.setDataAndType(contentUri, type);
|
||||
} else {
|
||||
intent.setDataAndType(contentUri, "*/*");
|
||||
}
|
||||
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
|
||||
private void displayAttachedFile(ChatMessage message, ChatMessageOldViewHolder holder) {
|
||||
holder.fileName.setVisibility(View.VISIBLE);
|
||||
|
||||
String appData = message.getAppdata();
|
||||
if (appData == null) {
|
||||
for (Content c : message.getContents()) {
|
||||
if (c.isFile()) {
|
||||
appData = c.getFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (appData != null) {
|
||||
FileUtils.scanFile(message);
|
||||
holder.fileName.setText(FileUtils.getNameFromFilePath(appData));
|
||||
if (FileUtils.isExtensionImage(appData)) {
|
||||
holder.messageImage.setVisibility(View.VISIBLE);
|
||||
loadBitmap(appData, holder.messageImage);
|
||||
holder.messageImage.setTag(appData);
|
||||
} else {
|
||||
holder.openFileButton.setVisibility(View.VISIBLE);
|
||||
holder.openFileButton.setTag(appData);
|
||||
holder.openFileButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
openFile((String) v.getTag());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean cancelPotentialWork(String path, ImageView imageView) {
|
||||
final BitmapWorkerTask bitmapWorkerTask = BitmapWorkerTask.getBitmapWorkerTask(imageView);
|
||||
|
||||
if (bitmapWorkerTask != null) {
|
||||
final String bitmapData = bitmapWorkerTask.path;
|
||||
// If bitmapData is not yet set or it differs from the new data
|
||||
if (bitmapData == null || !bitmapData.equals(path)) {
|
||||
// Cancel previous task
|
||||
bitmapWorkerTask.cancel(true);
|
||||
} else {
|
||||
// The same work is already in progress
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// No task associated with the ImageView, or an existing task was cancelled
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,665 @@
|
|||
/*
|
||||
ChatRoomCreationFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactAddress;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.ContactsUpdatedListener;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.contacts.SearchContactViewHolder;
|
||||
import org.linphone.contacts.SearchContactsAdapter;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.ChatRoomBackend;
|
||||
import org.linphone.core.ChatRoomListenerStub;
|
||||
import org.linphone.core.ChatRoomParams;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.FriendCapability;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.SearchResult;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.views.ContactSelectView;
|
||||
|
||||
public class ChatRoomCreationFragment extends Fragment
|
||||
implements View.OnClickListener,
|
||||
SearchContactViewHolder.ClickListener,
|
||||
ContactsUpdatedListener {
|
||||
private RecyclerView mContactsList;
|
||||
private LinearLayout mContactsSelectedLayout;
|
||||
private HorizontalScrollView mContactsSelectLayout;
|
||||
private ImageView mAllContactsButton, mLinphoneContactsButton, mBackButton, mNextButton;
|
||||
private boolean mOnlyDisplayLinphoneContacts;
|
||||
private View mAllContactsSelected, mLinphoneContactsSelected;
|
||||
private RelativeLayout mSearchLayout, mWaitLayout, mLinphoneContactsToggle, mAllContactsToggle;
|
||||
private SearchView mSearchField;
|
||||
private ProgressBar mContactsFetchInProgress;
|
||||
private SearchContactsAdapter mSearchAdapter;
|
||||
private String mChatRoomSubject, mChatRoomAddress;
|
||||
private ChatRoom mChatRoom;
|
||||
private ChatRoomListenerStub mChatRoomCreationListener;
|
||||
private Bundle mShareInfos;
|
||||
private ImageView mSecurityToggleOff, mSecurityToggleOn;
|
||||
private Switch mSecurityToggle;
|
||||
private boolean mCreateGroupChatRoom;
|
||||
private boolean mChatRoomEncrypted;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.chat_create, container, false);
|
||||
|
||||
ArrayList<ContactAddress> selectedContacts = new ArrayList<>();
|
||||
mChatRoomSubject = null;
|
||||
mChatRoomAddress = null;
|
||||
mCreateGroupChatRoom = false;
|
||||
|
||||
if (getArguments() != null) {
|
||||
if (getArguments().getSerializable("selectedContacts") != null) {
|
||||
selectedContacts =
|
||||
(ArrayList<ContactAddress>)
|
||||
getArguments().getSerializable("selectedContacts");
|
||||
}
|
||||
mChatRoomSubject = getArguments().getString("subject");
|
||||
mChatRoomAddress = getArguments().getString("groupChatRoomAddress");
|
||||
mCreateGroupChatRoom = getArguments().getBoolean("createGroupChatRoom", false);
|
||||
mChatRoomEncrypted = getArguments().getBoolean("encrypted", false);
|
||||
}
|
||||
|
||||
mWaitLayout = view.findViewById(R.id.waitScreen);
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
|
||||
mContactsList = view.findViewById(R.id.contactsList);
|
||||
mContactsSelectedLayout = view.findViewById(R.id.contactsSelected);
|
||||
mContactsSelectLayout = view.findViewById(R.id.layoutContactsSelected);
|
||||
|
||||
mAllContactsButton = view.findViewById(R.id.all_contacts);
|
||||
mAllContactsButton.setOnClickListener(this);
|
||||
|
||||
mLinphoneContactsButton = view.findViewById(R.id.linphone_contacts);
|
||||
mLinphoneContactsButton.setOnClickListener(this);
|
||||
|
||||
mAllContactsSelected = view.findViewById(R.id.all_contacts_select);
|
||||
mLinphoneContactsSelected = view.findViewById(R.id.linphone_contacts_select);
|
||||
|
||||
mBackButton = view.findViewById(R.id.back);
|
||||
mBackButton.setOnClickListener(this);
|
||||
|
||||
mNextButton = view.findViewById(R.id.next);
|
||||
mNextButton.setOnClickListener(this);
|
||||
mNextButton.setEnabled(false);
|
||||
mSearchLayout = view.findViewById(R.id.layoutSearchField);
|
||||
|
||||
mContactsFetchInProgress = view.findViewById(R.id.contactsFetchInProgress);
|
||||
mContactsFetchInProgress.setVisibility(View.GONE);
|
||||
|
||||
mSearchAdapter = new SearchContactsAdapter(this, !mCreateGroupChatRoom, mChatRoomEncrypted);
|
||||
|
||||
mSearchField = view.findViewById(R.id.searchField);
|
||||
mSearchField.setOnQueryTextListener(
|
||||
new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
mSearchAdapter.searchContacts(newText);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mLinphoneContactsToggle = view.findViewById(R.id.layout_linphone_contacts);
|
||||
mAllContactsToggle = view.findViewById(R.id.layout_all_contacts);
|
||||
|
||||
mSecurityToggle = view.findViewById(R.id.security_toogle);
|
||||
mSecurityToggle.setOnCheckedChangeListener(
|
||||
new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
setSecurityEnabled(isChecked);
|
||||
}
|
||||
});
|
||||
mSecurityToggleOn = view.findViewById(R.id.security_toogle_on);
|
||||
mSecurityToggleOff = view.findViewById(R.id.security_toogle_off);
|
||||
mSecurityToggleOn.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setSecurityEnabled(true);
|
||||
}
|
||||
});
|
||||
mSecurityToggleOff.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setSecurityEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
mSecurityToggle.setChecked(mChatRoomEncrypted);
|
||||
mSearchAdapter.setSecurityEnabled(mChatRoomEncrypted);
|
||||
ProxyConfig lpc = LinphoneManager.getLc().getDefaultProxyConfig();
|
||||
if ((mChatRoomSubject != null && mChatRoomAddress != null)
|
||||
|| (lpc == null || lpc.getConferenceFactoryUri() == null)) {
|
||||
mSecurityToggle.setVisibility(View.GONE);
|
||||
mSecurityToggleOn.setVisibility(View.GONE);
|
||||
mSecurityToggleOff.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
LinearLayoutManager layoutManager =
|
||||
new LinearLayoutManager(getActivity().getApplicationContext());
|
||||
|
||||
mContactsList.setAdapter(mSearchAdapter);
|
||||
|
||||
DividerItemDecoration dividerItemDecoration =
|
||||
new DividerItemDecoration(
|
||||
mContactsList.getContext(), layoutManager.getOrientation());
|
||||
dividerItemDecoration.setDrawable(
|
||||
getActivity()
|
||||
.getApplicationContext()
|
||||
.getResources()
|
||||
.getDrawable(R.drawable.divider));
|
||||
mContactsList.addItemDecoration(dividerItemDecoration);
|
||||
|
||||
mContactsList.setLayoutManager(layoutManager);
|
||||
|
||||
if (savedInstanceState != null
|
||||
&& savedInstanceState.getStringArrayList("selectedContacts") != null) {
|
||||
mContactsSelectedLayout.removeAllViews();
|
||||
// We need to get all contacts not only sip
|
||||
selectedContacts =
|
||||
(ArrayList<ContactAddress>)
|
||||
savedInstanceState.getSerializable("selectedContacts");
|
||||
}
|
||||
|
||||
if (selectedContacts.size() != 0) {
|
||||
mSearchAdapter.setContactsSelectedList(selectedContacts);
|
||||
updateList();
|
||||
updateListSelected();
|
||||
}
|
||||
|
||||
mOnlyDisplayLinphoneContacts =
|
||||
ContactsManager.getInstance().isLinphoneContactsPrefered()
|
||||
|| getResources().getBoolean(R.bool.hide_non_linphone_contacts);
|
||||
if (savedInstanceState != null) {
|
||||
mOnlyDisplayLinphoneContacts =
|
||||
savedInstanceState.getBoolean("onlySipContact", mOnlyDisplayLinphoneContacts);
|
||||
}
|
||||
mSearchAdapter.setOnlySipContact(mOnlyDisplayLinphoneContacts);
|
||||
updateList();
|
||||
|
||||
displayChatCreation();
|
||||
|
||||
mChatRoomCreationListener =
|
||||
new ChatRoomListenerStub() {
|
||||
@Override
|
||||
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
|
||||
if (newState == ChatRoom.State.Created) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
cr.getLocalAddress().asStringUriOnly(),
|
||||
cr.getPeerAddress().asStringUriOnly(),
|
||||
mShareInfos);
|
||||
} else if (newState == ChatRoom.State.CreationFailed) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
LinphoneActivity.instance().displayChatRoomError();
|
||||
Log.e(
|
||||
"[Chat Room Creation] Group chat room for address "
|
||||
+ cr.getPeerAddress()
|
||||
+ " has failed !");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (getArguments() != null) {
|
||||
String fileSharedUri = getArguments().getString("fileSharedUri");
|
||||
String messageDraft = getArguments().getString("messageDraft");
|
||||
|
||||
if (fileSharedUri != null || messageDraft != null) {
|
||||
Log.i("[ChatRoomCreation] Forwarding arguments to new chat room");
|
||||
mShareInfos = new Bundle();
|
||||
}
|
||||
|
||||
if (fileSharedUri != null) {
|
||||
LinphoneActivity.instance().checkAndRequestPermissionsToSendImage();
|
||||
mShareInfos.putString("fileSharedUri", fileSharedUri);
|
||||
}
|
||||
|
||||
if (messageDraft != null) mShareInfos.putString("messageDraft", messageDraft);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
ContactsManager.getInstance().addContactsListener(this);
|
||||
super.onResume();
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CREATE_CHAT);
|
||||
}
|
||||
|
||||
InputMethodManager inputMethodManager =
|
||||
(InputMethodManager) getActivity().getSystemService(INPUT_METHOD_SERVICE);
|
||||
if (getActivity().getCurrentFocus() != null) {
|
||||
inputMethodManager.hideSoftInputFromWindow(
|
||||
getActivity().getCurrentFocus().getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.removeListener(mChatRoomCreationListener);
|
||||
}
|
||||
ContactsManager.getInstance().removeContactsListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void setSecurityEnabled(boolean enabled) {
|
||||
mChatRoomEncrypted = enabled;
|
||||
mSecurityToggle.setChecked(mChatRoomEncrypted);
|
||||
mSearchAdapter.setSecurityEnabled(mChatRoomEncrypted);
|
||||
|
||||
if (enabled) {
|
||||
// Remove all contacts added before LIME switch was set
|
||||
// and that can stay because they don't have the capability
|
||||
mContactsSelectedLayout.removeAllViews();
|
||||
List<ContactAddress> toToggle = new ArrayList<>();
|
||||
for (ContactAddress ca : mSearchAdapter.getContactsSelectedList()) {
|
||||
// If the ContactAddress doesn't have a contact keep it anyway
|
||||
if (ca.getContact() != null && !ca.hasCapability(FriendCapability.LimeX3Dh)) {
|
||||
toToggle.add(ca);
|
||||
} else {
|
||||
if (ca.getView() != null) {
|
||||
mContactsSelectedLayout.addView(ca.getView());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ContactAddress ca : toToggle) {
|
||||
mSearchAdapter.toggleContactSelection(ca);
|
||||
}
|
||||
mContactsSelectedLayout.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayChatCreation() {
|
||||
mNextButton.setVisibility(View.VISIBLE);
|
||||
mNextButton.setEnabled(mSearchAdapter.getContactsSelectedList().size() > 0);
|
||||
|
||||
mContactsList.setVisibility(View.VISIBLE);
|
||||
mSearchLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
if (mCreateGroupChatRoom) {
|
||||
mLinphoneContactsToggle.setVisibility(View.GONE);
|
||||
mAllContactsToggle.setVisibility(View.GONE);
|
||||
mContactsSelectLayout.setVisibility(View.VISIBLE);
|
||||
mNextButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mLinphoneContactsToggle.setVisibility(View.VISIBLE);
|
||||
mAllContactsToggle.setVisibility(View.VISIBLE);
|
||||
mContactsSelectLayout.setVisibility(View.GONE);
|
||||
mNextButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (getResources().getBoolean(R.bool.hide_non_linphone_contacts)) {
|
||||
mLinphoneContactsToggle.setVisibility(View.GONE);
|
||||
mLinphoneContactsButton.setVisibility(View.INVISIBLE);
|
||||
|
||||
mAllContactsButton.setEnabled(false);
|
||||
mLinphoneContactsButton.setEnabled(false);
|
||||
|
||||
mOnlyDisplayLinphoneContacts = true;
|
||||
|
||||
mAllContactsButton.setOnClickListener(null);
|
||||
mLinphoneContactsButton.setOnClickListener(null);
|
||||
|
||||
mLinphoneContactsSelected.setVisibility(View.INVISIBLE);
|
||||
mLinphoneContactsSelected.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mAllContactsButton.setVisibility(View.VISIBLE);
|
||||
mLinphoneContactsButton.setVisibility(View.VISIBLE);
|
||||
|
||||
if (mOnlyDisplayLinphoneContacts) {
|
||||
mAllContactsSelected.setVisibility(View.INVISIBLE);
|
||||
mLinphoneContactsSelected.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mAllContactsSelected.setVisibility(View.VISIBLE);
|
||||
mLinphoneContactsSelected.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
mAllContactsButton.setEnabled(mOnlyDisplayLinphoneContacts);
|
||||
mLinphoneContactsButton.setEnabled(!mAllContactsButton.isEnabled());
|
||||
}
|
||||
|
||||
mContactsSelectedLayout.removeAllViews();
|
||||
if (mSearchAdapter.getContactsSelectedList().size() > 0) {
|
||||
for (ContactAddress ca : mSearchAdapter.getContactsSelectedList()) {
|
||||
addSelectedContactAddress(ca);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateList() {
|
||||
mSearchAdapter.searchContacts(mSearchField.getQuery().toString());
|
||||
mSearchAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void updateListSelected() {
|
||||
if (mSearchAdapter.getContactsSelectedList().size() > 0) {
|
||||
mContactsSelectLayout.invalidate();
|
||||
mNextButton.setEnabled(true);
|
||||
} else {
|
||||
mNextButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetAndResearch() {
|
||||
ContactsManager.getInstance().getMagicSearch().resetSearchCache();
|
||||
mSearchAdapter.searchContacts(mSearchField.getQuery().toString());
|
||||
}
|
||||
|
||||
private void addSelectedContactAddress(ContactAddress ca) {
|
||||
View viewContact =
|
||||
LayoutInflater.from(LinphoneActivity.instance())
|
||||
.inflate(R.layout.contact_selected, null);
|
||||
if (ca.getContact() != null) {
|
||||
String name =
|
||||
(ca.getContact().getFullName() != null
|
||||
&& !ca.getContact().getFullName().isEmpty())
|
||||
? ca.getContact().getFullName()
|
||||
: (ca.getDisplayName() != null)
|
||||
? ca.getDisplayName()
|
||||
: (ca.getUsername() != null) ? ca.getUsername() : "";
|
||||
((TextView) viewContact.findViewById(R.id.sipUri)).setText(name);
|
||||
} else {
|
||||
((TextView) viewContact.findViewById(R.id.sipUri))
|
||||
.setText(ca.getAddressAsDisplayableString());
|
||||
}
|
||||
View removeContact = viewContact.findViewById(R.id.contactChatDelete);
|
||||
removeContact.setTag(ca);
|
||||
removeContact.setOnClickListener(this);
|
||||
viewContact.setOnClickListener(this);
|
||||
ca.setView(viewContact);
|
||||
mContactsSelectedLayout.addView(viewContact);
|
||||
mContactsSelectedLayout.invalidate();
|
||||
}
|
||||
|
||||
private void updateContactsClick(ContactAddress ca) {
|
||||
boolean isSelected = mSearchAdapter.toggleContactSelection(ca);
|
||||
if (isSelected) {
|
||||
ContactSelectView csv = new ContactSelectView(LinphoneActivity.instance());
|
||||
csv.setListener(this);
|
||||
csv.setContactName(ca);
|
||||
addSelectedContactAddress(ca);
|
||||
} else {
|
||||
mContactsSelectedLayout.removeAllViews();
|
||||
for (ContactAddress contactAddress : mSearchAdapter.getContactsSelectedList()) {
|
||||
if (contactAddress.getView() != null)
|
||||
mContactsSelectedLayout.addView(contactAddress.getView());
|
||||
}
|
||||
}
|
||||
mContactsSelectedLayout.invalidate();
|
||||
}
|
||||
|
||||
private void addOrRemoveContactFromSelection(ContactAddress ca) {
|
||||
updateContactsClick(ca);
|
||||
mSearchAdapter.notifyDataSetChanged();
|
||||
updateListSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
if (mSearchAdapter.getContactsSelectedList().size() > 0) {
|
||||
outState.putSerializable("selectedContacts", mSearchAdapter.getContactsSelectedList());
|
||||
}
|
||||
outState.putBoolean("onlySipContact", mOnlyDisplayLinphoneContacts);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int id = view.getId();
|
||||
if (id == R.id.all_contacts) {
|
||||
mOnlyDisplayLinphoneContacts = false;
|
||||
mSearchAdapter.setOnlySipContact(mOnlyDisplayLinphoneContacts);
|
||||
mAllContactsSelected.setVisibility(View.VISIBLE);
|
||||
mAllContactsButton.setEnabled(false);
|
||||
mLinphoneContactsButton.setEnabled(true);
|
||||
mLinphoneContactsSelected.setVisibility(View.INVISIBLE);
|
||||
updateList();
|
||||
resetAndResearch();
|
||||
} else if (id == R.id.linphone_contacts) {
|
||||
mSearchAdapter.setOnlySipContact(true);
|
||||
mLinphoneContactsSelected.setVisibility(View.VISIBLE);
|
||||
mLinphoneContactsButton.setEnabled(false);
|
||||
mOnlyDisplayLinphoneContacts = true;
|
||||
mAllContactsButton.setEnabled(mOnlyDisplayLinphoneContacts);
|
||||
mAllContactsSelected.setVisibility(View.INVISIBLE);
|
||||
updateList();
|
||||
resetAndResearch();
|
||||
} else if (id == R.id.back) {
|
||||
if (LinphoneActivity.instance().isTablet()) {
|
||||
LinphoneActivity.instance().goToChatList();
|
||||
} else {
|
||||
mContactsSelectedLayout.removeAllViews();
|
||||
LinphoneActivity.instance().popBackStack();
|
||||
}
|
||||
} else if (id == R.id.next) {
|
||||
if (mChatRoomAddress == null && mChatRoomSubject == null) {
|
||||
mContactsSelectedLayout.removeAllViews();
|
||||
LinphoneActivity.instance()
|
||||
.goToChatGroupInfos(
|
||||
null,
|
||||
mSearchAdapter.getContactsSelectedList(),
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
mShareInfos,
|
||||
mSecurityToggle.isChecked());
|
||||
} else {
|
||||
LinphoneActivity.instance()
|
||||
.goToChatGroupInfos(
|
||||
mChatRoomAddress,
|
||||
mSearchAdapter.getContactsSelectedList(),
|
||||
mChatRoomSubject,
|
||||
true,
|
||||
true,
|
||||
mShareInfos,
|
||||
mSecurityToggle.isChecked());
|
||||
}
|
||||
} else if (id == R.id.clearSearchField) {
|
||||
mSearchField.setQuery("", false);
|
||||
mSearchAdapter.searchContacts("");
|
||||
} else if (id == R.id.contactChatDelete) {
|
||||
ContactAddress ca = (ContactAddress) view.getTag();
|
||||
addOrRemoveContactFromSelection(ca);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClicked(int position) {
|
||||
SearchResult searchResult = mSearchAdapter.getContacts().get(position);
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
ProxyConfig lpc = lc.getDefaultProxyConfig();
|
||||
boolean createEncryptedChatRoom = mSecurityToggle.isChecked();
|
||||
|
||||
if (createEncryptedChatRoom && !searchResult.hasCapability(FriendCapability.LimeX3Dh)) {
|
||||
Log.w(
|
||||
"[Chat Room Creation] Contact "
|
||||
+ searchResult.getFriend()
|
||||
+ " doesn't have LIME X3DH capability !");
|
||||
return;
|
||||
} else if (mCreateGroupChatRoom
|
||||
&& !searchResult.hasCapability(FriendCapability.GroupChat)) {
|
||||
Log.w(
|
||||
"[Chat Room Creation] Contact "
|
||||
+ searchResult.getFriend()
|
||||
+ " doesn't have group chat capability !");
|
||||
return;
|
||||
}
|
||||
|
||||
if (lpc == null || lpc.getConferenceFactoryUri() == null || !mCreateGroupChatRoom) {
|
||||
Address address = searchResult.getAddress();
|
||||
if (address == null) {
|
||||
Log.w(
|
||||
"[Chat Room Creation] Using search result without an address, trying with phone number...");
|
||||
address = lc.interpretUrl(searchResult.getPhoneNumber());
|
||||
}
|
||||
if (address == null) {
|
||||
Log.e("[Chat Room Creation] Can't create a chat room without a valid address !");
|
||||
return;
|
||||
}
|
||||
if (lpc != null && lpc.getIdentityAddress().weakEqual(address)) {
|
||||
Log.e("[Chat Room Creation] Can't create a 1-to-1 chat room with myself !");
|
||||
return;
|
||||
}
|
||||
|
||||
if (createEncryptedChatRoom && lpc != null && lpc.getConferenceFactoryUri() != null) {
|
||||
mChatRoom = lc.findOneToOneChatRoom(lpc.getIdentityAddress(), address, true);
|
||||
if (mChatRoom != null) {
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
mChatRoom.getLocalAddress().asStringUriOnly(),
|
||||
mChatRoom.getPeerAddress().asStringUriOnly(),
|
||||
mShareInfos);
|
||||
} else {
|
||||
ChatRoomParams params = lc.createDefaultChatRoomParams();
|
||||
// This will set the backend to FlexisipChat automatically
|
||||
params.enableEncryption(true);
|
||||
params.enableGroup(false);
|
||||
|
||||
Address participants[] = new Address[1];
|
||||
participants[0] = address;
|
||||
|
||||
mChatRoom =
|
||||
lc.createChatRoom(
|
||||
params,
|
||||
getString(R.string.dummy_group_chat_subject),
|
||||
participants);
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.addListener(mChatRoomCreationListener);
|
||||
} else {
|
||||
Log.w("[Chat Room Creation Fragment] createChatRoom returned null...");
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lpc != null
|
||||
&& lpc.getConferenceFactoryUri() != null
|
||||
&& !LinphonePreferences.instance().useBasicChatRoomFor1To1()) {
|
||||
mChatRoom = lc.findOneToOneChatRoom(lpc.getIdentityAddress(), address, false);
|
||||
if (mChatRoom == null) {
|
||||
mWaitLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
ChatRoomParams params = lc.createDefaultChatRoomParams();
|
||||
params.enableEncryption(false);
|
||||
params.enableGroup(false);
|
||||
// We don't want a basic chat room
|
||||
params.setBackend(ChatRoomBackend.FlexisipChat);
|
||||
|
||||
Address participants[] = new Address[1];
|
||||
participants[0] = address;
|
||||
|
||||
mChatRoom =
|
||||
lc.createChatRoom(
|
||||
params,
|
||||
getString(R.string.dummy_group_chat_subject),
|
||||
participants);
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.addListener(mChatRoomCreationListener);
|
||||
} else {
|
||||
Log.w("[Chat Room Creation Fragment] createChatRoom returned null...");
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
mChatRoom.getLocalAddress().asStringUriOnly(),
|
||||
mChatRoom.getPeerAddress().asStringUriOnly(),
|
||||
mShareInfos);
|
||||
}
|
||||
} else {
|
||||
ChatRoom chatRoom = lc.getChatRoom(address);
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
chatRoom.getLocalAddress().asStringUriOnly(),
|
||||
chatRoom.getPeerAddress().asStringUriOnly(),
|
||||
mShareInfos);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LinphoneContact c =
|
||||
searchResult.getFriend() != null
|
||||
? (LinphoneContact) searchResult.getFriend().getUserData()
|
||||
: null;
|
||||
if (c == null) {
|
||||
c = ContactsManager.getInstance().findContactFromAddress(searchResult.getAddress());
|
||||
if (c == null) {
|
||||
c =
|
||||
ContactsManager.getInstance()
|
||||
.findContactFromPhoneNumber(searchResult.getPhoneNumber());
|
||||
}
|
||||
}
|
||||
addOrRemoveContactFromSelection(
|
||||
new ContactAddress(
|
||||
c,
|
||||
searchResult.getAddress().asStringUriOnly(),
|
||||
searchResult.getPhoneNumber(),
|
||||
searchResult.getFriend() != null));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactsUpdated() {
|
||||
updateList();
|
||||
}
|
||||
}
|
||||
198
app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java
Normal file
198
app/src/main/java/org/linphone/chat/ChatRoomViewHolder.java
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
ChatRoomViewHolder.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.ChatRoomCapabilities;
|
||||
import org.linphone.core.Content;
|
||||
import org.linphone.core.Participant;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ChatRoomViewHolder extends RecyclerView.ViewHolder
|
||||
implements View.OnClickListener, View.OnLongClickListener {
|
||||
public final TextView lastMessageView;
|
||||
public final TextView date;
|
||||
public final TextView displayName;
|
||||
public final TextView unreadMessages;
|
||||
public final CheckBox delete;
|
||||
public final RelativeLayout avatarLayout;
|
||||
|
||||
private final Context mContext;
|
||||
private final ClickListener mListener;
|
||||
|
||||
public ChatRoomViewHolder(Context context, View itemView, ClickListener listener) {
|
||||
super(itemView);
|
||||
|
||||
mContext = context;
|
||||
lastMessageView = itemView.findViewById(R.id.lastMessage);
|
||||
date = itemView.findViewById(R.id.date);
|
||||
displayName = itemView.findViewById(R.id.sipUri);
|
||||
unreadMessages = itemView.findViewById(R.id.unreadMessages);
|
||||
delete = itemView.findViewById(R.id.delete_chatroom);
|
||||
avatarLayout = itemView.findViewById(R.id.avatar_layout);
|
||||
mListener = listener;
|
||||
|
||||
itemView.setOnClickListener(this);
|
||||
itemView.setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
public void bindChatRoom(ChatRoom room) {
|
||||
ChatMessage lastMessage = room.getLastMessageInHistory();
|
||||
|
||||
if (lastMessage != null) {
|
||||
StringBuilder messageContent = new StringBuilder();
|
||||
for (Content c : lastMessage.getContents()) {
|
||||
if (c.isFile() || c.isFileTransfer()) {
|
||||
messageContent.append(c.getName()).append(" ");
|
||||
} else if (c.isText()) {
|
||||
messageContent.insert(0, c.getStringBuffer() + " ");
|
||||
}
|
||||
}
|
||||
lastMessageView.setText(getSender(room) + messageContent);
|
||||
date.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
mContext,
|
||||
room.getLastUpdateTime(),
|
||||
R.string.messages_list_date_format));
|
||||
} else {
|
||||
date.setText("");
|
||||
lastMessageView.setText("");
|
||||
}
|
||||
|
||||
displayName.setText(getContact(room));
|
||||
unreadMessages.setText(String.valueOf(room.getUnreadMessagesCount()));
|
||||
getAvatar(room);
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
if (mListener != null) {
|
||||
mListener.onItemClicked(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onLongClick(View v) {
|
||||
if (mListener != null) {
|
||||
return mListener.onItemLongClicked(getAdapterPosition());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getSender(ChatRoom mRoom) {
|
||||
if (mRoom.getLastMessageInHistory() != null) {
|
||||
LinphoneContact contact =
|
||||
ContactsManager.getInstance()
|
||||
.findContactFromAddress(
|
||||
mRoom.getLastMessageInHistory().getFromAddress());
|
||||
if (contact != null) {
|
||||
return (contact.getFullName() + mContext.getString(R.string.separator));
|
||||
}
|
||||
return (LinphoneUtils.getAddressDisplayName(
|
||||
mRoom.getLastMessageInHistory().getFromAddress())
|
||||
+ mContext.getString(R.string.separator));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getContact(ChatRoom mRoom) {
|
||||
Address contactAddress = mRoom.getPeerAddress();
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
|
||||
&& mRoom.getParticipants().length > 0) {
|
||||
contactAddress = mRoom.getParticipants()[0].getAddress();
|
||||
}
|
||||
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
|
||||
LinphoneContact contact;
|
||||
contact = ContactsManager.getInstance().findContactFromAddress(contactAddress);
|
||||
if (contact != null) {
|
||||
return contact.getFullName();
|
||||
}
|
||||
return LinphoneUtils.getAddressDisplayName(contactAddress);
|
||||
}
|
||||
return mRoom.getSubject();
|
||||
}
|
||||
|
||||
public void getAvatar(ChatRoom mRoom) {
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
|
||||
LinphoneContact contact = null;
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())) {
|
||||
contact =
|
||||
ContactsManager.getInstance()
|
||||
.findContactFromAddress(mRoom.getPeerAddress());
|
||||
} else {
|
||||
Participant[] participants = mRoom.getParticipants();
|
||||
if (participants != null && participants.length > 0) {
|
||||
contact =
|
||||
ContactsManager.getInstance()
|
||||
.findContactFromAddress(participants[0].getAddress());
|
||||
}
|
||||
}
|
||||
if (contact != null) {
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) {
|
||||
ContactAvatar.displayAvatar(contact, mRoom.getSecurityLevel(), avatarLayout);
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(contact, avatarLayout);
|
||||
}
|
||||
} else {
|
||||
Address remoteAddr = null;
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) {
|
||||
Participant[] participants = mRoom.getParticipants();
|
||||
if (participants.length > 0) {
|
||||
remoteAddr = participants[0].getAddress();
|
||||
} else {
|
||||
// TODO: error
|
||||
}
|
||||
} else {
|
||||
remoteAddr = mRoom.getPeerAddress();
|
||||
}
|
||||
|
||||
String username = LinphoneUtils.getAddressDisplayName(remoteAddr);
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) {
|
||||
ContactAvatar.displayAvatar(username, mRoom.getSecurityLevel(), avatarLayout);
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(username, avatarLayout);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) {
|
||||
ContactAvatar.displayGroupChatAvatar(mRoom.getSecurityLevel(), avatarLayout);
|
||||
} else {
|
||||
ContactAvatar.displayGroupChatAvatar(avatarLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ClickListener {
|
||||
void onItemClicked(int position);
|
||||
|
||||
boolean onItemLongClicked(int position);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,25 +23,29 @@ import android.content.Context;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.ui.SelectableAdapter;
|
||||
import org.linphone.ui.SelectableHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.utils.SelectableAdapter;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
|
||||
public class ChatRoomsAdapter extends SelectableAdapter<ChatRoomViewHolder> {
|
||||
private Context mContext;
|
||||
public List<ChatRoom> mRooms;
|
||||
private int mItemResource;
|
||||
private ChatRoomViewHolder.ClickListener mClickListener;
|
||||
private final Context mContext;
|
||||
private List<ChatRoom> mRooms;
|
||||
private final int mItemResource;
|
||||
private final ChatRoomViewHolder.ClickListener mClickListener;
|
||||
|
||||
public ChatRoomsAdapter(Context context, int itemResource, List<ChatRoom> rooms, ChatRoomViewHolder.ClickListener clickListener, SelectableHelper helper) {
|
||||
public ChatRoomsAdapter(
|
||||
Context context,
|
||||
int itemResource,
|
||||
List<ChatRoom> rooms,
|
||||
ChatRoomViewHolder.ClickListener clickListener,
|
||||
SelectableHelper helper) {
|
||||
super(helper);
|
||||
mClickListener = clickListener;
|
||||
mRooms = rooms;
|
||||
|
|
@ -51,8 +55,7 @@ public class ChatRoomsAdapter extends SelectableAdapter<ChatRoomViewHolder> {
|
|||
|
||||
@Override
|
||||
public ChatRoomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(mItemResource, parent, false);
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(mItemResource, parent, false);
|
||||
return new ChatRoomViewHolder(mContext, view, mClickListener);
|
||||
}
|
||||
|
||||
|
|
@ -60,21 +63,33 @@ public class ChatRoomsAdapter extends SelectableAdapter<ChatRoomViewHolder> {
|
|||
public void onBindViewHolder(ChatRoomViewHolder holder, int position) {
|
||||
ChatRoom room = mRooms.get(position);
|
||||
holder.delete.setVisibility(isEditionEnabled() ? View.VISIBLE : View.INVISIBLE);
|
||||
holder.unreadMessages.setVisibility(isEditionEnabled() ? View.INVISIBLE : (room.getUnreadMessagesCount() > 0 ? View.VISIBLE : View.INVISIBLE));
|
||||
holder.unreadMessages.setVisibility(
|
||||
isEditionEnabled()
|
||||
? View.INVISIBLE
|
||||
: (room.getUnreadMessagesCount() > 0 ? View.VISIBLE : View.INVISIBLE));
|
||||
holder.delete.setChecked(isSelected(position));
|
||||
holder.bindChatRoom(room);
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
mRooms = new ArrayList<>(Arrays.asList(LinphoneManager.getLc().getChatRooms()));
|
||||
Collections.sort(mRooms, new Comparator<ChatRoom>() {
|
||||
public int compare(ChatRoom cr1, ChatRoom cr2) {
|
||||
long timeDiff = cr1.getLastUpdateTime() - cr2.getLastUpdateTime();
|
||||
if (timeDiff > 0) return -1;
|
||||
else if (timeDiff == 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
ChatRoom[] rooms = LinphoneManager.getLc().getChatRooms();
|
||||
if (mContext.getResources().getBoolean(R.bool.hide_empty_one_to_one_chat_rooms)) {
|
||||
mRooms = LinphoneUtils.removeEmptyOneToOneChatRooms(rooms);
|
||||
} else {
|
||||
mRooms = Arrays.asList(rooms);
|
||||
}
|
||||
|
||||
Collections.sort(
|
||||
mRooms,
|
||||
new Comparator<ChatRoom>() {
|
||||
public int compare(ChatRoom cr1, ChatRoom cr2) {
|
||||
long timeDiff = cr1.getLastUpdateTime() - cr2.getLastUpdateTime();
|
||||
if (timeDiff > 0) return -1;
|
||||
else if (timeDiff == 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
|
@ -83,10 +98,7 @@ public class ChatRoomsAdapter extends SelectableAdapter<ChatRoomViewHolder> {
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter's methods
|
||||
*/
|
||||
|
||||
/** Adapter's methods */
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mRooms.size();
|
||||
345
app/src/main/java/org/linphone/chat/ChatRoomsFragment.java
Normal file
345
app/src/main/java/org/linphone/chat/ChatRoomsFragment.java
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
ChatRoomsFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import static org.linphone.fragments.FragmentsAvailable.CHAT_LIST;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.ContactsUpdatedListener;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.ChatRoomListenerStub;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.EventLog;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
|
||||
public class ChatRoomsFragment extends Fragment
|
||||
implements ContactsUpdatedListener,
|
||||
ChatRoomViewHolder.ClickListener,
|
||||
SelectableHelper.DeleteListener {
|
||||
|
||||
private RecyclerView mChatRoomsList;
|
||||
private ImageView mNewDiscussionButton, mNewGroupDiscussionButton, mBackToCallButton;
|
||||
private ChatRoomsAdapter mChatRoomsAdapter;
|
||||
private CoreListenerStub mListener;
|
||||
private RelativeLayout mWaitLayout;
|
||||
private int mChatRoomDeletionPendingCount;
|
||||
private ChatRoomListenerStub mChatRoomListener;
|
||||
private Context mContext;
|
||||
private List<ChatRoom> mRooms;
|
||||
private SelectableHelper mSelectionHelper;
|
||||
private TextView mNoChatHistory;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mContext = getActivity().getApplicationContext();
|
||||
View view = inflater.inflate(R.layout.chatlist, container, false);
|
||||
|
||||
mChatRoomsList = view.findViewById(R.id.chatList);
|
||||
mWaitLayout = view.findViewById(R.id.waitScreen);
|
||||
mNewDiscussionButton = view.findViewById(R.id.new_discussion);
|
||||
mNewGroupDiscussionButton = view.findViewById(R.id.new_group_discussion);
|
||||
mBackToCallButton = view.findViewById(R.id.back_in_call);
|
||||
mNoChatHistory = view.findViewById(R.id.noChatHistory);
|
||||
|
||||
ChatRoom[] rooms = LinphoneManager.getLc().getChatRooms();
|
||||
if (mContext.getResources().getBoolean(R.bool.hide_empty_one_to_one_chat_rooms)) {
|
||||
mRooms = LinphoneUtils.removeEmptyOneToOneChatRooms(rooms);
|
||||
} else {
|
||||
mRooms = Arrays.asList(rooms);
|
||||
}
|
||||
|
||||
mSelectionHelper = new SelectableHelper(view, this);
|
||||
mChatRoomsAdapter =
|
||||
new ChatRoomsAdapter(
|
||||
mContext, R.layout.chatlist_cell, mRooms, this, mSelectionHelper);
|
||||
|
||||
mChatRoomsList.setAdapter(mChatRoomsAdapter);
|
||||
mSelectionHelper.setAdapter(mChatRoomsAdapter);
|
||||
mSelectionHelper.setDialogMessage(R.string.chat_room_delete_dialog);
|
||||
|
||||
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(mContext);
|
||||
mChatRoomsList.setLayoutManager(layoutManager);
|
||||
|
||||
DividerItemDecoration dividerItemDecoration =
|
||||
new DividerItemDecoration(
|
||||
mChatRoomsList.getContext(),
|
||||
((LinearLayoutManager) layoutManager).getOrientation());
|
||||
dividerItemDecoration.setDrawable(
|
||||
getActivity()
|
||||
.getApplicationContext()
|
||||
.getResources()
|
||||
.getDrawable(R.drawable.divider));
|
||||
mChatRoomsList.addItemDecoration(dividerItemDecoration);
|
||||
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
|
||||
mNewDiscussionButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Bundle extras = null;
|
||||
if (getArguments() != null) {
|
||||
Log.i("[ChatRooms] Forwarding arguments to new chat room");
|
||||
extras = (Bundle) getArguments().clone();
|
||||
getArguments().clear();
|
||||
}
|
||||
LinphoneActivity.instance()
|
||||
.goToChatCreator(null, null, null, false, extras, false, false);
|
||||
}
|
||||
});
|
||||
|
||||
mNewGroupDiscussionButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Bundle extras = null;
|
||||
if (getArguments() != null) {
|
||||
Log.i("[ChatRooms] Forwarding arguments to new group chat room");
|
||||
extras = (Bundle) getArguments().clone();
|
||||
getArguments().clear();
|
||||
}
|
||||
LinphoneActivity.instance()
|
||||
.goToChatCreator(null, null, null, false, extras, true, false);
|
||||
}
|
||||
});
|
||||
|
||||
mBackToCallButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LinphoneActivity.instance()
|
||||
.resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
|
||||
}
|
||||
});
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onMessageReceived(Core lc, ChatRoom cr, ChatMessage message) {
|
||||
refreshChatRoomsList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChatRoomStateChanged(Core lc, ChatRoom cr, ChatRoom.State state) {
|
||||
if (state == ChatRoom.State.Created) {
|
||||
refreshChatRoomsList();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mChatRoomListener =
|
||||
new ChatRoomListenerStub() {
|
||||
@Override
|
||||
public void onStateChanged(ChatRoom room, ChatRoom.State state) {
|
||||
super.onStateChanged(room, state);
|
||||
if (state == ChatRoom.State.Deleted
|
||||
|| state == ChatRoom.State.TerminationFailed) {
|
||||
mChatRoomDeletionPendingCount -= 1;
|
||||
|
||||
if (state == ChatRoom.State.TerminationFailed) {
|
||||
// TODO error message
|
||||
}
|
||||
|
||||
if (mChatRoomDeletionPendingCount == 0) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
refreshChatRoomsList();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (getArguments() != null) {
|
||||
String fileSharedUri = getArguments().getString("fileSharedUri");
|
||||
String messageSharedUri = getArguments().getString("messageDraft");
|
||||
if (fileSharedUri != null || messageSharedUri != null) {
|
||||
Toast.makeText(
|
||||
LinphoneActivity.instance(),
|
||||
R.string.toast_choose_chat_room_for_sharing,
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
Log.i("[ChatRooms] Arguments found: " + messageSharedUri + " / " + fileSharedUri);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClicked(int position) {
|
||||
if (mChatRoomsAdapter.isEditionEnabled()) {
|
||||
mChatRoomsAdapter.toggleSelection(position);
|
||||
} else {
|
||||
ChatRoom room = (ChatRoom) mChatRoomsAdapter.getItem(position);
|
||||
Bundle extras = null;
|
||||
if (getArguments() != null) {
|
||||
Log.i("[ChatRooms] Forwarding arguments to existing chat room");
|
||||
extras = (Bundle) getArguments().clone();
|
||||
getArguments().clear();
|
||||
}
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
room.getLocalAddress().asStringUriOnly(),
|
||||
room.getPeerAddress().asString(),
|
||||
extras);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClicked(int position) {
|
||||
if (!mChatRoomsAdapter.isEditionEnabled()) {
|
||||
mSelectionHelper.enterEditionMode();
|
||||
}
|
||||
mChatRoomsAdapter.toggleSelection(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void refreshChatRoomsList() {
|
||||
mChatRoomsAdapter.refresh();
|
||||
mNoChatHistory.setVisibility(
|
||||
mChatRoomsAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void displayFirstChat() {
|
||||
ChatRoomsAdapter adapter = (ChatRoomsAdapter) mChatRoomsList.getAdapter();
|
||||
if (adapter != null && adapter.getItemCount() > 0) {
|
||||
ChatRoom room = (ChatRoom) adapter.getItem(0);
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
room.getLocalAddress().asStringUriOnly(),
|
||||
room.getPeerAddress().asStringUriOnly(),
|
||||
null);
|
||||
} else {
|
||||
LinphoneActivity.instance().displayEmptyFragment();
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
if (mChatRoomsAdapter != null) {
|
||||
mChatRoomsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
ContactsManager.getInstance().addContactsListener(this);
|
||||
|
||||
if (LinphoneManager.getLc().getCallsNb() > 0) {
|
||||
mBackToCallButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mBackToCallButton.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CHAT_LIST);
|
||||
}
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
|
||||
refreshChatRoomsList();
|
||||
|
||||
ProxyConfig lpc = lc.getDefaultProxyConfig();
|
||||
mNewGroupDiscussionButton.setVisibility(
|
||||
(lpc != null && lpc.getConferenceFactoryUri() != null) ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
ContactsManager.getInstance().removeContactsListener(this);
|
||||
mChatRoomsAdapter.clear();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteSelection(Object[] objectsToDelete) {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
mChatRoomDeletionPendingCount = objectsToDelete.length;
|
||||
for (Object obj : objectsToDelete) {
|
||||
ChatRoom room = (ChatRoom) obj;
|
||||
|
||||
for (EventLog eventLog : room.getHistoryEvents(0)) {
|
||||
if (eventLog.getType() == EventLog.Type.ConferenceChatMessage) {
|
||||
ChatMessage message = eventLog.getChatMessage();
|
||||
if (message.getAppdata() != null && !message.isOutgoing()) {
|
||||
File file = new File(message.getAppdata());
|
||||
if (file.exists()) {
|
||||
file.delete(); // Delete downloaded file from incoming message that
|
||||
// will be deleted
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
room.addListener(mChatRoomListener);
|
||||
lc.deleteChatRoom(room);
|
||||
}
|
||||
if (mChatRoomDeletionPendingCount > 0) {
|
||||
mWaitLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
LinphoneActivity.instance()
|
||||
.displayMissedChats(LinphoneManager.getInstance().getUnreadMessageCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactsUpdated() {
|
||||
if (!LinphoneActivity.isInstanciated()
|
||||
|| LinphoneActivity.instance().getCurrentFragment() != CHAT_LIST) return;
|
||||
|
||||
ChatRoomsAdapter adapter = (ChatRoomsAdapter) mChatRoomsList.getAdapter();
|
||||
if (adapter != null) {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
78
app/src/main/java/org/linphone/chat/ChatScrollListener.java
Normal file
78
app/src/main/java/org/linphone/chat/ChatScrollListener.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
ChatScrollListener.java
|
||||
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
abstract class ChatScrollListener extends RecyclerView.OnScrollListener {
|
||||
// The minimum amount of items to have below your current scroll position
|
||||
// before mLoading more.
|
||||
private final int mVisibleThreshold = 5;
|
||||
// The total number of items in the dataset after the last load
|
||||
private int mPreviousTotalItemCount = 0;
|
||||
// True if we are still waiting for the last set of data to load.
|
||||
private boolean mLoading = true;
|
||||
|
||||
private final LinearLayoutManager mLayoutManager;
|
||||
|
||||
public ChatScrollListener(LinearLayoutManager layoutManager) {
|
||||
mLayoutManager = layoutManager;
|
||||
}
|
||||
|
||||
// This happens many times a second during a scroll, so be wary of the code you place here.
|
||||
// We are given a few useful parameters to help us work out if we need to load some more data,
|
||||
// but first we check if we are waiting for the previous load to finish.
|
||||
@Override
|
||||
public void onScrolled(RecyclerView view, int dx, int dy) {
|
||||
int lastVisibleItemPosition;
|
||||
int totalItemCount = mLayoutManager.getItemCount();
|
||||
|
||||
lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
|
||||
|
||||
// If the total item count is zero and the previous isn't, assume the
|
||||
// list is invalidated and should be reset back to initial state
|
||||
if (totalItemCount < mPreviousTotalItemCount) {
|
||||
this.mPreviousTotalItemCount = totalItemCount;
|
||||
if (totalItemCount == 0) {
|
||||
this.mLoading = true;
|
||||
}
|
||||
}
|
||||
// If it’s still mLoading, we check to see if the dataset count has
|
||||
// changed, if so we conclude it has finished mLoading and update the current page
|
||||
// number and total item count.
|
||||
if (mLoading && (totalItemCount > mPreviousTotalItemCount)) {
|
||||
mLoading = false;
|
||||
mPreviousTotalItemCount = totalItemCount;
|
||||
}
|
||||
|
||||
// If it isn’t currently mLoading, we check to see if we have breached
|
||||
// the mVisibleThreshold and need to reload more data.
|
||||
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
|
||||
// threshold should reflect how many total columns there are too
|
||||
if (!mLoading && (lastVisibleItemPosition + mVisibleThreshold) > totalItemCount) {
|
||||
onLoadMore(totalItemCount);
|
||||
mLoading = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Defines the process for actually mLoading more data based on page
|
||||
protected abstract void onLoadMore(int totalItemsCount);
|
||||
}
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
package org.linphone.compatibility;
|
||||
package org.linphone.chat;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
|
||||
/*ApiNineteenPlus.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
/*
|
||||
DeviceChildViewHolder.java
|
||||
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
|
|
@ -22,13 +19,17 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
@TargetApi(19)
|
||||
public class ApiNineteenPlus {
|
||||
public static void scheduleAlarm(AlarmManager alarmManager, int type, long triggerAtMillis, PendingIntent operation) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
||||
alarmManager.setExact(type, triggerAtMillis, operation);
|
||||
} else {
|
||||
alarmManager.set(type, triggerAtMillis, operation);
|
||||
}
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import org.linphone.R;
|
||||
|
||||
class DeviceChildViewHolder {
|
||||
public final TextView deviceName;
|
||||
public final ImageView securityLevel;
|
||||
|
||||
public DeviceChildViewHolder(View v) {
|
||||
deviceName = v.findViewById(R.id.name);
|
||||
securityLevel = v.findViewById(R.id.security_level);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
DeviceGroupViewHolder.java
|
||||
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import org.linphone.R;
|
||||
|
||||
class DeviceGroupViewHolder {
|
||||
public final RelativeLayout avatarLayout;
|
||||
public final TextView participantName, sipUri;
|
||||
public final ImageView groupExpander, securityLevel;
|
||||
|
||||
public DeviceGroupViewHolder(View v) {
|
||||
avatarLayout = v.findViewById(R.id.avatar_layout);
|
||||
participantName = v.findViewById(R.id.name);
|
||||
sipUri = v.findViewById(R.id.sipUri);
|
||||
groupExpander = v.findViewById(R.id.dropdown);
|
||||
securityLevel = v.findViewById(R.id.device_security_level);
|
||||
}
|
||||
}
|
||||
249
app/src/main/java/org/linphone/chat/DevicesAdapter.java
Normal file
249
app/src/main/java/org/linphone/chat/DevicesAdapter.java
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
DevicesAdapter.java
|
||||
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseExpandableListAdapter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatRoomSecurityLevel;
|
||||
import org.linphone.core.Participant;
|
||||
import org.linphone.core.ParticipantDevice;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
class DevicesAdapter extends BaseExpandableListAdapter {
|
||||
private final Context mContext;
|
||||
private List<Participant> mParticipants;
|
||||
private boolean mOnlyDisplayChildsAsGroups;
|
||||
|
||||
public DevicesAdapter(Context context) {
|
||||
mContext = context;
|
||||
mParticipants = new ArrayList<>();
|
||||
mOnlyDisplayChildsAsGroups = false;
|
||||
}
|
||||
|
||||
public void updateListItems(List<Participant> participants, boolean childsAsGroups) {
|
||||
mOnlyDisplayChildsAsGroups = childsAsGroups;
|
||||
mParticipants = participants;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getGroupView(
|
||||
int groupPosition, boolean isExpanded, View view, ViewGroup viewGroup) {
|
||||
if (mOnlyDisplayChildsAsGroups) {
|
||||
ParticipantDevice device = (ParticipantDevice) getGroup(groupPosition);
|
||||
|
||||
DeviceChildViewHolder holder = null;
|
||||
if (view != null) {
|
||||
Object possibleHolder = view.getTag();
|
||||
if (possibleHolder instanceof DeviceChildViewHolder) {
|
||||
holder = (DeviceChildViewHolder) possibleHolder;
|
||||
}
|
||||
} else {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
view = inflater.inflate(R.layout.chat_device_cell_as_group, viewGroup, false);
|
||||
}
|
||||
if (holder == null) {
|
||||
holder = new DeviceChildViewHolder(view);
|
||||
view.setTag(holder);
|
||||
}
|
||||
|
||||
holder.deviceName.setText(device.getName());
|
||||
|
||||
ChatRoomSecurityLevel level = device.getSecurityLevel();
|
||||
switch (level) {
|
||||
case Safe:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_2_indicator);
|
||||
break;
|
||||
case Encrypted:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_1_indicator);
|
||||
break;
|
||||
case ClearText:
|
||||
case Unsafe:
|
||||
default:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_alert_indicator);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Participant participant = (Participant) getGroup(groupPosition);
|
||||
|
||||
DeviceGroupViewHolder holder = null;
|
||||
if (view != null) {
|
||||
Object possibleHolder = view.getTag();
|
||||
if (possibleHolder instanceof DeviceGroupViewHolder) {
|
||||
holder = (DeviceGroupViewHolder) possibleHolder;
|
||||
}
|
||||
} else {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
view = inflater.inflate(R.layout.chat_device_group, viewGroup, false);
|
||||
}
|
||||
if (holder == null) {
|
||||
holder = new DeviceGroupViewHolder(view);
|
||||
view.setTag(holder);
|
||||
}
|
||||
|
||||
Address participantAddress = participant.getAddress();
|
||||
LinphoneContact contact =
|
||||
ContactsManager.getInstance().findContactFromAddress(participantAddress);
|
||||
if (contact != null) {
|
||||
ContactAvatar.displayAvatar(
|
||||
contact, participant.getSecurityLevel(), holder.avatarLayout);
|
||||
holder.participantName.setText(contact.getFullName());
|
||||
} else {
|
||||
String displayName = LinphoneUtils.getAddressDisplayName(participantAddress);
|
||||
ContactAvatar.displayAvatar(
|
||||
displayName, participant.getSecurityLevel(), holder.avatarLayout);
|
||||
holder.participantName.setText(displayName);
|
||||
}
|
||||
|
||||
holder.sipUri.setText(participantAddress.asStringUriOnly());
|
||||
if (!mContext.getResources().getBoolean(R.bool.show_sip_uri_in_chat)) {
|
||||
holder.sipUri.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (getChildrenCount(groupPosition) == 1) {
|
||||
holder.securityLevel.setVisibility(View.VISIBLE);
|
||||
holder.groupExpander.setVisibility(View.GONE);
|
||||
|
||||
ParticipantDevice device = (ParticipantDevice) getChild(groupPosition, 0);
|
||||
ChatRoomSecurityLevel level = device.getSecurityLevel();
|
||||
switch (level) {
|
||||
case Safe:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_2_indicator);
|
||||
break;
|
||||
case Encrypted:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_1_indicator);
|
||||
break;
|
||||
case ClearText:
|
||||
case Unsafe:
|
||||
default:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_alert_indicator);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
holder.securityLevel.setVisibility(View.GONE);
|
||||
holder.groupExpander.setVisibility(View.VISIBLE);
|
||||
holder.groupExpander.setImageResource(
|
||||
isExpanded ? R.drawable.chevron_list_open : R.drawable.chevron_list_close);
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildView(
|
||||
int groupPosition, int childPosition, boolean b, View view, ViewGroup viewGroup) {
|
||||
ParticipantDevice device = (ParticipantDevice) getChild(groupPosition, childPosition);
|
||||
|
||||
DeviceChildViewHolder holder = null;
|
||||
if (view != null) {
|
||||
Object possibleHolder = view.getTag();
|
||||
if (possibleHolder instanceof DeviceChildViewHolder) {
|
||||
holder = (DeviceChildViewHolder) possibleHolder;
|
||||
}
|
||||
} else {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
view = inflater.inflate(R.layout.chat_device_cell, viewGroup, false);
|
||||
}
|
||||
if (holder == null) {
|
||||
holder = new DeviceChildViewHolder(view);
|
||||
view.setTag(holder);
|
||||
}
|
||||
|
||||
holder.deviceName.setText(device.getName());
|
||||
|
||||
ChatRoomSecurityLevel level = device.getSecurityLevel();
|
||||
switch (level) {
|
||||
case Safe:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_2_indicator);
|
||||
break;
|
||||
case Encrypted:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_1_indicator);
|
||||
break;
|
||||
case ClearText:
|
||||
case Unsafe:
|
||||
default:
|
||||
holder.securityLevel.setImageResource(R.drawable.security_alert_indicator);
|
||||
break;
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupCount() {
|
||||
if (mParticipants.size() == 0) return 0;
|
||||
return mOnlyDisplayChildsAsGroups
|
||||
? mParticipants.get(0).getDevices().length
|
||||
: mParticipants.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildrenCount(int groupPosition) {
|
||||
if (mParticipants.size() == 0) return 0;
|
||||
return mOnlyDisplayChildsAsGroups
|
||||
? 0
|
||||
: mParticipants.get(groupPosition).getDevices().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGroup(int groupPosition) {
|
||||
if (mParticipants.size() == 0) return null;
|
||||
return mOnlyDisplayChildsAsGroups
|
||||
? mParticipants.get(0).getDevices()[groupPosition]
|
||||
: mParticipants.get(groupPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChild(int groupPosition, int childPosition) {
|
||||
if (mParticipants.size() == 0) return null;
|
||||
return mParticipants.get(groupPosition).getDevices()[childPosition];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGroupId(int groupPosition) {
|
||||
return groupPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getChildId(int groupPosition, int childPosition) {
|
||||
return childPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
187
app/src/main/java/org/linphone/chat/DevicesFragment.java
Normal file
187
app/src/main/java/org/linphone/chat/DevicesFragment.java
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
DevicesFragment.java
|
||||
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.call.CallManager;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.ChatRoomCapabilities;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.ParticipantDevice;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
public class DevicesFragment extends Fragment {
|
||||
private LayoutInflater mInflater;
|
||||
private ImageView mBackButton;
|
||||
private TextView mTitle;
|
||||
private ExpandableListView mExpandableList;
|
||||
private DevicesAdapter mAdapter;
|
||||
|
||||
private String mLocalSipUri, mRoomUri;
|
||||
private Address mLocalSipAddr, mRoomAddr;
|
||||
private ChatRoom mRoom;
|
||||
private boolean mOnlyDisplayChilds;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getArguments() != null) {
|
||||
mLocalSipUri = getArguments().getString("LocalSipUri");
|
||||
mLocalSipAddr = LinphoneManager.getLc().createAddress(mLocalSipUri);
|
||||
mRoomUri = getArguments().getString("RemoteSipUri");
|
||||
mRoomAddr = LinphoneManager.getLc().createAddress(mRoomUri);
|
||||
}
|
||||
|
||||
mInflater = inflater;
|
||||
View view = mInflater.inflate(R.layout.chat_devices, container, false);
|
||||
|
||||
mOnlyDisplayChilds = false;
|
||||
|
||||
mExpandableList = view.findViewById(R.id.devices_list);
|
||||
mExpandableList.setOnChildClickListener(
|
||||
new ExpandableListView.OnChildClickListener() {
|
||||
@Override
|
||||
public boolean onChildClick(
|
||||
ExpandableListView expandableListView,
|
||||
View view,
|
||||
int groupPosition,
|
||||
int childPosition,
|
||||
long l) {
|
||||
ParticipantDevice device =
|
||||
(ParticipantDevice) mAdapter.getChild(groupPosition, childPosition);
|
||||
CallManager.getInstance().inviteAddress(device.getAddress(), true);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
mExpandableList.setOnGroupClickListener(
|
||||
new ExpandableListView.OnGroupClickListener() {
|
||||
@Override
|
||||
public boolean onGroupClick(
|
||||
ExpandableListView expandableListView,
|
||||
View view,
|
||||
int groupPosition,
|
||||
long l) {
|
||||
if (mOnlyDisplayChilds) {
|
||||
// in this case groups are childs, so call on click
|
||||
ParticipantDevice device =
|
||||
(ParticipantDevice) mAdapter.getGroup(groupPosition);
|
||||
CallManager.getInstance().inviteAddress(device.getAddress(), true);
|
||||
return true;
|
||||
} else {
|
||||
if (mAdapter.getChildrenCount(groupPosition) == 1) {
|
||||
ParticipantDevice device =
|
||||
(ParticipantDevice) mAdapter.getChild(groupPosition, 0);
|
||||
CallManager.getInstance().inviteAddress(device.getAddress(), true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
initChatRoom();
|
||||
|
||||
mTitle = view.findViewById(R.id.title);
|
||||
initHeader();
|
||||
|
||||
mBackButton = view.findViewById(R.id.back);
|
||||
mBackButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (LinphoneActivity.instance().isTablet()) {
|
||||
LinphoneActivity.instance().goToChat(mLocalSipUri, mRoomUri, null);
|
||||
} else {
|
||||
LinphoneActivity.instance().onBackPressed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CONTACT_DEVICES);
|
||||
}
|
||||
|
||||
initValues();
|
||||
}
|
||||
|
||||
private void initChatRoom() {
|
||||
Core core = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
mRoom = core.getChatRoom(mRoomAddr, mLocalSipAddr);
|
||||
}
|
||||
|
||||
private void initHeader() {
|
||||
if (mRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
|
||||
Address remoteParticipantAddr = mRoomAddr;
|
||||
if (mRoom.getParticipants().length > 0) {
|
||||
remoteParticipantAddr = mRoom.getParticipants()[0].getAddress();
|
||||
}
|
||||
LinphoneContact c =
|
||||
ContactsManager.getInstance().findContactFromAddress(remoteParticipantAddr);
|
||||
String displayName;
|
||||
if (c != null) {
|
||||
displayName = c.getFullName();
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(remoteParticipantAddr);
|
||||
}
|
||||
mTitle.setText(getString(R.string.chat_room_devices).replace("%s", displayName));
|
||||
}
|
||||
}
|
||||
|
||||
private void initValues() {
|
||||
if (mAdapter == null) {
|
||||
mAdapter = new DevicesAdapter(getActivity());
|
||||
mExpandableList.setAdapter(mAdapter);
|
||||
}
|
||||
if (mRoom == null) {
|
||||
initChatRoom();
|
||||
}
|
||||
|
||||
if (mRoom != null && mRoom.getNbParticipants() > 0) {
|
||||
mOnlyDisplayChilds = mRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt());
|
||||
mAdapter.updateListItems(Arrays.asList(mRoom.getParticipants()), mOnlyDisplayChilds);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
app/src/main/java/org/linphone/chat/GroupInfoAdapter.java
Normal file
178
app/src/main/java/org/linphone/chat/GroupInfoAdapter.java
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
GroupInfoAdapter.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactAddress;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.Participant;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
class GroupInfoAdapter extends RecyclerView.Adapter<GroupInfoViewHolder> {
|
||||
private List<ContactAddress> mItems;
|
||||
private View.OnClickListener mDeleteListener;
|
||||
private boolean mHideAdminFeatures;
|
||||
private ChatRoom mChatRoom;
|
||||
|
||||
public GroupInfoAdapter(
|
||||
List<ContactAddress> items, boolean hideAdminFeatures, boolean isCreation) {
|
||||
mItems = items;
|
||||
mHideAdminFeatures = hideAdminFeatures || isCreation;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GroupInfoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.chat_infos_cell, parent, false);
|
||||
return new GroupInfoViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final GroupInfoViewHolder holder, int position) {
|
||||
final ContactAddress ca = (ContactAddress) getItem(position);
|
||||
LinphoneContact c = ca.getContact();
|
||||
|
||||
holder.name.setText(
|
||||
(c != null && c.getFullName() != null)
|
||||
? c.getFullName()
|
||||
: (ca.getDisplayName() != null) ? ca.getDisplayName() : ca.getUsername());
|
||||
|
||||
if (c != null) {
|
||||
ContactAvatar.displayAvatar(c, holder.avatarLayout);
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(holder.name.getText().toString(), holder.avatarLayout);
|
||||
}
|
||||
|
||||
holder.sipUri.setText(ca.getAddressAsDisplayableString());
|
||||
|
||||
if (!LinphoneActivity.instance().getResources().getBoolean(R.bool.show_sip_uri_in_chat)) {
|
||||
holder.sipUri.setVisibility(View.GONE);
|
||||
holder.name.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
holder.sipUri.setVisibility(
|
||||
holder.sipUri.getVisibility() == View.VISIBLE
|
||||
? View.GONE
|
||||
: View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
holder.delete.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mDeleteListener != null) {
|
||||
mDeleteListener.onClick(view);
|
||||
}
|
||||
}
|
||||
});
|
||||
holder.delete.setTag(ca);
|
||||
|
||||
holder.isAdmin.setVisibility(ca.isAdmin() ? View.VISIBLE : View.GONE);
|
||||
holder.isNotAdmin.setVisibility(ca.isAdmin() ? View.GONE : View.VISIBLE);
|
||||
|
||||
holder.isAdmin.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
holder.isNotAdmin.setVisibility(View.VISIBLE);
|
||||
holder.isAdmin.setVisibility(View.GONE);
|
||||
ca.setAdmin(false);
|
||||
}
|
||||
});
|
||||
|
||||
holder.isNotAdmin.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
holder.isNotAdmin.setVisibility(View.GONE);
|
||||
holder.isAdmin.setVisibility(View.VISIBLE);
|
||||
ca.setAdmin(true);
|
||||
}
|
||||
});
|
||||
|
||||
holder.delete.setVisibility(View.VISIBLE);
|
||||
if (mHideAdminFeatures) {
|
||||
holder.delete.setVisibility(View.INVISIBLE);
|
||||
holder.isAdmin.setOnClickListener(
|
||||
null); // Do not allow not admin to remove it's rights but display admins
|
||||
holder.isNotAdmin.setVisibility(
|
||||
View.GONE); // Hide not admin button for not admin participants
|
||||
} else if (mChatRoom != null) {
|
||||
boolean found = false;
|
||||
for (Participant p : mChatRoom.getParticipants()) {
|
||||
if (p.getAddress().weakEqual(ca.getAddress())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
holder.isNotAdmin.setVisibility(
|
||||
View.GONE); // Hide not admin button for participant not yet added so
|
||||
// even if user click it it won't have any effect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public void setChatRoom(ChatRoom room) {
|
||||
mChatRoom = room;
|
||||
}
|
||||
|
||||
private Object getItem(int i) {
|
||||
return mItems.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
public void setOnDeleteClickListener(View.OnClickListener onClickListener) {
|
||||
mDeleteListener = onClickListener;
|
||||
}
|
||||
|
||||
public void updateDataSet(ArrayList<ContactAddress> mParticipants) {
|
||||
mItems = mParticipants;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setAdminFeaturesVisible(boolean visible) {
|
||||
mHideAdminFeatures = !visible;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
590
app/src/main/java/org/linphone/chat/GroupInfoFragment.java
Normal file
590
app/src/main/java/org/linphone/chat/GroupInfoFragment.java
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
/*
|
||||
InfoGroupChatFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactAddress;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.ChatRoomListener;
|
||||
import org.linphone.core.ChatRoomListenerStub;
|
||||
import org.linphone.core.ChatRoomParams;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.EventLog;
|
||||
import org.linphone.core.Participant;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
public class GroupInfoFragment extends Fragment implements ChatRoomListener {
|
||||
private ImageView mBackButton, mConfirmButton, mAddParticipantsButton;
|
||||
private RelativeLayout mAddParticipantsLayout;
|
||||
private Address mGroupChatRoomAddress;
|
||||
private EditText mSubjectField;
|
||||
|
||||
private RecyclerView mParticipantsList;
|
||||
|
||||
private LinearLayout mLeaveGroupButton;
|
||||
private RelativeLayout mWaitLayout;
|
||||
private GroupInfoAdapter mAdapter;
|
||||
private boolean mIsAlreadyCreatedGroup;
|
||||
private boolean mIsEditionEnabled;
|
||||
private ArrayList<ContactAddress> mParticipants;
|
||||
private String mSubject;
|
||||
private ChatRoom mChatRoom, mTempChatRoom;
|
||||
private Dialog mAdminStateChangedDialog;
|
||||
private ChatRoomListenerStub mChatRoomCreationListener;
|
||||
private Bundle mShareInfos;
|
||||
private Context mContext;
|
||||
private LinearLayoutManager layoutManager;
|
||||
private boolean mIsEncryptionEnabled;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.chat_infos, container, false);
|
||||
|
||||
if (getArguments() == null || getArguments().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
mContext = getActivity().getApplicationContext();
|
||||
|
||||
mParticipants =
|
||||
(ArrayList<ContactAddress>) getArguments().getSerializable("ContactAddress");
|
||||
|
||||
mGroupChatRoomAddress = null;
|
||||
mChatRoom = null;
|
||||
|
||||
String address = getArguments().getString("groupChatRoomAddress");
|
||||
if (address != null && address.length() > 0) {
|
||||
mGroupChatRoomAddress = LinphoneManager.getLc().createAddress(address);
|
||||
}
|
||||
mIsAlreadyCreatedGroup = mGroupChatRoomAddress != null;
|
||||
if (mIsAlreadyCreatedGroup) {
|
||||
mChatRoom = LinphoneManager.getLc().getChatRoom(mGroupChatRoomAddress);
|
||||
}
|
||||
if (mChatRoom == null) mIsAlreadyCreatedGroup = false;
|
||||
|
||||
mIsEditionEnabled = getArguments().getBoolean("isEditionEnabled");
|
||||
mSubject = getArguments().getString("subject");
|
||||
|
||||
if (mChatRoom != null && mChatRoom.hasBeenLeft()) {
|
||||
mIsEditionEnabled = false;
|
||||
}
|
||||
|
||||
mIsEncryptionEnabled = getArguments().getBoolean("encryptionEnabled", false);
|
||||
|
||||
mParticipantsList = view.findViewById(R.id.chat_room_participants);
|
||||
mAdapter = new GroupInfoAdapter(mParticipants, !mIsEditionEnabled, !mIsAlreadyCreatedGroup);
|
||||
mAdapter.setOnDeleteClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
ContactAddress ca = (ContactAddress) view.getTag();
|
||||
mParticipants.remove(ca);
|
||||
mAdapter.updateDataSet(mParticipants);
|
||||
mParticipantsList.setAdapter(mAdapter);
|
||||
mConfirmButton.setEnabled(
|
||||
mSubjectField.getText().length() > 0 && mParticipants.size() > 0);
|
||||
}
|
||||
});
|
||||
mParticipantsList.setAdapter(mAdapter);
|
||||
mAdapter.setChatRoom(mChatRoom);
|
||||
layoutManager = new LinearLayoutManager(mContext);
|
||||
mParticipantsList.setLayoutManager(layoutManager);
|
||||
|
||||
// Divider between items
|
||||
DividerItemDecoration dividerItemDecoration =
|
||||
new DividerItemDecoration(
|
||||
mParticipantsList.getContext(), layoutManager.getOrientation());
|
||||
dividerItemDecoration.setDrawable(mContext.getResources().getDrawable(R.drawable.divider));
|
||||
mParticipantsList.addItemDecoration(dividerItemDecoration);
|
||||
|
||||
String fileSharedUri = getArguments().getString("fileSharedUri");
|
||||
String messageDraft = getArguments().getString("messageDraft");
|
||||
|
||||
if (fileSharedUri != null || messageDraft != null) {
|
||||
Log.i("[GroupInfo] Forwarding arguments to group chat room");
|
||||
mShareInfos = new Bundle();
|
||||
}
|
||||
|
||||
if (fileSharedUri != null) mShareInfos.putString("fileSharedUri", fileSharedUri);
|
||||
|
||||
if (messageDraft != null) mShareInfos.putString("messageDraft", messageDraft);
|
||||
|
||||
mBackButton = view.findViewById(R.id.back);
|
||||
mBackButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mIsAlreadyCreatedGroup) {
|
||||
if (LinphoneActivity.instance().isTablet()) {
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
mChatRoom.getLocalAddress().asStringUriOnly(),
|
||||
mGroupChatRoomAddress.asStringUriOnly(),
|
||||
mShareInfos);
|
||||
} else {
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
} else {
|
||||
LinphoneActivity.instance()
|
||||
.goToChatCreator(
|
||||
null,
|
||||
mParticipants,
|
||||
null,
|
||||
true,
|
||||
mShareInfos,
|
||||
true,
|
||||
mIsEncryptionEnabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mConfirmButton = view.findViewById(R.id.confirm);
|
||||
|
||||
mLeaveGroupButton = view.findViewById(R.id.leaveGroupLayout);
|
||||
mLeaveGroupButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final Dialog dialog =
|
||||
LinphoneActivity.instance()
|
||||
.displayDialog(getString(R.string.chat_room_leave_dialog));
|
||||
Button delete = dialog.findViewById(R.id.dialog_delete_button);
|
||||
delete.setText(getString(R.string.chat_room_leave_button));
|
||||
Button cancel = dialog.findViewById(R.id.dialog_cancel_button);
|
||||
|
||||
delete.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.leave();
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
mChatRoom
|
||||
.getLocalAddress()
|
||||
.asStringUriOnly(),
|
||||
mGroupChatRoomAddress.asString(),
|
||||
null);
|
||||
} else {
|
||||
Log.e(
|
||||
"Can't leave, chatRoom for address "
|
||||
+ mGroupChatRoomAddress.asString()
|
||||
+ " is null...");
|
||||
}
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
cancel.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
mLeaveGroupButton.setVisibility(
|
||||
mIsAlreadyCreatedGroup && mChatRoom.hasBeenLeft()
|
||||
? View.GONE
|
||||
: mIsAlreadyCreatedGroup ? View.VISIBLE : View.GONE);
|
||||
|
||||
mAddParticipantsLayout = view.findViewById(R.id.addParticipantsLayout);
|
||||
mAddParticipantsLayout.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mIsEditionEnabled && mIsAlreadyCreatedGroup) {
|
||||
LinphoneActivity.instance()
|
||||
.goToChatCreator(
|
||||
mGroupChatRoomAddress != null
|
||||
? mGroupChatRoomAddress.asString()
|
||||
: null,
|
||||
mParticipants,
|
||||
mSubject,
|
||||
!mIsAlreadyCreatedGroup,
|
||||
null,
|
||||
true,
|
||||
mIsEncryptionEnabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
mAddParticipantsButton = view.findViewById(R.id.addParticipants);
|
||||
mAddParticipantsButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mIsEditionEnabled && mIsAlreadyCreatedGroup) {
|
||||
LinphoneActivity.instance()
|
||||
.goToChatCreator(
|
||||
mGroupChatRoomAddress != null
|
||||
? mGroupChatRoomAddress.asString()
|
||||
: null,
|
||||
mParticipants,
|
||||
mSubject,
|
||||
!mIsAlreadyCreatedGroup,
|
||||
null,
|
||||
true,
|
||||
mIsEncryptionEnabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
mAddParticipantsButton.setVisibility(mIsAlreadyCreatedGroup ? View.VISIBLE : View.GONE);
|
||||
|
||||
mSubjectField = view.findViewById(R.id.subjectField);
|
||||
mSubjectField.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mConfirmButton.setEnabled(
|
||||
mSubjectField.getText().length() > 0 && mParticipants.size() > 0);
|
||||
}
|
||||
});
|
||||
mSubjectField.setText(mSubject);
|
||||
|
||||
mChatRoomCreationListener =
|
||||
new ChatRoomListenerStub() {
|
||||
@Override
|
||||
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
|
||||
if (newState == ChatRoom.State.Created) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
// This will remove both the creation fragment and the group info
|
||||
// fragment from the back stack
|
||||
getFragmentManager().popBackStack();
|
||||
getFragmentManager().popBackStack();
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
cr.getLocalAddress().asStringUriOnly(),
|
||||
cr.getPeerAddress().asStringUriOnly(),
|
||||
mShareInfos);
|
||||
} else if (newState == ChatRoom.State.CreationFailed) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
LinphoneActivity.instance().displayChatRoomError();
|
||||
Log.e(
|
||||
"Group chat room for address "
|
||||
+ cr.getPeerAddress()
|
||||
+ " has failed !");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mConfirmButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (!mIsAlreadyCreatedGroup) {
|
||||
mWaitLayout.setVisibility(View.VISIBLE);
|
||||
Core core = LinphoneManager.getLc();
|
||||
|
||||
int i = 0;
|
||||
Address[] participants = new Address[mParticipants.size()];
|
||||
for (ContactAddress ca : mParticipants) {
|
||||
participants[i] = ca.getAddress();
|
||||
i++;
|
||||
}
|
||||
|
||||
ChatRoomParams params = core.createDefaultChatRoomParams();
|
||||
params.enableEncryption(mIsEncryptionEnabled);
|
||||
params.enableGroup(true);
|
||||
|
||||
mTempChatRoom =
|
||||
core.createChatRoom(
|
||||
params,
|
||||
mSubjectField.getText().toString(),
|
||||
participants);
|
||||
if (mTempChatRoom != null) {
|
||||
mTempChatRoom.addListener(mChatRoomCreationListener);
|
||||
} else {
|
||||
Log.w("[Group Info Fragment] createChatRoom returned null...");
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
// Subject
|
||||
String newSubject = mSubjectField.getText().toString();
|
||||
if (!newSubject.equals(mSubject)) {
|
||||
mChatRoom.setSubject(newSubject);
|
||||
}
|
||||
|
||||
// Participants removed
|
||||
ArrayList<Participant> toRemove = new ArrayList<>();
|
||||
for (Participant p : mChatRoom.getParticipants()) {
|
||||
boolean found = false;
|
||||
for (ContactAddress c : mParticipants) {
|
||||
if (c.getAddress().weakEqual(p.getAddress())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
toRemove.add(p);
|
||||
}
|
||||
}
|
||||
Participant[] participantsToRemove = new Participant[toRemove.size()];
|
||||
toRemove.toArray(participantsToRemove);
|
||||
mChatRoom.removeParticipants(participantsToRemove);
|
||||
|
||||
// Participants added
|
||||
ArrayList<Address> toAdd = new ArrayList<>();
|
||||
for (ContactAddress c : mParticipants) {
|
||||
boolean found = false;
|
||||
for (Participant p : mChatRoom.getParticipants()) {
|
||||
if (p.getAddress().weakEqual(c.getAddress())) {
|
||||
// Admin rights
|
||||
if (c.isAdmin() != p.isAdmin()) {
|
||||
mChatRoom.setParticipantAdminStatus(p, c.isAdmin());
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
Address addr = c.getAddress();
|
||||
if (addr != null) {
|
||||
toAdd.add(addr);
|
||||
} else {
|
||||
// TODO error
|
||||
}
|
||||
}
|
||||
}
|
||||
Address[] participantsToAdd = new Address[toAdd.size()];
|
||||
toAdd.toArray(participantsToAdd);
|
||||
mChatRoom.addParticipants(participantsToAdd);
|
||||
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
mChatRoom.getLocalAddress().asStringUriOnly(),
|
||||
mGroupChatRoomAddress.asString(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
});
|
||||
mConfirmButton.setEnabled(mSubjectField.getText().length() > 0 && mParticipants.size() > 0);
|
||||
|
||||
if (!mIsEditionEnabled) {
|
||||
mSubjectField.setEnabled(false);
|
||||
mConfirmButton.setVisibility(View.INVISIBLE);
|
||||
mAddParticipantsButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mWaitLayout = view.findViewById(R.id.waitScreen);
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.addListener(this);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.INFO_GROUP_CHAT);
|
||||
}
|
||||
|
||||
InputMethodManager inputMethodManager =
|
||||
(InputMethodManager) getActivity().getSystemService(INPUT_METHOD_SERVICE);
|
||||
if (getActivity().getCurrentFocus() != null) {
|
||||
inputMethodManager.hideSoftInputFromWindow(
|
||||
getActivity().getCurrentFocus().getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mTempChatRoom != null) {
|
||||
mTempChatRoom.removeListener(mChatRoomCreationListener);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.removeListener(this);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void refreshParticipantsList() {
|
||||
if (mChatRoom == null) return;
|
||||
mParticipants = new ArrayList<>();
|
||||
for (Participant p : mChatRoom.getParticipants()) {
|
||||
Address a = p.getAddress();
|
||||
LinphoneContact c = ContactsManager.getInstance().findContactFromAddress(a);
|
||||
if (c == null) {
|
||||
c = new LinphoneContact();
|
||||
String displayName = LinphoneUtils.getAddressDisplayName(a);
|
||||
c.setFullName(displayName);
|
||||
}
|
||||
ContactAddress ca = new ContactAddress(c, a.asString(), "", c.isFriend(), p.isAdmin());
|
||||
mParticipants.add(ca);
|
||||
}
|
||||
|
||||
mAdapter.updateDataSet(mParticipants);
|
||||
mAdapter.setChatRoom(mChatRoom);
|
||||
}
|
||||
|
||||
private void refreshAdminRights() {
|
||||
mAdapter.setAdminFeaturesVisible(mIsEditionEnabled);
|
||||
mAdapter.setChatRoom(mChatRoom);
|
||||
mSubjectField.setEnabled(mIsEditionEnabled);
|
||||
mConfirmButton.setVisibility(mIsEditionEnabled ? View.VISIBLE : View.INVISIBLE);
|
||||
mAddParticipantsButton.setVisibility(mIsEditionEnabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void displayMeAdminStatusUpdated() {
|
||||
if (mAdminStateChangedDialog != null) mAdminStateChangedDialog.dismiss();
|
||||
|
||||
mAdminStateChangedDialog =
|
||||
LinphoneActivity.instance()
|
||||
.displayDialog(
|
||||
getString(
|
||||
mIsEditionEnabled
|
||||
? R.string.chat_room_you_are_now_admin
|
||||
: R.string.chat_room_you_are_no_longer_admin));
|
||||
Button cancel = mAdminStateChangedDialog.findViewById(R.id.dialog_cancel_button);
|
||||
mAdminStateChangedDialog.findViewById(R.id.dialog_delete_button).setVisibility(View.GONE);
|
||||
cancel.setText(getString(R.string.ok));
|
||||
cancel.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mAdminStateChangedDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
mAdminStateChangedDialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onParticipantAdminStatusChanged(ChatRoom cr, EventLog event_log) {
|
||||
if (mChatRoom.getMe().isAdmin() != mIsEditionEnabled) {
|
||||
// Either we weren't admin and we are now or the other way around
|
||||
mIsEditionEnabled = mChatRoom.getMe().isAdmin();
|
||||
displayMeAdminStatusUpdated();
|
||||
refreshAdminRights();
|
||||
}
|
||||
refreshParticipantsList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubjectChanged(ChatRoom cr, EventLog event_log) {
|
||||
mSubjectField.setText(event_log.getSubject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConferenceJoined(ChatRoom cr, EventLog event_log) {}
|
||||
|
||||
@Override
|
||||
public void onConferenceLeft(ChatRoom cr, EventLog event_log) {}
|
||||
|
||||
@Override
|
||||
public void onParticipantAdded(ChatRoom cr, EventLog event_log) {
|
||||
refreshParticipantsList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onParticipantRemoved(ChatRoom cr, EventLog event_log) {
|
||||
refreshParticipantsList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChatMessageShouldBeStored(ChatRoom cr, ChatMessage msg) {}
|
||||
|
||||
@Override
|
||||
public void onIsComposingReceived(ChatRoom cr, Address remoteAddr, boolean isComposing) {}
|
||||
|
||||
@Override
|
||||
public void onChatMessageSent(ChatRoom cr, EventLog event_log) {}
|
||||
|
||||
@Override
|
||||
public void onConferenceAddressGeneration(ChatRoom cr) {}
|
||||
|
||||
@Override
|
||||
public void onChatMessageReceived(ChatRoom cr, EventLog event_log) {}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(ChatRoom cr, ChatMessage msg) {}
|
||||
|
||||
@Override
|
||||
public void onParticipantDeviceRemoved(ChatRoom cr, EventLog event_log) {}
|
||||
|
||||
@Override
|
||||
public void onParticipantDeviceAdded(ChatRoom cr, EventLog event_log) {}
|
||||
|
||||
@Override
|
||||
public void onSecurityEvent(ChatRoom cr, EventLog eventLog) {
|
||||
refreshParticipantsList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUndecryptableMessageReceived(ChatRoom cr, ChatMessage msg) {}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {}
|
||||
|
||||
@Override
|
||||
public void onParticipantRegistrationSubscriptionRequested(
|
||||
ChatRoom cr, Address participantAddr) {}
|
||||
|
||||
@Override
|
||||
public void onParticipantRegistrationUnsubscriptionRequested(
|
||||
ChatRoom cr, Address participantAddr) {}
|
||||
}
|
||||
46
app/src/main/java/org/linphone/chat/GroupInfoViewHolder.java
Normal file
46
app/src/main/java/org/linphone/chat/GroupInfoViewHolder.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
GroupInfoViewHolder.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.linphone.R;
|
||||
|
||||
public class GroupInfoViewHolder extends RecyclerView.ViewHolder {
|
||||
public final TextView name, sipUri;
|
||||
public final RelativeLayout avatarLayout;
|
||||
public final ImageView delete;
|
||||
public final LinearLayout isAdmin;
|
||||
public final LinearLayout isNotAdmin;
|
||||
|
||||
public GroupInfoViewHolder(View view) {
|
||||
super(view);
|
||||
name = view.findViewById(R.id.name);
|
||||
sipUri = view.findViewById(R.id.sipUri);
|
||||
avatarLayout = view.findViewById(R.id.avatar_layout);
|
||||
delete = view.findViewById(R.id.delete);
|
||||
isAdmin = view.findViewById(R.id.isAdminLayout);
|
||||
isNotAdmin = view.findViewById(R.id.isNotAdminLayout);
|
||||
}
|
||||
}
|
||||
370
app/src/main/java/org/linphone/chat/ImdnFragment.java
Normal file
370
app/src/main/java/org/linphone/chat/ImdnFragment.java
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
ImdnOldFragment.java
|
||||
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.Nullable;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.ChatMessageListenerStub;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.ParticipantImdnState;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ImdnFragment extends Fragment {
|
||||
private LayoutInflater mInflater;
|
||||
private LinearLayout mRead,
|
||||
mReadHeader,
|
||||
mDelivered,
|
||||
mDeliveredHeader,
|
||||
mSent,
|
||||
mSentHeader,
|
||||
mUndelivered,
|
||||
mUndeliveredHeader;
|
||||
private ImageView mBackButton;
|
||||
private ChatMessageViewHolder mBubble;
|
||||
private ViewGroup mContainer;
|
||||
|
||||
private String mLocalSipuri, mRoomUri, mMessageId;
|
||||
private Address mLocalSipAddr, mRoomAddr;
|
||||
private ChatRoom mRoom;
|
||||
private ChatMessage mMessage;
|
||||
private ChatMessageListenerStub mListener;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getArguments() != null) {
|
||||
mLocalSipuri = getArguments().getString("LocalSipUri");
|
||||
mLocalSipAddr = LinphoneManager.getLc().createAddress(mLocalSipuri);
|
||||
mRoomUri = getArguments().getString("RemoteSipUri");
|
||||
mRoomAddr = LinphoneManager.getLc().createAddress(mRoomUri);
|
||||
mMessageId = getArguments().getString("MessageId");
|
||||
}
|
||||
|
||||
Core core = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
mRoom = core.getChatRoom(mRoomAddr, mLocalSipAddr);
|
||||
|
||||
mInflater = inflater;
|
||||
mContainer = container;
|
||||
View view = mInflater.inflate(R.layout.chat_imdn, container, false);
|
||||
|
||||
mBackButton = view.findViewById(R.id.back);
|
||||
mBackButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (LinphoneActivity.instance().isTablet()) {
|
||||
LinphoneActivity.instance().goToChat(mLocalSipuri, mRoomUri, null);
|
||||
} else {
|
||||
LinphoneActivity.instance().onBackPressed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mRead = view.findViewById(R.id.read_layout);
|
||||
mDelivered = view.findViewById(R.id.delivered_layout);
|
||||
mSent = view.findViewById(R.id.sent_layout);
|
||||
mUndelivered = view.findViewById(R.id.undelivered_layout);
|
||||
mReadHeader = view.findViewById(R.id.read_layout_header);
|
||||
mDeliveredHeader = view.findViewById(R.id.delivered_layout_header);
|
||||
mSentHeader = view.findViewById(R.id.sent_layout_header);
|
||||
mUndeliveredHeader = view.findViewById(R.id.undelivered_layout_header);
|
||||
|
||||
mBubble = new ChatMessageViewHolder(getActivity(), view.findViewById(R.id.bubble), null);
|
||||
|
||||
mMessage = mRoom.findMessage(mMessageId);
|
||||
mListener =
|
||||
new ChatMessageListenerStub() {
|
||||
@Override
|
||||
public void onParticipantImdnStateChanged(
|
||||
ChatMessage msg, ParticipantImdnState state) {
|
||||
refreshInfo();
|
||||
}
|
||||
};
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.MESSAGE_IMDN);
|
||||
}
|
||||
|
||||
refreshInfo();
|
||||
if (mMessage != null) {
|
||||
mMessage.addListener(mListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mMessage != null) {
|
||||
mMessage.removeListener(mListener);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void refreshInfo() {
|
||||
if (mMessage == null) {
|
||||
// TODO: error
|
||||
return;
|
||||
}
|
||||
Address remoteSender = mMessage.getFromAddress();
|
||||
LinphoneContact contact =
|
||||
ContactsManager.getInstance().findContactFromAddress(remoteSender);
|
||||
|
||||
mBubble.delete.setVisibility(View.GONE);
|
||||
mBubble.eventLayout.setVisibility(View.GONE);
|
||||
mBubble.securityEventLayout.setVisibility(View.GONE);
|
||||
mBubble.rightAnchor.setVisibility(View.GONE);
|
||||
mBubble.bubbleLayout.setVisibility(View.GONE);
|
||||
mBubble.bindMessage(mMessage, contact);
|
||||
|
||||
mRead.removeAllViews();
|
||||
mDelivered.removeAllViews();
|
||||
mSent.removeAllViews();
|
||||
mUndelivered.removeAllViews();
|
||||
|
||||
ParticipantImdnState[] participants =
|
||||
mMessage.getParticipantsByImdnState(ChatMessage.State.Displayed);
|
||||
mReadHeader.setVisibility(participants.length == 0 ? View.GONE : View.VISIBLE);
|
||||
boolean first = true;
|
||||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
((TextView) v.findViewById(R.id.time))
|
||||
.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
getActivity(),
|
||||
participant.getStateChangeTime(),
|
||||
R.string.messages_date_format));
|
||||
TextView name = v.findViewById(R.id.name);
|
||||
name.setText(participantDisplayName);
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
final TextView sipUri = v.findViewById(R.id.sipUri);
|
||||
sipUri.setText(address.asStringUriOnly());
|
||||
if (!LinphoneActivity.instance()
|
||||
.getResources()
|
||||
.getBoolean(R.bool.show_sip_uri_in_chat)) {
|
||||
sipUri.setVisibility(View.GONE);
|
||||
name.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sipUri.setVisibility(
|
||||
sipUri.getVisibility() == View.VISIBLE
|
||||
? View.GONE
|
||||
: View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mRead.addView(v);
|
||||
first = false;
|
||||
}
|
||||
|
||||
participants = mMessage.getParticipantsByImdnState(ChatMessage.State.DeliveredToUser);
|
||||
mDeliveredHeader.setVisibility(participants.length == 0 ? View.GONE : View.VISIBLE);
|
||||
first = true;
|
||||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
((TextView) v.findViewById(R.id.time))
|
||||
.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
getActivity(),
|
||||
participant.getStateChangeTime(),
|
||||
R.string.messages_date_format));
|
||||
TextView name = v.findViewById(R.id.name);
|
||||
name.setText(participantDisplayName);
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
final TextView sipUri = v.findViewById(R.id.sipUri);
|
||||
sipUri.setText(address.asStringUriOnly());
|
||||
if (!LinphoneActivity.instance()
|
||||
.getResources()
|
||||
.getBoolean(R.bool.show_sip_uri_in_chat)) {
|
||||
sipUri.setVisibility(View.GONE);
|
||||
name.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sipUri.setVisibility(
|
||||
sipUri.getVisibility() == View.VISIBLE
|
||||
? View.GONE
|
||||
: View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mDelivered.addView(v);
|
||||
first = false;
|
||||
}
|
||||
|
||||
participants = mMessage.getParticipantsByImdnState(ChatMessage.State.Delivered);
|
||||
mSentHeader.setVisibility(participants.length == 0 ? View.GONE : View.VISIBLE);
|
||||
first = true;
|
||||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
((TextView) v.findViewById(R.id.time))
|
||||
.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
getActivity(),
|
||||
participant.getStateChangeTime(),
|
||||
R.string.messages_date_format));
|
||||
TextView name = v.findViewById(R.id.name);
|
||||
name.setText(participantDisplayName);
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
final TextView sipUri = v.findViewById(R.id.sipUri);
|
||||
sipUri.setText(address.asStringUriOnly());
|
||||
if (!LinphoneActivity.instance()
|
||||
.getResources()
|
||||
.getBoolean(R.bool.show_sip_uri_in_chat)) {
|
||||
sipUri.setVisibility(View.GONE);
|
||||
name.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sipUri.setVisibility(
|
||||
sipUri.getVisibility() == View.VISIBLE
|
||||
? View.GONE
|
||||
: View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mSent.addView(v);
|
||||
first = false;
|
||||
}
|
||||
|
||||
participants = mMessage.getParticipantsByImdnState(ChatMessage.State.NotDelivered);
|
||||
mUndeliveredHeader.setVisibility(participants.length == 0 ? View.GONE : View.VISIBLE);
|
||||
first = true;
|
||||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
TextView name = v.findViewById(R.id.name);
|
||||
name.setText(participantDisplayName);
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
final TextView sipUri = v.findViewById(R.id.sipUri);
|
||||
sipUri.setText(address.asStringUriOnly());
|
||||
if (!LinphoneActivity.instance()
|
||||
.getResources()
|
||||
.getBoolean(R.bool.show_sip_uri_in_chat)) {
|
||||
sipUri.setVisibility(View.GONE);
|
||||
name.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sipUri.setVisibility(
|
||||
sipUri.getVisibility() == View.VISIBLE
|
||||
? View.GONE
|
||||
: View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mUndelivered.addView(v);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package org.linphone.chat;
|
||||
|
||||
/*
|
||||
ImdnFragment.java
|
||||
ImdnOldFragment.java
|
||||
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
|
|
@ -17,11 +19,8 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.linphone.chat;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -31,11 +30,10 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneUtils;
|
||||
import org.linphone.R;
|
||||
import org.linphone.activities.LinphoneActivity;
|
||||
import org.linphone.compatibility.Compatibility;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
|
|
@ -45,54 +43,64 @@ import org.linphone.core.ChatMessageListenerStub;
|
|||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.ParticipantImdnState;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.utils.FileUtils;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ImdnFragment extends Fragment {
|
||||
public class ImdnOldFragment extends Fragment {
|
||||
private LayoutInflater mInflater;
|
||||
private LinearLayout mRead, mReadHeader, mDelivered, mDeliveredHeader, mSent, mSentHeader, mUndelivered, mUndeliveredHeader;
|
||||
private LinearLayout mRead,
|
||||
mReadHeader,
|
||||
mDelivered,
|
||||
mDeliveredHeader,
|
||||
mSent,
|
||||
mSentHeader,
|
||||
mUndelivered,
|
||||
mUndeliveredHeader;
|
||||
private ImageView mBackButton;
|
||||
private ChatBubbleViewHolder mBubble;
|
||||
private ChatMessageOldViewHolder mBubble;
|
||||
private ViewGroup mContainer;
|
||||
|
||||
private String mRoomUri, mMessageId;
|
||||
private Address mRoomAddr;
|
||||
private String mLocalSipUri, mRoomUri, mMessageId;
|
||||
private Address mLocalAddr, mRoomAddr;
|
||||
private ChatRoom mRoom;
|
||||
private ChatMessage mMessage;
|
||||
private ChatMessageListenerStub mListener;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getArguments() != null) {
|
||||
mRoomUri = getArguments().getString("SipUri");
|
||||
mLocalSipUri = getArguments().getString("LocalSipUri");
|
||||
mLocalAddr = LinphoneManager.getLc().createAddress(mLocalSipUri);
|
||||
mRoomUri = getArguments().getString("RemoteSipUri");
|
||||
mRoomAddr = LinphoneManager.getLc().createAddress(mRoomUri);
|
||||
mMessageId = getArguments().getString("MessageId");
|
||||
}
|
||||
|
||||
Core core = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
Address proxyConfigContact = core.getDefaultProxyConfig().getContact();
|
||||
if (proxyConfigContact != null) {
|
||||
mRoom = core.findOneToOneChatRoom(proxyConfigContact, mRoomAddr);
|
||||
}
|
||||
if (mRoom == null) {
|
||||
mRoom = core.getChatRoomFromUri(mRoomAddr.asStringUriOnly());
|
||||
}
|
||||
mRoom = core.getChatRoom(mRoomAddr, mLocalAddr);
|
||||
|
||||
mInflater = inflater;
|
||||
mContainer = container;
|
||||
View view = mInflater.inflate(R.layout.chat_imdn, container, false);
|
||||
View view = mInflater.inflate(R.layout.chat_imdn_old, container, false);
|
||||
|
||||
mBackButton = view.findViewById(R.id.back);
|
||||
mBackButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (LinphoneActivity.instance().isTablet()) {
|
||||
LinphoneActivity.instance().goToChat(mRoomUri, null, mRoom.getLocalAddress().asString());
|
||||
} else {
|
||||
LinphoneActivity.instance().onBackPressed();
|
||||
}
|
||||
}
|
||||
});
|
||||
mBackButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (LinphoneActivity.instance().isTablet()) {
|
||||
LinphoneActivity.instance().goToChat(mLocalSipUri, mRoomUri, null);
|
||||
} else {
|
||||
LinphoneActivity.instance().onBackPressed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mRead = view.findViewById(R.id.read_layout);
|
||||
mDelivered = view.findViewById(R.id.delivered_layout);
|
||||
|
|
@ -103,7 +111,7 @@ public class ImdnFragment extends Fragment {
|
|||
mSentHeader = view.findViewById(R.id.sent_layout_header);
|
||||
mUndeliveredHeader = view.findViewById(R.id.undelivered_layout_header);
|
||||
|
||||
mBubble = new ChatBubbleViewHolder(view.findViewById(R.id.bubble));
|
||||
mBubble = new ChatMessageOldViewHolder(view.findViewById(R.id.bubble));
|
||||
mBubble.eventLayout.setVisibility(View.GONE);
|
||||
mBubble.bubbleLayout.setVisibility(View.VISIBLE);
|
||||
mBubble.delete.setVisibility(View.GONE);
|
||||
|
|
@ -115,32 +123,38 @@ public class ImdnFragment extends Fragment {
|
|||
mBubble.messageStatus.setVisibility(View.INVISIBLE);
|
||||
mBubble.messageSendingInProgress.setVisibility(View.GONE);
|
||||
mBubble.imdmLayout.setVisibility(View.INVISIBLE);
|
||||
mBubble.contactPicture.setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
||||
|
||||
mMessage = mRoom.findMessage(mMessageId);
|
||||
mListener = new ChatMessageListenerStub() {
|
||||
@Override
|
||||
public void onParticipantImdnStateChanged(ChatMessage msg, ParticipantImdnState state) {
|
||||
refreshInfo();
|
||||
}
|
||||
};
|
||||
mMessage.setListener(mListener);
|
||||
mListener =
|
||||
new ChatMessageListenerStub() {
|
||||
@Override
|
||||
public void onParticipantImdnStateChanged(
|
||||
ChatMessage msg, ParticipantImdnState state) {
|
||||
refreshInfo();
|
||||
}
|
||||
};
|
||||
if (mMessage == null) return null;
|
||||
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
RelativeLayout.LayoutParams layoutParams =
|
||||
new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT,
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
layoutParams.setMargins(100, 10, 10, 10);
|
||||
if (mMessage.isOutgoing()) {
|
||||
mBubble.background.setBackgroundResource(R.drawable.resizable_chat_bubble_outgoing);
|
||||
Compatibility.setTextAppearance(mBubble.contactName, getActivity(), R.style.font3);
|
||||
Compatibility.setTextAppearance(mBubble.fileTransferAction, getActivity(), R.style.font15);
|
||||
mBubble.fileTransferAction.setBackgroundResource(R.drawable.resizable_confirm_delete_button);
|
||||
mBubble.contactPictureMask.setImageResource(R.drawable.avatar_chat_mask_outgoing);
|
||||
Compatibility.setTextAppearance(
|
||||
mBubble.fileTransferAction, getActivity(), R.style.font15);
|
||||
mBubble.fileTransferAction.setBackgroundResource(
|
||||
R.drawable.resizable_confirm_delete_button);
|
||||
} else {
|
||||
mBubble.background.setBackgroundResource(R.drawable.resizable_chat_bubble_incoming);
|
||||
Compatibility.setTextAppearance(mBubble.contactName, getActivity(), R.style.font9);
|
||||
Compatibility.setTextAppearance(mBubble.fileTransferAction, getActivity(), R.style.font8);
|
||||
Compatibility.setTextAppearance(
|
||||
mBubble.contactName, getActivity(), R.style.contact_organization_font);
|
||||
Compatibility.setTextAppearance(
|
||||
mBubble.fileTransferAction, getActivity(), R.style.button_font);
|
||||
mBubble.fileTransferAction.setBackgroundResource(R.drawable.resizable_assistant_button);
|
||||
mBubble.contactPictureMask.setImageResource(R.drawable.avatar_chat_mask);
|
||||
}
|
||||
|
||||
return view;
|
||||
|
|
@ -150,12 +164,29 @@ public class ImdnFragment extends Fragment {
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.MESSAGE_IMDN);
|
||||
}
|
||||
if (mMessage != null) {
|
||||
mMessage.addListener(mListener);
|
||||
}
|
||||
|
||||
refreshInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mMessage != null) {
|
||||
mMessage.removeListener(mListener);
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void refreshInfo() {
|
||||
Address remoteSender = mMessage.getFromAddress();
|
||||
LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(remoteSender);
|
||||
LinphoneContact contact =
|
||||
ContactsManager.getInstance().findContactFromAddress(remoteSender);
|
||||
String displayName;
|
||||
|
||||
if (contact != null) {
|
||||
|
|
@ -165,15 +196,16 @@ public class ImdnFragment extends Fragment {
|
|||
displayName = LinphoneUtils.getAddressDisplayName(remoteSender);
|
||||
}
|
||||
|
||||
mBubble.contactPicture.setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
||||
if (contact.hasPhoto()) {
|
||||
LinphoneUtils.setThumbnailPictureFromUri(getActivity(), mBubble.contactPicture, contact.getThumbnailUri());
|
||||
}
|
||||
ContactAvatar.displayAvatar(contact, mBubble.avatarLayout);
|
||||
} else {
|
||||
displayName = LinphoneUtils.getAddressDisplayName(remoteSender);
|
||||
mBubble.contactPicture.setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
||||
ContactAvatar.displayAvatar(displayName, mBubble.avatarLayout);
|
||||
}
|
||||
mBubble.contactName.setText(LinphoneUtils.timestampToHumanDate(getActivity(), mMessage.getTime(), R.string.messages_date_format) + " - " + displayName);
|
||||
mBubble.contactName.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
getActivity(), mMessage.getTime(), R.string.messages_date_format)
|
||||
+ " - "
|
||||
+ displayName);
|
||||
|
||||
if (mMessage.hasTextContent()) {
|
||||
String msg = mMessage.getTextContent();
|
||||
|
|
@ -186,7 +218,7 @@ public class ImdnFragment extends Fragment {
|
|||
String appData = mMessage.getAppdata();
|
||||
if (appData != null) { // Something to display
|
||||
mBubble.fileName.setVisibility(View.VISIBLE);
|
||||
mBubble.fileName.setText(LinphoneUtils.getNameFromFilePath(appData));
|
||||
mBubble.fileName.setText(FileUtils.getNameFromFilePath(appData));
|
||||
// We purposely chose not to display the image
|
||||
}
|
||||
|
||||
|
|
@ -195,23 +227,34 @@ public class ImdnFragment extends Fragment {
|
|||
mSent.removeAllViews();
|
||||
mUndelivered.removeAllViews();
|
||||
|
||||
ParticipantImdnState[] participants = mMessage.getParticipantsByImdnState(ChatMessage.State.Displayed);
|
||||
ParticipantImdnState[] participants =
|
||||
mMessage.getParticipantsByImdnState(ChatMessage.State.Displayed);
|
||||
mReadHeader.setVisibility(participants.length == 0 ? View.GONE : View.VISIBLE);
|
||||
boolean first = true;
|
||||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact = ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName = participantContact != null ? participantContact.getFullName() : LinphoneUtils.getAddressDisplayName(address);
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
((TextView) v.findViewById(R.id.time)).setText(LinphoneUtils.timestampToHumanDate(getActivity(), participant.getStateChangeTime(), R.string.messages_date_format));
|
||||
((TextView) v.findViewById(R.id.time))
|
||||
.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
getActivity(),
|
||||
participant.getStateChangeTime(),
|
||||
R.string.messages_date_format));
|
||||
((TextView) v.findViewById(R.id.name)).setText(participantDisplayName);
|
||||
if (participantContact != null && participantContact.hasPhoto()) {
|
||||
LinphoneUtils.setThumbnailPictureFromUri(getActivity(), ((ImageView) v.findViewById(R.id.contact_picture)), participantContact.getThumbnailUri());
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
((ImageView) v.findViewById(R.id.contact_picture)).setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
mRead.addView(v);
|
||||
|
|
@ -224,17 +267,27 @@ public class ImdnFragment extends Fragment {
|
|||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact = ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName = participantContact != null ? participantContact.getFullName() : LinphoneUtils.getAddressDisplayName(address);
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
((TextView) v.findViewById(R.id.time)).setText(LinphoneUtils.timestampToHumanDate(getActivity(), participant.getStateChangeTime(), R.string.messages_date_format));
|
||||
((TextView) v.findViewById(R.id.time))
|
||||
.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
getActivity(),
|
||||
participant.getStateChangeTime(),
|
||||
R.string.messages_date_format));
|
||||
((TextView) v.findViewById(R.id.name)).setText(participantDisplayName);
|
||||
if (participantContact != null && participantContact.hasPhoto()) {
|
||||
LinphoneUtils.setThumbnailPictureFromUri(getActivity(), ((ImageView) v.findViewById(R.id.contact_picture)), participantContact.getThumbnailUri());
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
((ImageView) v.findViewById(R.id.contact_picture)).setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
mDelivered.addView(v);
|
||||
|
|
@ -247,17 +300,27 @@ public class ImdnFragment extends Fragment {
|
|||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact = ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName = participantContact != null ? participantContact.getFullName() : LinphoneUtils.getAddressDisplayName(address);
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
((TextView) v.findViewById(R.id.time)).setText(LinphoneUtils.timestampToHumanDate(getActivity(), participant.getStateChangeTime(), R.string.messages_date_format));
|
||||
((TextView) v.findViewById(R.id.time))
|
||||
.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
getActivity(),
|
||||
participant.getStateChangeTime(),
|
||||
R.string.messages_date_format));
|
||||
((TextView) v.findViewById(R.id.name)).setText(participantDisplayName);
|
||||
if (participantContact != null && participantContact.hasPhoto()) {
|
||||
LinphoneUtils.setThumbnailPictureFromUri(getActivity(), ((ImageView) v.findViewById(R.id.contact_picture)), participantContact.getThumbnailUri());
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
((ImageView) v.findViewById(R.id.contact_picture)).setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
mSent.addView(v);
|
||||
|
|
@ -270,16 +333,21 @@ public class ImdnFragment extends Fragment {
|
|||
for (ParticipantImdnState participant : participants) {
|
||||
Address address = participant.getParticipant().getAddress();
|
||||
|
||||
LinphoneContact participantContact = ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName = participantContact != null ? participantContact.getFullName() : LinphoneUtils.getAddressDisplayName(address);
|
||||
LinphoneContact participantContact =
|
||||
ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String participantDisplayName =
|
||||
participantContact != null
|
||||
? participantContact.getFullName()
|
||||
: LinphoneUtils.getAddressDisplayName(address);
|
||||
|
||||
View v = mInflater.inflate(R.layout.chat_imdn_cell, mContainer, false);
|
||||
v.findViewById(R.id.separator).setVisibility(first ? View.GONE : View.VISIBLE);
|
||||
((TextView) v.findViewById(R.id.name)).setText(participantDisplayName);
|
||||
if (participantContact != null && participantContact.hasPhoto()) {
|
||||
LinphoneUtils.setThumbnailPictureFromUri(getActivity(), ((ImageView) v.findViewById(R.id.contact_picture)), participantContact.getThumbnailUri());
|
||||
if (participantContact != null) {
|
||||
ContactAvatar.displayAvatar(participantContact, v.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
((ImageView) v.findViewById(R.id.contact_picture)).setImageBitmap(ContactsManager.getInstance().getDefaultAvatarBitmap());
|
||||
ContactAvatar.displayAvatar(
|
||||
participantDisplayName, v.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
mUndelivered.addView(v);
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package org.linphone.compatibility;
|
||||
|
||||
/*
|
||||
ApiTwentyEightPlus.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import static org.linphone.compatibility.Compatibility.CHAT_NOTIFICATIONS_GROUP;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Person;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Icon;
|
||||
import org.linphone.R;
|
||||
import org.linphone.notifications.Notifiable;
|
||||
import org.linphone.notifications.NotifiableMessage;
|
||||
|
||||
@TargetApi(28)
|
||||
class ApiTwentyEightPlus {
|
||||
public static Notification createMessageNotification(
|
||||
Context context, Notifiable notif, Bitmap contactIcon, PendingIntent intent) {
|
||||
|
||||
Person me = new Person.Builder().setName(notif.getMyself()).build();
|
||||
Notification.MessagingStyle style = new Notification.MessagingStyle(me);
|
||||
for (NotifiableMessage message : notif.getMessages()) {
|
||||
Icon userIcon = Icon.createWithBitmap(message.getSenderBitmap());
|
||||
Person user =
|
||||
new Person.Builder().setName(message.getSender()).setIcon(userIcon).build();
|
||||
Notification.MessagingStyle.Message msg =
|
||||
new Notification.MessagingStyle.Message(
|
||||
message.getMessage(), message.getTime(), user);
|
||||
if (message.getFilePath() != null)
|
||||
msg.setData(message.getFileMime(), message.getFilePath());
|
||||
style.addMessage(msg);
|
||||
}
|
||||
if (notif.isGroup()) {
|
||||
style.setConversationTitle(notif.getGroupTitle());
|
||||
}
|
||||
style.setGroupConversation(notif.isGroup());
|
||||
|
||||
return new Notification.Builder(
|
||||
context, context.getString(R.string.notification_channel_id))
|
||||
.setSmallIcon(R.drawable.topbar_chat_notification)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(
|
||||
Notification.DEFAULT_SOUND
|
||||
| Notification.DEFAULT_VIBRATE
|
||||
| Notification.DEFAULT_LIGHTS)
|
||||
.setLargeIcon(contactIcon)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setGroup(CHAT_NOTIFICATIONS_GROUP)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setNumber(notif.getMessages().size())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.setStyle(style)
|
||||
.addAction(ApiTwentyFourPlus.getReplyMessageAction(context, notif))
|
||||
.addAction(ApiTwentyFourPlus.getMarkMessageAsReadAction(context, notif))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static boolean isAppUserRestricted(Context context) {
|
||||
ActivityManager activityManager =
|
||||
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
return activityManager.isBackgroundRestricted();
|
||||
}
|
||||
|
||||
public static int getAppStandbyBucket(Context context) {
|
||||
UsageStatsManager usageStatsManager =
|
||||
(UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
return usageStatsManager.getAppStandbyBucket();
|
||||
}
|
||||
|
||||
public static String getAppStandbyBucketNameFromValue(int bucket) {
|
||||
switch (bucket) {
|
||||
case UsageStatsManager.STANDBY_BUCKET_ACTIVE:
|
||||
return "STANDBY_BUCKET_ACTIVE";
|
||||
case UsageStatsManager.STANDBY_BUCKET_FREQUENT:
|
||||
return "STANDBY_BUCKET_FREQUENT";
|
||||
case UsageStatsManager.STANDBY_BUCKET_RARE:
|
||||
return "STANDBY_BUCKET_RARE";
|
||||
case UsageStatsManager.STANDBY_BUCKET_WORKING_SET:
|
||||
return "STANDBY_BUCKET_WORKING_SET";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
package org.linphone.compatibility;
|
||||
|
||||
/*
|
||||
ApiTwentyFourPlus.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import static org.linphone.compatibility.Compatibility.CHAT_NOTIFICATIONS_GROUP;
|
||||
import static org.linphone.compatibility.Compatibility.INTENT_ANSWER_CALL_NOTIF_ACTION;
|
||||
import static org.linphone.compatibility.Compatibility.INTENT_HANGUP_CALL_NOTIF_ACTION;
|
||||
import static org.linphone.compatibility.Compatibility.INTENT_LOCAL_IDENTITY;
|
||||
import static org.linphone.compatibility.Compatibility.INTENT_MARK_AS_READ_ACTION;
|
||||
import static org.linphone.compatibility.Compatibility.INTENT_NOTIF_ID;
|
||||
import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION;
|
||||
import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.RemoteInput;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import org.linphone.R;
|
||||
import org.linphone.notifications.Notifiable;
|
||||
import org.linphone.notifications.NotifiableMessage;
|
||||
import org.linphone.notifications.NotificationBroadcastReceiver;
|
||||
|
||||
@TargetApi(24)
|
||||
class ApiTwentyFourPlus {
|
||||
|
||||
public static Notification createRepliedNotification(Context context, String reply) {
|
||||
return new Notification.Builder(context)
|
||||
.setSmallIcon(R.drawable.topbar_chat_notification)
|
||||
.setContentText(
|
||||
context.getString(R.string.notification_replied_label).replace("%s", reply))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Notification createMessageNotification(
|
||||
Context context, Notifiable notif, Bitmap contactIcon, PendingIntent intent) {
|
||||
|
||||
Notification.MessagingStyle style = new Notification.MessagingStyle(notif.getMyself());
|
||||
for (NotifiableMessage message : notif.getMessages()) {
|
||||
Notification.MessagingStyle.Message msg =
|
||||
new Notification.MessagingStyle.Message(
|
||||
message.getMessage(), message.getTime(), message.getSender());
|
||||
if (message.getFilePath() != null)
|
||||
msg.setData(message.getFileMime(), message.getFilePath());
|
||||
style.addMessage(msg);
|
||||
}
|
||||
if (notif.isGroup()) {
|
||||
style.setConversationTitle(notif.getGroupTitle());
|
||||
}
|
||||
|
||||
return new Notification.Builder(context)
|
||||
.setSmallIcon(R.drawable.topbar_chat_notification)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(
|
||||
Notification.DEFAULT_SOUND
|
||||
| Notification.DEFAULT_VIBRATE
|
||||
| Notification.DEFAULT_LIGHTS)
|
||||
.setLargeIcon(contactIcon)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setGroup(CHAT_NOTIFICATIONS_GROUP)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setNumber(notif.getMessages().size())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.setStyle(style)
|
||||
.addAction(getReplyMessageAction(context, notif))
|
||||
.addAction(getMarkMessageAsReadAction(context, notif))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Notification createInCallNotification(
|
||||
Context context,
|
||||
int callId,
|
||||
boolean showAnswerAction,
|
||||
String msg,
|
||||
int iconID,
|
||||
Bitmap contactIcon,
|
||||
String contactName,
|
||||
PendingIntent intent) {
|
||||
|
||||
Notification.Builder builder =
|
||||
new Notification.Builder(context)
|
||||
.setContentTitle(contactName)
|
||||
.setContentText(msg)
|
||||
.setSmallIcon(iconID)
|
||||
.setAutoCancel(false)
|
||||
.setContentIntent(intent)
|
||||
.setLargeIcon(contactIcon)
|
||||
.setCategory(Notification.CATEGORY_CALL)
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.addAction(getCallDeclineAction(context, callId));
|
||||
|
||||
if (showAnswerAction) {
|
||||
builder.addAction(getCallAnswerAction(context, callId));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static Notification.Action getReplyMessageAction(Context context, Notifiable notif) {
|
||||
String replyLabel = context.getResources().getString(R.string.notification_reply_label);
|
||||
RemoteInput remoteInput =
|
||||
new RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build();
|
||||
|
||||
Intent replyIntent = new Intent(context, NotificationBroadcastReceiver.class);
|
||||
replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION);
|
||||
replyIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId());
|
||||
replyIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity());
|
||||
|
||||
PendingIntent replyPendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
notif.getNotificationId(),
|
||||
replyIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Notification.Action replyAction =
|
||||
new Notification.Action.Builder(
|
||||
R.drawable.chat_send_over,
|
||||
context.getString(R.string.notification_reply_label),
|
||||
replyPendingIntent)
|
||||
.addRemoteInput(remoteInput)
|
||||
.setAllowGeneratedReplies(true)
|
||||
.build();
|
||||
return replyAction;
|
||||
}
|
||||
|
||||
public static Notification.Action getMarkMessageAsReadAction(
|
||||
Context context, Notifiable notif) {
|
||||
Intent markAsReadIntent = new Intent(context, NotificationBroadcastReceiver.class);
|
||||
markAsReadIntent.setAction(INTENT_MARK_AS_READ_ACTION);
|
||||
markAsReadIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId());
|
||||
markAsReadIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity());
|
||||
|
||||
PendingIntent markAsReadPendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
notif.getNotificationId(),
|
||||
markAsReadIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Notification.Action markAsReadAction =
|
||||
new Notification.Action.Builder(
|
||||
R.drawable.chat_send_over,
|
||||
context.getString(R.string.notification_mark_as_read_label),
|
||||
markAsReadPendingIntent)
|
||||
.build();
|
||||
|
||||
return markAsReadAction;
|
||||
}
|
||||
|
||||
public static Notification.Action getCallAnswerAction(Context context, int callId) {
|
||||
Intent answerIntent = new Intent(context, NotificationBroadcastReceiver.class);
|
||||
answerIntent.setAction(INTENT_ANSWER_CALL_NOTIF_ACTION);
|
||||
answerIntent.putExtra(INTENT_NOTIF_ID, callId);
|
||||
|
||||
PendingIntent answerPendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, callId, answerIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Notification.Action answerAction =
|
||||
new Notification.Action.Builder(
|
||||
R.drawable.call_audio_start,
|
||||
context.getString(R.string.notification_call_answer_label),
|
||||
answerPendingIntent)
|
||||
.build();
|
||||
|
||||
return answerAction;
|
||||
}
|
||||
|
||||
public static Notification.Action getCallDeclineAction(Context context, int callId) {
|
||||
Intent hangupIntent = new Intent(context, NotificationBroadcastReceiver.class);
|
||||
hangupIntent.setAction(INTENT_HANGUP_CALL_NOTIF_ACTION);
|
||||
hangupIntent.putExtra(INTENT_NOTIF_ID, callId);
|
||||
|
||||
PendingIntent hangupPendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, callId, hangupIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Notification.Action declineAction =
|
||||
new Notification.Action.Builder(
|
||||
R.drawable.call_hangup,
|
||||
context.getString(R.string.notification_call_hangup_label),
|
||||
hangupPendingIntent)
|
||||
.build();
|
||||
return declineAction;
|
||||
}
|
||||
|
||||
public static void closeContentProviderClient(ContentProviderClient client) {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
package org.linphone.compatibility;
|
||||
|
||||
/*
|
||||
ApiTwentyOnePlus.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.WindowManager;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import org.linphone.R;
|
||||
|
||||
@TargetApi(21)
|
||||
class ApiTwentyOnePlus {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Notification createMessageNotification(
|
||||
Context context,
|
||||
int msgCount,
|
||||
String msgSender,
|
||||
String msg,
|
||||
Bitmap contactIcon,
|
||||
PendingIntent intent) {
|
||||
String title;
|
||||
if (msgCount == 1) {
|
||||
title = msgSender;
|
||||
} else {
|
||||
title =
|
||||
context.getString(R.string.unread_messages)
|
||||
.replace("%i", String.valueOf(msgCount));
|
||||
}
|
||||
|
||||
return new Notification.Builder(context)
|
||||
.setContentTitle(title)
|
||||
.setContentText(msg)
|
||||
.setSmallIcon(R.drawable.topbar_chat_notification)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(
|
||||
Notification.DEFAULT_SOUND
|
||||
| Notification.DEFAULT_VIBRATE
|
||||
| Notification.DEFAULT_LIGHTS)
|
||||
.setLargeIcon(contactIcon)
|
||||
.setLights(
|
||||
ContextCompat.getColor(context, R.color.notification_led_color),
|
||||
context.getResources().getInteger(R.integer.notification_ms_on),
|
||||
context.getResources().getInteger(R.integer.notification_ms_off))
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setNumber(msgCount)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Notification createInCallNotification(
|
||||
Context context,
|
||||
String msg,
|
||||
int iconID,
|
||||
Bitmap contactIcon,
|
||||
String contactName,
|
||||
PendingIntent intent) {
|
||||
|
||||
return new Notification.Builder(context)
|
||||
.setContentTitle(contactName)
|
||||
.setContentText(msg)
|
||||
.setSmallIcon(iconID)
|
||||
.setAutoCancel(false)
|
||||
.setContentIntent(intent)
|
||||
.setLargeIcon(contactIcon)
|
||||
.setCategory(Notification.CATEGORY_CALL)
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setLights(
|
||||
ContextCompat.getColor(context, R.color.notification_led_color),
|
||||
context.getResources().getInteger(R.integer.notification_ms_on),
|
||||
context.getResources().getInteger(R.integer.notification_ms_off))
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Notification createNotification(
|
||||
Context context,
|
||||
String title,
|
||||
String message,
|
||||
int icon,
|
||||
int level,
|
||||
Bitmap largeIcon,
|
||||
PendingIntent intent,
|
||||
int priority) {
|
||||
Notification notif;
|
||||
|
||||
if (largeIcon != null) {
|
||||
notif =
|
||||
new Notification.Builder(context)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setSmallIcon(icon, level)
|
||||
.setLargeIcon(largeIcon)
|
||||
.setContentIntent(intent)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.setLights(
|
||||
ContextCompat.getColor(context, R.color.notification_led_color),
|
||||
context.getResources().getInteger(R.integer.notification_ms_on),
|
||||
context.getResources()
|
||||
.getInteger(R.integer.notification_ms_off))
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setPriority(priority)
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
} else {
|
||||
notif =
|
||||
new Notification.Builder(context)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setSmallIcon(icon, level)
|
||||
.setContentIntent(intent)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.setLights(
|
||||
ContextCompat.getColor(context, R.color.notification_led_color),
|
||||
context.getResources().getInteger(R.integer.notification_ms_on),
|
||||
context.getResources()
|
||||
.getInteger(R.integer.notification_ms_off))
|
||||
.setPriority(priority)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
return notif;
|
||||
}
|
||||
|
||||
public static Notification createMissedCallNotification(
|
||||
Context context, String title, String text, PendingIntent intent) {
|
||||
|
||||
return new Notification.Builder(context)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setSmallIcon(R.drawable.call_status_missed)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
|
||||
.setCategory(Notification.CATEGORY_EVENT)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setLights(
|
||||
ContextCompat.getColor(context, R.color.notification_led_color),
|
||||
context.getResources().getInteger(R.integer.notification_ms_on),
|
||||
context.getResources().getInteger(R.integer.notification_ms_off))
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Notification createSimpleNotification(
|
||||
Context context, String title, String text, PendingIntent intent) {
|
||||
|
||||
return new Notification.Builder(context)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setSmallIcon(R.drawable.linphone_logo)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setLights(
|
||||
ContextCompat.getColor(context, R.color.notification_led_color),
|
||||
context.getResources().getInteger(R.integer.notification_ms_on),
|
||||
context.getResources().getInteger(R.integer.notification_ms_off))
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void closeContentProviderClient(ContentProviderClient client) {
|
||||
client.release();
|
||||
}
|
||||
|
||||
public static void setShowWhenLocked(Activity activity, boolean enable) {
|
||||
if (enable) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTurnScreenOn(Activity activity, boolean enable) {
|
||||
if (enable) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
package org.linphone.compatibility;
|
||||
|
||||
/*
|
||||
ApiTwentySixPlus.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import static org.linphone.compatibility.Compatibility.CHAT_NOTIFICATIONS_GROUP;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import org.linphone.R;
|
||||
import org.linphone.notifications.Notifiable;
|
||||
import org.linphone.notifications.NotifiableMessage;
|
||||
|
||||
@TargetApi(26)
|
||||
class ApiTwentySixPlus {
|
||||
public static String getDeviceName(Context context) {
|
||||
String name =
|
||||
Settings.Global.getString(
|
||||
context.getContentResolver(), Settings.Global.DEVICE_NAME);
|
||||
if (name == null) {
|
||||
name = BluetoothAdapter.getDefaultAdapter().getName();
|
||||
}
|
||||
if (name == null) {
|
||||
name = Settings.Secure.getString(context.getContentResolver(), "bluetooth_name");
|
||||
}
|
||||
if (name == null) {
|
||||
name = Build.MANUFACTURER + " " + Build.MODEL;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static Notification createRepliedNotification(Context context, String reply) {
|
||||
return new Notification.Builder(
|
||||
context, context.getString(R.string.notification_channel_id))
|
||||
.setSmallIcon(R.drawable.topbar_chat_notification)
|
||||
.setContentText(
|
||||
context.getString(R.string.notification_replied_label).replace("%s", reply))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void createServiceChannel(Context context) {
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
// Create service/call notification channel
|
||||
String id = context.getString(R.string.notification_service_channel_id);
|
||||
CharSequence name = context.getString(R.string.content_title_notification_service);
|
||||
String description = context.getString(R.string.content_title_notification_service);
|
||||
NotificationChannel channel =
|
||||
new NotificationChannel(id, name, NotificationManager.IMPORTANCE_NONE);
|
||||
channel.setDescription(description);
|
||||
channel.enableVibration(false);
|
||||
channel.enableLights(false);
|
||||
channel.setShowBadge(false);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
public static void createMessageChannel(Context context) {
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
// Create message notification channel
|
||||
String id = context.getString(R.string.notification_channel_id);
|
||||
String name = context.getString(R.string.content_title_notification);
|
||||
String description = context.getString(R.string.content_title_notification);
|
||||
NotificationChannel channel =
|
||||
new NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setDescription(description);
|
||||
channel.setLightColor(context.getColor(R.color.notification_led_color));
|
||||
channel.enableLights(true);
|
||||
channel.enableVibration(true);
|
||||
channel.setShowBadge(true);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
public static Notification createMessageNotification(
|
||||
Context context, Notifiable notif, Bitmap contactIcon, PendingIntent intent) {
|
||||
|
||||
Notification.MessagingStyle style = new Notification.MessagingStyle(notif.getMyself());
|
||||
for (NotifiableMessage message : notif.getMessages()) {
|
||||
Notification.MessagingStyle.Message msg =
|
||||
new Notification.MessagingStyle.Message(
|
||||
message.getMessage(), message.getTime(), message.getSender());
|
||||
if (message.getFilePath() != null)
|
||||
msg.setData(message.getFileMime(), message.getFilePath());
|
||||
style.addMessage(msg);
|
||||
}
|
||||
if (notif.isGroup()) {
|
||||
style.setConversationTitle(notif.getGroupTitle());
|
||||
}
|
||||
|
||||
return new Notification.Builder(
|
||||
context, context.getString(R.string.notification_channel_id))
|
||||
.setSmallIcon(R.drawable.topbar_chat_notification)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(
|
||||
Notification.DEFAULT_SOUND
|
||||
| Notification.DEFAULT_VIBRATE
|
||||
| Notification.DEFAULT_LIGHTS)
|
||||
.setLargeIcon(contactIcon)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setGroup(CHAT_NOTIFICATIONS_GROUP)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setNumber(notif.getMessages().size())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.setStyle(style)
|
||||
.addAction(ApiTwentyFourPlus.getReplyMessageAction(context, notif))
|
||||
.addAction(ApiTwentyFourPlus.getMarkMessageAsReadAction(context, notif))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Notification createInCallNotification(
|
||||
Context context,
|
||||
int callId,
|
||||
boolean showAnswerAction,
|
||||
String msg,
|
||||
int iconID,
|
||||
Bitmap contactIcon,
|
||||
String contactName,
|
||||
PendingIntent intent) {
|
||||
|
||||
Notification.Builder builder =
|
||||
new Notification.Builder(
|
||||
context,
|
||||
context.getString(R.string.notification_service_channel_id))
|
||||
.setContentTitle(contactName)
|
||||
.setContentText(msg)
|
||||
.setSmallIcon(iconID)
|
||||
.setAutoCancel(false)
|
||||
.setContentIntent(intent)
|
||||
.setLargeIcon(contactIcon)
|
||||
.setCategory(Notification.CATEGORY_CALL)
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.addAction(ApiTwentyFourPlus.getCallDeclineAction(context, callId));
|
||||
|
||||
if (showAnswerAction) {
|
||||
builder.addAction(ApiTwentyFourPlus.getCallAnswerAction(context, callId));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static Notification createNotification(
|
||||
Context context,
|
||||
String title,
|
||||
String message,
|
||||
int icon,
|
||||
int level,
|
||||
Bitmap largeIcon,
|
||||
PendingIntent intent,
|
||||
int priority) {
|
||||
|
||||
if (largeIcon != null) {
|
||||
return new Notification.Builder(
|
||||
context, context.getString(R.string.notification_service_channel_id))
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setSmallIcon(icon, level)
|
||||
.setLargeIcon(largeIcon)
|
||||
.setContentIntent(intent)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.setPriority(priority)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.build();
|
||||
} else {
|
||||
return new Notification.Builder(
|
||||
context, context.getString(R.string.notification_service_channel_id))
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setSmallIcon(icon, level)
|
||||
.setContentIntent(intent)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.setPriority(priority)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public static Notification createMissedCallNotification(
|
||||
Context context, String title, String text, PendingIntent intent) {
|
||||
return new Notification.Builder(
|
||||
context, context.getString(R.string.notification_channel_id))
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setSmallIcon(R.drawable.call_status_missed)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
|
||||
.setCategory(Notification.CATEGORY_EVENT)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Notification createSimpleNotification(
|
||||
Context context, String title, String text, PendingIntent intent) {
|
||||
return new Notification.Builder(
|
||||
context, context.getString(R.string.notification_channel_id))
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setSmallIcon(R.drawable.linphone_logo)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setColorized(true)
|
||||
.setColor(context.getColor(R.color.notification_led_color))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void startService(Context context, Intent intent) {
|
||||
context.startForegroundService(intent);
|
||||
}
|
||||
|
||||
public static void setFragmentTransactionReorderingAllowed(
|
||||
FragmentTransaction transaction, boolean allowed) {
|
||||
transaction.setReorderingAllowed(allowed);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
package org.linphone.compatibility;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.widget.TextView;
|
||||
|
||||
/*
|
||||
ApiTwentyThreePlus.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
|
@ -22,9 +19,18 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
@TargetApi(23)
|
||||
public class ApiTwentyThreePlus {
|
||||
class ApiTwentyThreePlus {
|
||||
public static void setTextAppearance(TextView textview, int style) {
|
||||
textview.setTextAppearance(style);
|
||||
}
|
||||
|
||||
public static boolean isAppIdleMode(Context context) {
|
||||
return ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).isDeviceIdleMode();
|
||||
}
|
||||
}
|
||||
240
app/src/main/java/org/linphone/compatibility/Compatibility.java
Normal file
240
app/src/main/java/org/linphone/compatibility/Compatibility.java
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
package org.linphone.compatibility;
|
||||
/*
|
||||
Compatibility.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.widget.TextView;
|
||||
import org.linphone.mediastream.Version;
|
||||
import org.linphone.notifications.Notifiable;
|
||||
|
||||
public class Compatibility {
|
||||
public static final String CHAT_NOTIFICATIONS_GROUP = "CHAT_NOTIF_GROUP";
|
||||
public static final String KEY_TEXT_REPLY = "key_text_reply";
|
||||
public static final String INTENT_NOTIF_ID = "NOTIFICATION_ID";
|
||||
public static final String INTENT_REPLY_NOTIF_ACTION = "org.linphone.REPLY_ACTION";
|
||||
public static final String INTENT_HANGUP_CALL_NOTIF_ACTION = "org.linphone.HANGUP_CALL_ACTION";
|
||||
public static final String INTENT_ANSWER_CALL_NOTIF_ACTION = "org.linphone.ANSWER_CALL_ACTION";
|
||||
public static final String INTENT_LOCAL_IDENTITY = "LOCAL_IDENTITY";
|
||||
public static final String INTENT_MARK_AS_READ_ACTION = "org.linphone.MARK_AS_READ_ACTION";
|
||||
|
||||
public static String getDeviceName(Context context) {
|
||||
if (Version.sdkAboveOrEqual(25)) {
|
||||
return ApiTwentySixPlus.getDeviceName(context);
|
||||
}
|
||||
|
||||
String name = BluetoothAdapter.getDefaultAdapter().getName();
|
||||
if (name == null) {
|
||||
name = Settings.Secure.getString(context.getContentResolver(), "bluetooth_name");
|
||||
}
|
||||
if (name == null) {
|
||||
name = Build.MANUFACTURER + " " + Build.MODEL;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static void createNotificationChannels(Context context) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
ApiTwentySixPlus.createServiceChannel(context);
|
||||
ApiTwentySixPlus.createMessageChannel(context);
|
||||
}
|
||||
}
|
||||
|
||||
public static Notification createSimpleNotification(
|
||||
Context context, String title, String text, PendingIntent intent) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
return ApiTwentySixPlus.createSimpleNotification(context, title, text, intent);
|
||||
}
|
||||
return ApiTwentyOnePlus.createSimpleNotification(context, title, text, intent);
|
||||
}
|
||||
|
||||
public static Notification createMissedCallNotification(
|
||||
Context context, String title, String text, PendingIntent intent) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
return ApiTwentySixPlus.createMissedCallNotification(context, title, text, intent);
|
||||
}
|
||||
return ApiTwentyOnePlus.createMissedCallNotification(context, title, text, intent);
|
||||
}
|
||||
|
||||
public static Notification createMessageNotification(
|
||||
Context context,
|
||||
Notifiable notif,
|
||||
String msgSender,
|
||||
String msg,
|
||||
Bitmap contactIcon,
|
||||
PendingIntent intent) {
|
||||
if (Version.sdkAboveOrEqual(28)) {
|
||||
return ApiTwentyEightPlus.createMessageNotification(
|
||||
context, notif, contactIcon, intent);
|
||||
} else if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
return ApiTwentySixPlus.createMessageNotification(context, notif, contactIcon, intent);
|
||||
} else if (Version.sdkAboveOrEqual(Version.API24_NOUGAT_70)) {
|
||||
return ApiTwentyFourPlus.createMessageNotification(context, notif, contactIcon, intent);
|
||||
}
|
||||
return ApiTwentyOnePlus.createMessageNotification(
|
||||
context, notif.getMessages().size(), msgSender, msg, contactIcon, intent);
|
||||
}
|
||||
|
||||
public static Notification createRepliedNotification(Context context, String reply) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
return ApiTwentySixPlus.createRepliedNotification(context, reply);
|
||||
} else if (Version.sdkAboveOrEqual(Version.API24_NOUGAT_70)) {
|
||||
return ApiTwentyFourPlus.createRepliedNotification(context, reply);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Notification createInCallNotification(
|
||||
Context context,
|
||||
int callId,
|
||||
boolean showAnswerAction,
|
||||
String msg,
|
||||
int iconID,
|
||||
Bitmap contactIcon,
|
||||
String contactName,
|
||||
PendingIntent intent) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
return ApiTwentySixPlus.createInCallNotification(
|
||||
context,
|
||||
callId,
|
||||
showAnswerAction,
|
||||
msg,
|
||||
iconID,
|
||||
contactIcon,
|
||||
contactName,
|
||||
intent);
|
||||
} else if (Version.sdkAboveOrEqual(Version.API24_NOUGAT_70)) {
|
||||
return ApiTwentyFourPlus.createInCallNotification(
|
||||
context,
|
||||
callId,
|
||||
showAnswerAction,
|
||||
msg,
|
||||
iconID,
|
||||
contactIcon,
|
||||
contactName,
|
||||
intent);
|
||||
}
|
||||
return ApiTwentyOnePlus.createInCallNotification(
|
||||
context, msg, iconID, contactIcon, contactName, intent);
|
||||
}
|
||||
|
||||
public static Notification createNotification(
|
||||
Context context,
|
||||
String title,
|
||||
String message,
|
||||
int icon,
|
||||
int iconLevel,
|
||||
Bitmap largeIcon,
|
||||
PendingIntent intent,
|
||||
int priority) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
return ApiTwentySixPlus.createNotification(
|
||||
context, title, message, icon, iconLevel, largeIcon, intent, priority);
|
||||
}
|
||||
return ApiTwentyOnePlus.createNotification(
|
||||
context, title, message, icon, iconLevel, largeIcon, intent, priority);
|
||||
}
|
||||
|
||||
public static boolean canDrawOverlays(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return Settings.canDrawOverlays(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void setTextAppearance(TextView textview, Context context, int style) {
|
||||
if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
|
||||
ApiTwentyThreePlus.setTextAppearance(textview, style);
|
||||
} else {
|
||||
textview.setTextAppearance(context, style);
|
||||
}
|
||||
}
|
||||
|
||||
public static void startService(Context context, Intent intent) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
ApiTwentySixPlus.startService(context, intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setFragmentTransactionReorderingAllowed(
|
||||
FragmentTransaction transaction, boolean allowed) {
|
||||
if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
|
||||
ApiTwentySixPlus.setFragmentTransactionReorderingAllowed(transaction, allowed);
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeContentProviderClient(ContentProviderClient client) {
|
||||
if (Version.sdkAboveOrEqual(Version.API24_NOUGAT_70)) {
|
||||
ApiTwentyFourPlus.closeContentProviderClient(client);
|
||||
} else {
|
||||
ApiTwentyOnePlus.closeContentProviderClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAppUserRestricted(Context context) {
|
||||
if (Version.sdkAboveOrEqual(Version.API28_PIE_90)) {
|
||||
return ApiTwentyEightPlus.isAppUserRestricted(context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isAppIdleMode(Context context) {
|
||||
if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
|
||||
return ApiTwentyThreePlus.isAppIdleMode(context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getAppStandbyBucket(Context context) {
|
||||
if (Version.sdkAboveOrEqual(Version.API28_PIE_90)) {
|
||||
return ApiTwentyEightPlus.getAppStandbyBucket(context);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static String getAppStandbyBucketNameFromValue(int bucket) {
|
||||
if (Version.sdkAboveOrEqual(Version.API28_PIE_90)) {
|
||||
return ApiTwentyEightPlus.getAppStandbyBucketNameFromValue(bucket);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setShowWhenLocked(Activity activity, boolean enable) {
|
||||
if (Version.sdkStrictlyBelow(Version.API27_OREO_81)) {
|
||||
ApiTwentyOnePlus.setShowWhenLocked(activity, enable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTurnScreenOn(Activity activity, boolean enable) {
|
||||
if (Version.sdkStrictlyBelow(Version.API27_OREO_81)) {
|
||||
ApiTwentyOnePlus.setTurnScreenOn(activity, enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,13 +19,12 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
|
||||
@TargetApi(8)
|
||||
public class CompatibilityScaleGestureDetector extends ScaleGestureDetector.SimpleOnScaleGestureListener {
|
||||
public class CompatibilityScaleGestureDetector
|
||||
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
|
||||
private ScaleGestureDetector detector;
|
||||
private CompatibilityScaleGestureListener listener;
|
||||
|
||||
|
|
@ -58,4 +57,4 @@ public class CompatibilityScaleGestureDetector extends ScaleGestureDetector.Simp
|
|||
listener = null;
|
||||
detector = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,5 +20,5 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
*/
|
||||
|
||||
public interface CompatibilityScaleGestureListener {
|
||||
public boolean onScale(CompatibilityScaleGestureDetector detector);
|
||||
}
|
||||
boolean onScale(CompatibilityScaleGestureDetector detector);
|
||||
}
|
||||
659
app/src/main/java/org/linphone/contacts/AndroidContact.java
Normal file
659
app/src/main/java/org/linphone/contacts/AndroidContact.java
Normal file
|
|
@ -0,0 +1,659 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
AndroidContact.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.tools.Log;
|
||||
|
||||
class AndroidContact implements Serializable {
|
||||
protected String mAndroidId, mAndroidRawId, mAndroidLookupKey;
|
||||
protected boolean isAndroidRawIdLinphone;
|
||||
|
||||
private transient ArrayList<ContentProviderOperation> mChangesToCommit;
|
||||
|
||||
protected AndroidContact() {
|
||||
mChangesToCommit = new ArrayList<>();
|
||||
isAndroidRawIdLinphone = false;
|
||||
}
|
||||
|
||||
protected String getAndroidId() {
|
||||
return mAndroidId;
|
||||
}
|
||||
|
||||
protected void setAndroidId(String id) {
|
||||
mAndroidId = id;
|
||||
}
|
||||
|
||||
protected String getAndroidLookupKey() {
|
||||
return mAndroidLookupKey;
|
||||
}
|
||||
|
||||
protected void setAndroidLookupKey(String lookupKey) {
|
||||
mAndroidLookupKey = lookupKey;
|
||||
}
|
||||
|
||||
protected Uri getAndroidLookupUri() {
|
||||
return ContactsContract.Contacts.getLookupUri(
|
||||
Long.parseLong(mAndroidId), getAndroidLookupKey());
|
||||
}
|
||||
|
||||
protected boolean isAndroidContact() {
|
||||
return mAndroidId != null;
|
||||
}
|
||||
|
||||
protected void addChangesToCommit(ContentProviderOperation operation) {
|
||||
Log.i("[Contact] Added operation " + operation);
|
||||
mChangesToCommit.add(operation);
|
||||
}
|
||||
|
||||
protected void saveChangesCommited() {
|
||||
if (ContactsManager.getInstance().hasReadContactsAccess() && mChangesToCommit.size() > 0) {
|
||||
try {
|
||||
ContentResolver contentResolver = LinphoneService.instance().getContentResolver();
|
||||
ContentProviderResult[] results =
|
||||
contentResolver.applyBatch(ContactsContract.AUTHORITY, mChangesToCommit);
|
||||
if (results != null
|
||||
&& results.length > 0
|
||||
&& results[0] != null
|
||||
&& results[0].uri != null) {
|
||||
String rawId = String.valueOf(ContentUris.parseId(results[0].uri));
|
||||
if (mAndroidId == null) {
|
||||
Log.i("[Contact] Contact created with RAW ID " + rawId);
|
||||
|
||||
final String[] projection =
|
||||
new String[] {ContactsContract.RawContacts.CONTACT_ID};
|
||||
final Cursor cursor =
|
||||
contentResolver.query(results[0].uri, projection, null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToNext();
|
||||
long contactId = cursor.getLong(0);
|
||||
mAndroidId = String.valueOf(contactId);
|
||||
cursor.close();
|
||||
Log.i("[Contact] Contact created with ID " + mAndroidId);
|
||||
}
|
||||
} else {
|
||||
if (mAndroidRawId == null || !isAndroidRawIdLinphone) {
|
||||
Log.i(
|
||||
"[Contact] Linphone RAW ID "
|
||||
+ rawId
|
||||
+ " created from existing RAW ID "
|
||||
+ mAndroidRawId);
|
||||
mAndroidRawId = rawId;
|
||||
isAndroidRawIdLinphone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("[Contact] Exception while saving changes: " + e);
|
||||
} finally {
|
||||
mChangesToCommit.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void createAndroidContact() {
|
||||
if (LinphoneManager.getInstance()
|
||||
.getContext()
|
||||
.getResources()
|
||||
.getBoolean(R.bool.use_linphone_tag)) {
|
||||
Log.i("[Contact] Creating contact using linphone account type");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
||||
.withValue(
|
||||
RawContacts.ACCOUNT_TYPE,
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.sync_account_type))
|
||||
.withValue(
|
||||
RawContacts.ACCOUNT_NAME,
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.sync_account_name))
|
||||
.withValue(
|
||||
RawContacts.AGGREGATION_MODE,
|
||||
RawContacts.AGGREGATION_MODE_DEFAULT)
|
||||
.build());
|
||||
isAndroidRawIdLinphone = true;
|
||||
} else {
|
||||
Log.i("[Contact] Creating contact using default account type");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
||||
.withValue(RawContacts.ACCOUNT_TYPE, null)
|
||||
.withValue(RawContacts.ACCOUNT_NAME, null)
|
||||
.withValue(
|
||||
RawContacts.AGGREGATION_MODE,
|
||||
RawContacts.AGGREGATION_MODE_DEFAULT)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected void deleteAndroidContact() {
|
||||
ContactsManager.getInstance().delete(mAndroidId);
|
||||
}
|
||||
|
||||
protected Uri getContactThumbnailPictureUri() {
|
||||
Uri person = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(mAndroidId));
|
||||
return Uri.withAppendedPath(person, Contacts.Photo.CONTENT_DIRECTORY);
|
||||
}
|
||||
|
||||
protected Uri getContactPictureUri() {
|
||||
Uri person = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(mAndroidId));
|
||||
return Uri.withAppendedPath(person, Contacts.Photo.DISPLAY_PHOTO);
|
||||
}
|
||||
|
||||
protected void setName(String fn, String ln) {
|
||||
if ((fn == null || fn.isEmpty()) && (ln == null || ln.isEmpty())) {
|
||||
Log.e("[Contact] Can't set both first and last name to null or empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAndroidId == null) {
|
||||
Log.i("[Contact] Setting given & family name " + fn + " " + ln + " to new contact.");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(Data.CONTENT_URI)
|
||||
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(
|
||||
Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
.withValue(CommonDataKinds.StructuredName.GIVEN_NAME, fn)
|
||||
.withValue(CommonDataKinds.StructuredName.FAMILY_NAME, ln)
|
||||
.build());
|
||||
} else {
|
||||
Log.i(
|
||||
"[Contact] Setting given & family name "
|
||||
+ fn
|
||||
+ " "
|
||||
+ ln
|
||||
+ " to existing contact "
|
||||
+ mAndroidId
|
||||
+ " ("
|
||||
+ mAndroidRawId
|
||||
+ ")");
|
||||
String select =
|
||||
ContactsContract.Data.CONTACT_ID
|
||||
+ "=? AND "
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=?";
|
||||
String[] args =
|
||||
new String[] {
|
||||
getAndroidId(),
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
|
||||
};
|
||||
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
|
||||
.withSelection(select, args)
|
||||
.withValue(
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.CommonDataKinds.StructuredName
|
||||
.CONTENT_ITEM_TYPE)
|
||||
.withValue(
|
||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, fn)
|
||||
.withValue(
|
||||
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, ln)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected void addNumberOrAddress(String value, String oldValueToReplace, boolean isSIP) {
|
||||
if (value == null || value.isEmpty()) {
|
||||
Log.e("[Contact] Can't add null or empty number or address");
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldValueToReplace != null) {
|
||||
if (mAndroidId == null) {
|
||||
Log.e("[Contact] Can't update a number or address in non existing contact");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(
|
||||
"[Contact] Updating "
|
||||
+ oldValueToReplace
|
||||
+ " by "
|
||||
+ value
|
||||
+ " in contact "
|
||||
+ mAndroidId
|
||||
+ " ("
|
||||
+ mAndroidRawId
|
||||
+ ")");
|
||||
if (isSIP) {
|
||||
String select =
|
||||
ContactsContract.Data.CONTACT_ID
|
||||
+ "=? AND ("
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=? OR "
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=? OR "
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=?) AND data1=?";
|
||||
String[] args =
|
||||
new String[] {
|
||||
mAndroidId,
|
||||
"vnd.android.cursor.item/org.linphone.profile", // Old value
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.linphone_address_mime_type),
|
||||
ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE,
|
||||
oldValueToReplace
|
||||
};
|
||||
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newUpdate(Data.CONTENT_URI)
|
||||
.withSelection(select, args)
|
||||
.withValue(
|
||||
Data.MIMETYPE,
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.linphone_address_mime_type))
|
||||
.withValue("data1", value) // Value
|
||||
.withValue(
|
||||
"data2",
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.app_name)) // Summary
|
||||
.withValue("data3", value) // Detail
|
||||
.build());
|
||||
} else {
|
||||
String select =
|
||||
ContactsContract.Data.CONTACT_ID
|
||||
+ "=? AND "
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=? AND data1=?";
|
||||
String[] args =
|
||||
new String[] {
|
||||
mAndroidId,
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
|
||||
oldValueToReplace
|
||||
};
|
||||
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newUpdate(Data.CONTENT_URI)
|
||||
.withSelection(select, args)
|
||||
.withValue(Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, value)
|
||||
.withValue(
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE,
|
||||
CommonDataKinds.Phone.TYPE_MOBILE)
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
if (mAndroidId == null) {
|
||||
Log.i("[Contact] Adding number or address " + value + " to new contact.");
|
||||
if (isSIP) {
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(Data.CONTENT_URI)
|
||||
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(
|
||||
Data.MIMETYPE,
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.linphone_address_mime_type))
|
||||
.withValue("data1", value) // Value
|
||||
.withValue(
|
||||
"data2",
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.app_name)) // Summary
|
||||
.withValue("data3", value) // Detail
|
||||
.build());
|
||||
} else {
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(Data.CONTENT_URI)
|
||||
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(
|
||||
Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, value)
|
||||
.withValue(
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE,
|
||||
CommonDataKinds.Phone.TYPE_MOBILE)
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
Log.i(
|
||||
"[Contact] Adding number or address "
|
||||
+ value
|
||||
+ " to existing contact "
|
||||
+ mAndroidId
|
||||
+ " ("
|
||||
+ mAndroidRawId
|
||||
+ ")");
|
||||
if (isSIP) {
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(Data.CONTENT_URI)
|
||||
.withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId)
|
||||
.withValue(
|
||||
Data.MIMETYPE,
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.linphone_address_mime_type))
|
||||
.withValue("data1", value) // Value
|
||||
.withValue(
|
||||
"data2",
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.app_name)) // Summary
|
||||
.withValue("data3", value) // Detail
|
||||
.build());
|
||||
} else {
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(Data.CONTENT_URI)
|
||||
.withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId)
|
||||
.withValue(
|
||||
Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, value)
|
||||
.withValue(
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE,
|
||||
CommonDataKinds.Phone.TYPE_MOBILE)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeNumberOrAddress(String noa, boolean isSIP) {
|
||||
if (noa == null || noa.isEmpty()) {
|
||||
Log.e("[Contact] Can't remove null or empty number or address.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAndroidId == null) {
|
||||
Log.e("[Contact] Can't remove a number or address from non existing contact");
|
||||
return;
|
||||
} else {
|
||||
Log.i(
|
||||
"[Contact] Removing number or address "
|
||||
+ noa
|
||||
+ " from existing contact "
|
||||
+ mAndroidId
|
||||
+ " ("
|
||||
+ mAndroidRawId
|
||||
+ ")");
|
||||
if (isSIP) {
|
||||
String select =
|
||||
ContactsContract.Data.CONTACT_ID
|
||||
+ "=? AND ("
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=? OR "
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=?) AND data1=?";
|
||||
String[] args =
|
||||
new String[] {
|
||||
mAndroidId,
|
||||
ContactsManager.getInstance()
|
||||
.getString(R.string.linphone_address_mime_type),
|
||||
ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE,
|
||||
noa
|
||||
};
|
||||
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
|
||||
.withSelection(select, args)
|
||||
.build());
|
||||
} else {
|
||||
String select =
|
||||
ContactsContract.Data.CONTACT_ID
|
||||
+ "=? AND "
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=? AND data1=?";
|
||||
String[] args =
|
||||
new String[] {
|
||||
mAndroidId,
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
|
||||
noa
|
||||
};
|
||||
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
|
||||
.withSelection(select, args)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setOrganization(String org, String previousValue) {
|
||||
if (org == null || org.isEmpty()) {
|
||||
if (mAndroidId == null) {
|
||||
Log.e("[Contact] Can't set organization to null or empty for new contact");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mAndroidId == null) {
|
||||
Log.i("[Contact] Setting organization " + org + " to new contact.");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(Data.CONTENT_URI)
|
||||
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(
|
||||
Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
|
||||
.withValue(CommonDataKinds.Organization.COMPANY, org)
|
||||
.build());
|
||||
} else {
|
||||
if (previousValue != null) {
|
||||
String select =
|
||||
ContactsContract.Data.CONTACT_ID
|
||||
+ "=? AND "
|
||||
+ ContactsContract.Data.MIMETYPE
|
||||
+ "=? AND "
|
||||
+ ContactsContract.CommonDataKinds.Organization.COMPANY
|
||||
+ "=?";
|
||||
String[] args =
|
||||
new String[] {
|
||||
getAndroidId(),
|
||||
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE,
|
||||
previousValue
|
||||
};
|
||||
|
||||
Log.i(
|
||||
"[Contact] Updating organization "
|
||||
+ org
|
||||
+ " to existing contact "
|
||||
+ mAndroidId
|
||||
+ " ("
|
||||
+ mAndroidRawId
|
||||
+ ")");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
|
||||
.withSelection(select, args)
|
||||
.withValue(
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.CommonDataKinds.Organization
|
||||
.CONTENT_ITEM_TYPE)
|
||||
.withValue(
|
||||
ContactsContract.CommonDataKinds.Organization.COMPANY, org)
|
||||
.build());
|
||||
} else {
|
||||
Log.i(
|
||||
"[Contact] Setting organization "
|
||||
+ org
|
||||
+ " to existing contact "
|
||||
+ mAndroidId
|
||||
+ " ("
|
||||
+ mAndroidRawId
|
||||
+ ")");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||
.withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId)
|
||||
.withValue(
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.CommonDataKinds.Organization
|
||||
.CONTENT_ITEM_TYPE)
|
||||
.withValue(
|
||||
ContactsContract.CommonDataKinds.Organization.COMPANY, org)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setPhoto(byte[] photo) {
|
||||
if (photo == null) {
|
||||
Log.e("[Contact] Can't set null picture.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAndroidId == null) {
|
||||
Log.i("[Contact] Setting picture to new contact.");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo)
|
||||
.build());
|
||||
} else {
|
||||
Log.i(
|
||||
"[Contact] Setting picture to existing contact "
|
||||
+ mAndroidId
|
||||
+ " ("
|
||||
+ mAndroidRawId
|
||||
+ ")");
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||
.withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId)
|
||||
.withValue(
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo)
|
||||
.withValue(ContactsContract.Data.IS_PRIMARY, 1)
|
||||
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected String findRawContactID() {
|
||||
ContentResolver resolver = LinphoneService.instance().getContentResolver();
|
||||
String result = null;
|
||||
String[] projection = {ContactsContract.RawContacts._ID};
|
||||
|
||||
String selection = ContactsContract.RawContacts.CONTACT_ID + "=?";
|
||||
Cursor c =
|
||||
resolver.query(
|
||||
ContactsContract.RawContacts.CONTENT_URI,
|
||||
projection,
|
||||
selection,
|
||||
new String[] {mAndroidId},
|
||||
null);
|
||||
if (c != null) {
|
||||
if (c.moveToFirst()) {
|
||||
result = c.getString(c.getColumnIndex(ContactsContract.RawContacts._ID));
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void createRawLinphoneContactFromExistingAndroidContactIfNeeded(String fullName) {
|
||||
if (LinphoneManager.getInstance()
|
||||
.getContext()
|
||||
.getResources()
|
||||
.getBoolean(R.bool.use_linphone_tag)) {
|
||||
if (mAndroidId != null && (mAndroidRawId == null || !isAndroidRawIdLinphone)) {
|
||||
if (mAndroidRawId == null) {
|
||||
Log.i("[Contact] RAW ID not found for contact " + mAndroidId);
|
||||
mAndroidRawId = findRawContactID();
|
||||
}
|
||||
Log.i("[Contact] Found RAW ID for contact " + mAndroidId + " : " + mAndroidRawId);
|
||||
|
||||
String linphoneRawId = findLinphoneRawContactId();
|
||||
if (linphoneRawId == null) {
|
||||
Log.i("[Contact] Linphone RAW ID not found for contact " + mAndroidId);
|
||||
createRawLinphoneContactFromExistingAndroidContact();
|
||||
} else {
|
||||
Log.i(
|
||||
"[Contact] Linphone RAW ID found for contact "
|
||||
+ mAndroidId
|
||||
+ " : "
|
||||
+ linphoneRawId);
|
||||
mAndroidRawId = linphoneRawId;
|
||||
}
|
||||
isAndroidRawIdLinphone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createRawLinphoneContactFromExistingAndroidContact() {
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
|
||||
.withValue(
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE,
|
||||
ContactsManager.getInstance().getString(R.string.sync_account_type))
|
||||
.withValue(
|
||||
ContactsContract.RawContacts.ACCOUNT_NAME,
|
||||
ContactsManager.getInstance().getString(R.string.sync_account_name))
|
||||
.withValue(
|
||||
ContactsContract.RawContacts.AGGREGATION_MODE,
|
||||
ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
|
||||
.build());
|
||||
|
||||
addChangesToCommit(
|
||||
ContentProviderOperation.newUpdate(
|
||||
ContactsContract.AggregationExceptions.CONTENT_URI)
|
||||
.withValue(
|
||||
ContactsContract.AggregationExceptions.TYPE,
|
||||
ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER)
|
||||
.withValue(
|
||||
ContactsContract.AggregationExceptions.RAW_CONTACT_ID1,
|
||||
mAndroidRawId)
|
||||
.withValueBackReference(
|
||||
ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, 0)
|
||||
.build());
|
||||
|
||||
Log.i(
|
||||
"[Contact] Creating linphone RAW contact for contact "
|
||||
+ mAndroidId
|
||||
+ " linked with existing RAW contact "
|
||||
+ mAndroidRawId);
|
||||
saveChangesCommited();
|
||||
}
|
||||
|
||||
private String findLinphoneRawContactId() {
|
||||
ContentResolver resolver = LinphoneService.instance().getContentResolver();
|
||||
String result = null;
|
||||
String[] projection = {ContactsContract.RawContacts._ID};
|
||||
|
||||
String selection =
|
||||
ContactsContract.RawContacts.CONTACT_ID
|
||||
+ "=? AND "
|
||||
+ ContactsContract.RawContacts.ACCOUNT_TYPE
|
||||
+ "=?";
|
||||
Cursor c =
|
||||
resolver.query(
|
||||
ContactsContract.RawContacts.CONTENT_URI,
|
||||
projection,
|
||||
selection,
|
||||
new String[] {
|
||||
mAndroidId,
|
||||
ContactsManager.getInstance().getString(R.string.sync_account_type)
|
||||
},
|
||||
null);
|
||||
if (c != null) {
|
||||
if (c.moveToFirst()) {
|
||||
result = c.getString(c.getColumnIndex(ContactsContract.RawContacts._ID));
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
244
app/src/main/java/org/linphone/contacts/AsyncContactsLoader.java
Normal file
244
app/src/main/java/org/linphone/contacts/AsyncContactsLoader.java
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
AsyncContactsLoader.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
import android.provider.ContactsContract;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.Friend;
|
||||
import org.linphone.core.FriendList;
|
||||
import org.linphone.core.PresenceBasicStatus;
|
||||
import org.linphone.core.PresenceModel;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.AsyncContactsData> {
|
||||
@SuppressLint("InlinedApi")
|
||||
public static final String[] PROJECTION = {
|
||||
ContactsContract.Data.CONTACT_ID,
|
||||
ContactsContract.Contacts.LOOKUP_KEY,
|
||||
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
"data1", // Company, Phone or SIP Address
|
||||
"data2", // ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
|
||||
"data3", // ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
|
||||
"data4", // Normalized phone number
|
||||
};
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public AsyncContactsLoader(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Log.i("[Contacts Manager] Synchronization started");
|
||||
if (mContext == null) {
|
||||
mContext = LinphoneService.instance().getApplicationContext();
|
||||
}
|
||||
|
||||
if (LinphonePreferences.instance().isFriendlistsubscriptionEnabled()) {
|
||||
String rls = mContext.getString(R.string.rls_uri);
|
||||
for (FriendList list : LinphoneManager.getLc().getFriendsLists()) {
|
||||
if (rls != null
|
||||
&& (list.getRlsAddress() == null
|
||||
|| !list.getRlsAddress().asStringUriOnly().equals(rls))) {
|
||||
list.setRlsUri(rls);
|
||||
}
|
||||
list.addListener(ContactsManager.getInstance());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AsyncContactsData doInBackground(Void... params) {
|
||||
Log.i("[Contacts Manager] Background synchronization started");
|
||||
Cursor c =
|
||||
mContext.getContentResolver()
|
||||
.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
PROJECTION,
|
||||
ContactsContract.Data.IN_VISIBLE_GROUP + " == 1",
|
||||
null,
|
||||
null);
|
||||
|
||||
HashMap<String, LinphoneContact> androidContactsCache = new HashMap<>();
|
||||
AsyncContactsData data = new AsyncContactsData();
|
||||
List<String> nativeIds = new ArrayList<>();
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
for (FriendList list : lc.getFriendsLists()) {
|
||||
for (Friend friend : list.getFriends()) {
|
||||
if (isCancelled()) return data;
|
||||
|
||||
LinphoneContact contact = (LinphoneContact) friend.getUserData();
|
||||
if (contact != null) {
|
||||
contact.clearAddresses();
|
||||
if (contact.getAndroidId() != null) {
|
||||
androidContactsCache.put(contact.getAndroidId(), contact);
|
||||
nativeIds.add(contact.getAndroidId());
|
||||
}
|
||||
} else {
|
||||
if (friend.getRefKey() != null) {
|
||||
// Friend has a refkey but no LinphoneContact => represents a
|
||||
// native contact stored in db from a previous version of Linphone,
|
||||
// remove it
|
||||
list.removeFriend(friend);
|
||||
} else {
|
||||
// No refkey so it's a standalone contact
|
||||
contact = new LinphoneContact();
|
||||
contact.setFriend(friend);
|
||||
contact.syncValuesFromFriend();
|
||||
data.contacts.add(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
while (c.moveToNext()) {
|
||||
if (isCancelled()) return data;
|
||||
|
||||
String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
|
||||
String lookupKey =
|
||||
c.getString(c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
|
||||
|
||||
LinphoneContact contact = androidContactsCache.get(id);
|
||||
if (contact == null) {
|
||||
nativeIds.add(id);
|
||||
contact = new LinphoneContact();
|
||||
contact.setAndroidId(id);
|
||||
contact.setAndroidLookupKey(lookupKey);
|
||||
androidContactsCache.put(id, contact);
|
||||
}
|
||||
|
||||
contact.syncValuesFromAndroidCusor(c);
|
||||
}
|
||||
c.close();
|
||||
|
||||
for (FriendList list : lc.getFriendsLists()) {
|
||||
for (Friend friend : list.getFriends()) {
|
||||
if (isCancelled()) return data;
|
||||
|
||||
LinphoneContact contact = (LinphoneContact) friend.getUserData();
|
||||
if (contact != null && contact.isAndroidContact()) {
|
||||
String id = contact.getAndroidId();
|
||||
if (id != null && !nativeIds.contains(id)) {
|
||||
// Has been removed since last fetch
|
||||
androidContactsCache.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nativeIds.clear();
|
||||
}
|
||||
|
||||
for (LinphoneContact contact : androidContactsCache.values()) {
|
||||
if (isCancelled()) return data;
|
||||
|
||||
if (contact.getFullName() == null) {
|
||||
for (LinphoneNumberOrAddress noa : contact.getNumbersOrAddresses()) {
|
||||
if (noa.isSIPAddress()) {
|
||||
contact.setFullName(LinphoneUtils.getAddressDisplayName(noa.getValue()));
|
||||
Log.w(
|
||||
"[Contacts Manager] Couldn't find a display name for contact "
|
||||
+ contact.getFullName()
|
||||
+ ", used SIP address display name / username instead...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (contact.getFriend() != null) {
|
||||
for (LinphoneNumberOrAddress noa : contact.getNumbersOrAddresses()) {
|
||||
PresenceModel pm =
|
||||
contact.getFriend().getPresenceModelForUriOrTel(noa.getValue());
|
||||
if (pm != null && pm.getBasicStatus().equals(PresenceBasicStatus.Open)) {
|
||||
data.sipContacts.add(contact);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mContext.getResources().getBoolean(R.bool.hide_sip_contacts_without_presence)) {
|
||||
if (contact.hasAddress() && !data.sipContacts.contains(contact)) {
|
||||
data.sipContacts.add(contact);
|
||||
}
|
||||
}
|
||||
data.contacts.add(contact);
|
||||
}
|
||||
|
||||
androidContactsCache.clear();
|
||||
|
||||
Collections.sort(data.contacts);
|
||||
Collections.sort(data.sipContacts);
|
||||
|
||||
Log.i("[Contacts Manager] Background synchronization finished");
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(AsyncContactsData data) {
|
||||
for (LinphoneContact contact : data.contacts) {
|
||||
contact.createOrUpdateFriendFromNativeContact();
|
||||
}
|
||||
|
||||
// Now that contact fetching is asynchronous, this is required to ensure
|
||||
// presence subscription event will be sent with all friends
|
||||
if (LinphonePreferences.instance().isFriendlistsubscriptionEnabled()) {
|
||||
for (FriendList list : LinphoneManager.getLc().getFriendsLists()) {
|
||||
list.updateSubscriptions();
|
||||
}
|
||||
}
|
||||
|
||||
ContactsManager.getInstance().setContacts(data.contacts);
|
||||
ContactsManager.getInstance().setSipContacts(data.sipContacts);
|
||||
|
||||
for (ContactsUpdatedListener listener :
|
||||
ContactsManager.getInstance().getContactsListeners()) {
|
||||
listener.onContactsUpdated();
|
||||
}
|
||||
Log.i("[Contacts Manager] Synchronization finished");
|
||||
}
|
||||
|
||||
class AsyncContactsData {
|
||||
final List<LinphoneContact> contacts;
|
||||
final List<LinphoneContact> sipContacts;
|
||||
|
||||
AsyncContactsData() {
|
||||
contacts = new ArrayList<>();
|
||||
sipContacts = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,64 +20,73 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
*/
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Factory;
|
||||
import org.linphone.core.SearchResult;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.linphone.core.FriendCapability;
|
||||
|
||||
public class ContactAddress implements Serializable {
|
||||
private LinphoneContact contact;
|
||||
private SearchResult result;
|
||||
private String address;
|
||||
private String phoneNumber;
|
||||
private boolean isLinphoneContact;
|
||||
private boolean isSelect = false;
|
||||
private boolean isAdmin = false;
|
||||
private transient View view;
|
||||
private LinphoneContact mContact;
|
||||
private String mAddress;
|
||||
private String mPhoneNumber;
|
||||
private boolean mIsLinphoneContact;
|
||||
private boolean mIsSelect = false;
|
||||
private boolean mIsAdmin = false;
|
||||
private transient View mView;
|
||||
|
||||
public ContactAddress(LinphoneContact c, String a, String pn, boolean isLC) {
|
||||
init(c, a, pn, isLC);
|
||||
}
|
||||
|
||||
public ContactAddress(LinphoneContact c, String a, String pn, boolean isLC, boolean isAdmin) {
|
||||
init(c, a, pn, isLC);
|
||||
mIsAdmin = isAdmin;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return isAdmin;
|
||||
return mIsAdmin;
|
||||
}
|
||||
|
||||
public void setAdmin(boolean admin) {
|
||||
isAdmin = admin;
|
||||
mIsAdmin = admin;
|
||||
}
|
||||
|
||||
public boolean isSelect() {
|
||||
return isSelect;
|
||||
return mIsSelect;
|
||||
}
|
||||
|
||||
public void setView(View v) {
|
||||
view = v;
|
||||
public void setSelect(boolean select) {
|
||||
mIsSelect = select;
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return view;
|
||||
return mView;
|
||||
}
|
||||
|
||||
public void setView(View v) {
|
||||
mView = v;
|
||||
}
|
||||
|
||||
public LinphoneContact getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
public SearchResult getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(SearchResult result) {
|
||||
this.result = result;
|
||||
return mContact;
|
||||
}
|
||||
|
||||
public String getAddressAsDisplayableString() {
|
||||
Address addr = getAddress();
|
||||
if (addr != null && addr.getUsername() != null) return addr.asStringUriOnly();
|
||||
return address;
|
||||
return mAddress;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
String presence = contact.getPresenceModelForUriOrTel((phoneNumber != null && !phoneNumber.isEmpty()) ? phoneNumber : address);
|
||||
Address addr = Factory.instance().createAddress(presence != null ? presence : address);
|
||||
String presence = null;
|
||||
if (mContact != null) {
|
||||
presence =
|
||||
mContact.getContactFromPresenceModelForUriOrTel(
|
||||
(mPhoneNumber != null && !mPhoneNumber.isEmpty())
|
||||
? mPhoneNumber
|
||||
: mAddress);
|
||||
}
|
||||
Address addr = Factory.instance().createAddress(presence != null ? presence : mAddress);
|
||||
// Remove the user=phone URI param if existing, it will break everything otherwise
|
||||
if (addr.hasUriParam("user")) {
|
||||
addr.removeUriParam("user");
|
||||
|
|
@ -86,8 +95,8 @@ public class ContactAddress implements Serializable {
|
|||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
if (address != null) {
|
||||
Address addr = Factory.instance().createAddress(address);
|
||||
if (mAddress != null) {
|
||||
Address addr = Factory.instance().createAddress(mAddress);
|
||||
if (addr != null) {
|
||||
return addr.getDisplayName();
|
||||
}
|
||||
|
|
@ -96,8 +105,8 @@ public class ContactAddress implements Serializable {
|
|||
}
|
||||
|
||||
public String getUsername() {
|
||||
if (address != null) {
|
||||
Address addr = Factory.instance().createAddress(address);
|
||||
if (mAddress != null) {
|
||||
Address addr = Factory.instance().createAddress(mAddress);
|
||||
if (addr != null) {
|
||||
return addr.getUsername();
|
||||
}
|
||||
|
|
@ -106,31 +115,18 @@ public class ContactAddress implements Serializable {
|
|||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
return mPhoneNumber;
|
||||
}
|
||||
|
||||
public void setSelect(boolean select) {
|
||||
isSelect = select;
|
||||
}
|
||||
|
||||
public boolean isLinphoneContact() {
|
||||
return isLinphoneContact;
|
||||
public boolean hasCapability(FriendCapability capability) {
|
||||
return mContact != null && mContact.hasFriendCapability(capability);
|
||||
}
|
||||
|
||||
private void init(LinphoneContact c, String a, String pn, boolean isLC) {
|
||||
contact = c;
|
||||
address = a;
|
||||
phoneNumber = pn;
|
||||
isLinphoneContact = isLC;
|
||||
}
|
||||
|
||||
public ContactAddress(LinphoneContact c, String a, String pn, boolean isLC) {
|
||||
init(c, a, pn, isLC);
|
||||
}
|
||||
|
||||
public ContactAddress(LinphoneContact c, String a, String pn, boolean isLC, boolean isAdmin) {
|
||||
init(c, a, pn, isLC);
|
||||
this.isAdmin = isAdmin;
|
||||
mContact = c;
|
||||
mAddress = a;
|
||||
mPhoneNumber = pn;
|
||||
mIsLinphoneContact = isLC;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -138,8 +134,8 @@ public class ContactAddress implements Serializable {
|
|||
if (other == null) return false;
|
||||
if (other == this) return true;
|
||||
if (!(other instanceof ContactAddress)) return false;
|
||||
if (((ContactAddress) other).getAddressAsDisplayableString() == this.getAddressAsDisplayableString())
|
||||
return true;
|
||||
return false;
|
||||
return ((ContactAddress) other)
|
||||
.getAddressAsDisplayableString()
|
||||
.equals(getAddressAsDisplayableString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
ContactDetailsFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TextView;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.ChatRoomBackend;
|
||||
import org.linphone.core.ChatRoomListenerStub;
|
||||
import org.linphone.core.ChatRoomParams;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.Factory;
|
||||
import org.linphone.core.FriendCapability;
|
||||
import org.linphone.core.PresenceBasicStatus;
|
||||
import org.linphone.core.PresenceModel;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ContactDetailsFragment extends Fragment
|
||||
implements OnClickListener, ContactsUpdatedListener {
|
||||
private LinphoneContact mContact;
|
||||
private ImageView mEditContact, mDeleteContact, mBack;
|
||||
private TextView mOrganization;
|
||||
private RelativeLayout mWaitLayout;
|
||||
private LayoutInflater mInflater;
|
||||
private View mView;
|
||||
private boolean mDisplayChatAddressOnly = false;
|
||||
private ChatRoom mChatRoom;
|
||||
private ChatRoomListenerStub mChatRoomCreationListener;
|
||||
|
||||
private final OnClickListener mDialListener =
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
String tag = (String) v.getTag();
|
||||
LinphoneActivity.instance()
|
||||
.setAddresGoToDialerAndCall(tag, mContact.getFullName());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final OnClickListener mChatListener =
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
String tag = (String) v.getTag();
|
||||
Core lc = LinphoneManager.getLc();
|
||||
Address participant = Factory.instance().createAddress(tag);
|
||||
ProxyConfig defaultProxyConfig = lc.getDefaultProxyConfig();
|
||||
boolean isSecured = v.getId() == R.id.contact_chat_secured;
|
||||
|
||||
if (defaultProxyConfig != null) {
|
||||
ChatRoom room =
|
||||
lc.findOneToOneChatRoom(
|
||||
defaultProxyConfig.getContact(),
|
||||
participant,
|
||||
isSecured);
|
||||
if (room != null) {
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
room.getLocalAddress().asStringUriOnly(),
|
||||
room.getPeerAddress().asStringUriOnly(),
|
||||
null);
|
||||
} else {
|
||||
if (defaultProxyConfig.getConferenceFactoryUri() != null
|
||||
&& (isSecured
|
||||
|| !LinphonePreferences.instance()
|
||||
.useBasicChatRoomFor1To1())) {
|
||||
mWaitLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
ChatRoomParams params = lc.createDefaultChatRoomParams();
|
||||
params.enableEncryption(isSecured);
|
||||
params.enableGroup(false);
|
||||
// We don't want a basic chat room,
|
||||
// so if isSecured is false we have to set this manually
|
||||
params.setBackend(ChatRoomBackend.FlexisipChat);
|
||||
|
||||
Address participants[] = new Address[1];
|
||||
participants[0] = participant;
|
||||
|
||||
mChatRoom =
|
||||
lc.createChatRoom(
|
||||
params,
|
||||
getString(R.string.dummy_group_chat_subject),
|
||||
participants);
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.addListener(mChatRoomCreationListener);
|
||||
} else {
|
||||
Log.w(
|
||||
"[Contact Details Fragment] createChatRoom returned null...");
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
room = lc.getChatRoom(participant);
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
room.getLocalAddress().asStringUriOnly(),
|
||||
room.getPeerAddress().asStringUriOnly(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mContact = (LinphoneContact) getArguments().getSerializable("Contact");
|
||||
|
||||
this.mInflater = inflater;
|
||||
mView = inflater.inflate(R.layout.contact, container, false);
|
||||
|
||||
if (getArguments() != null) {
|
||||
mDisplayChatAddressOnly = getArguments().getBoolean("ChatAddressOnly");
|
||||
}
|
||||
|
||||
mWaitLayout = mView.findViewById(R.id.waitScreen);
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
|
||||
mEditContact = mView.findViewById(R.id.editContact);
|
||||
mEditContact.setOnClickListener(this);
|
||||
|
||||
mDeleteContact = mView.findViewById(R.id.deleteContact);
|
||||
mDeleteContact.setOnClickListener(this);
|
||||
|
||||
mOrganization = mView.findViewById(R.id.contactOrganization);
|
||||
boolean isOrgVisible = getResources().getBoolean(R.bool.display_contact_organization);
|
||||
String org = mContact.getOrganization();
|
||||
if (org != null && !org.isEmpty() && isOrgVisible) {
|
||||
mOrganization.setText(org);
|
||||
} else {
|
||||
mOrganization.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mBack = mView.findViewById(R.id.back);
|
||||
if (getResources().getBoolean(R.bool.isTablet)) {
|
||||
mBack.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mBack.setOnClickListener(this);
|
||||
}
|
||||
|
||||
mChatRoomCreationListener =
|
||||
new ChatRoomListenerStub() {
|
||||
@Override
|
||||
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
|
||||
if (newState == ChatRoom.State.Created) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
cr.getLocalAddress().asStringUriOnly(),
|
||||
cr.getPeerAddress().asStringUriOnly(),
|
||||
null);
|
||||
} else if (newState == ChatRoom.State.CreationFailed) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
LinphoneActivity.instance().displayChatRoomError();
|
||||
Log.e(
|
||||
"Group chat room for address "
|
||||
+ cr.getPeerAddress()
|
||||
+ " has failed !");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
||||
public void changeDisplayedContact(LinphoneContact newContact) {
|
||||
mContact = newContact;
|
||||
displayContact(mInflater, mView);
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
private void displayContact(LayoutInflater inflater, View view) {
|
||||
ContactAvatar.displayAvatar(mContact, view.findViewById(R.id.avatar_layout));
|
||||
|
||||
TextView contactName = view.findViewById(R.id.contact_name);
|
||||
contactName.setText(mContact.getFullName());
|
||||
mOrganization.setText(
|
||||
(mContact.getOrganization() != null) ? mContact.getOrganization() : "");
|
||||
|
||||
TableLayout controls = view.findViewById(R.id.controls);
|
||||
controls.removeAllViews();
|
||||
for (LinphoneNumberOrAddress noa : mContact.getNumbersOrAddresses()) {
|
||||
boolean skip = false;
|
||||
View v = inflater.inflate(R.layout.contact_control_row, null);
|
||||
|
||||
String value = noa.getValue();
|
||||
String displayednumberOrAddress =
|
||||
LinphoneUtils.getDisplayableUsernameFromAddress(value);
|
||||
|
||||
TextView label = v.findViewById(R.id.address_label);
|
||||
if (noa.isSIPAddress()) {
|
||||
label.setText(R.string.sip_address);
|
||||
skip |= getResources().getBoolean(R.bool.hide_contact_sip_addresses);
|
||||
} else {
|
||||
label.setText(R.string.phone_number);
|
||||
skip |= getResources().getBoolean(R.bool.hide_contact_phone_numbers);
|
||||
}
|
||||
|
||||
TextView tv = v.findViewById(R.id.numeroOrAddress);
|
||||
tv.setText(displayednumberOrAddress);
|
||||
tv.setSelected(true);
|
||||
|
||||
ProxyConfig lpc = LinphoneManager.getLc().getDefaultProxyConfig();
|
||||
if (lpc != null) {
|
||||
String username = lpc.normalizePhoneNumber(displayednumberOrAddress);
|
||||
if (username != null) {
|
||||
value = LinphoneUtils.getFullAddressFromUsername(username);
|
||||
}
|
||||
}
|
||||
|
||||
v.findViewById(R.id.friendLinphone).setVisibility(View.GONE);
|
||||
if (mContact.getFriend() != null) {
|
||||
PresenceModel pm = mContact.getFriend().getPresenceModelForUriOrTel(noa.getValue());
|
||||
if (pm != null && pm.getBasicStatus().equals(PresenceBasicStatus.Open)) {
|
||||
v.findViewById(R.id.friendLinphone).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
if (getResources()
|
||||
.getBoolean(R.bool.hide_numbers_and_addresses_without_presence)) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v.findViewById(R.id.inviteFriend).setVisibility(View.GONE);
|
||||
if (!noa.isSIPAddress()
|
||||
&& v.findViewById(R.id.friendLinphone).getVisibility() == View.GONE
|
||||
&& !getResources().getBoolean(R.bool.hide_invite_contact)) {
|
||||
v.findViewById(R.id.inviteFriend).setVisibility(View.VISIBLE);
|
||||
v.findViewById(R.id.inviteFriend).setTag(noa.getNormalizedPhone());
|
||||
v.findViewById(R.id.inviteFriend)
|
||||
.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String number = (String) v.getTag();
|
||||
Intent smsIntent = new Intent(Intent.ACTION_SENDTO);
|
||||
smsIntent.putExtra("address", number);
|
||||
smsIntent.setData(Uri.parse("smsto:" + number));
|
||||
String text =
|
||||
getString(R.string.invite_friend_text)
|
||||
.replace(
|
||||
"%s",
|
||||
getString(R.string.download_link));
|
||||
smsIntent.putExtra("sms_body", text);
|
||||
startActivity(smsIntent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String contactAddress = mContact.getContactFromPresenceModelForUriOrTel(noa.getValue());
|
||||
if (!mDisplayChatAddressOnly) {
|
||||
v.findViewById(R.id.contact_call).setOnClickListener(mDialListener);
|
||||
if (contactAddress != null) {
|
||||
v.findViewById(R.id.contact_call).setTag(contactAddress);
|
||||
} else {
|
||||
v.findViewById(R.id.contact_call).setTag(value);
|
||||
}
|
||||
} else {
|
||||
v.findViewById(R.id.contact_call).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
v.findViewById(R.id.contact_chat).setOnClickListener(mChatListener);
|
||||
v.findViewById(R.id.contact_chat_secured).setOnClickListener(mChatListener);
|
||||
if (contactAddress != null) {
|
||||
v.findViewById(R.id.contact_chat).setTag(contactAddress);
|
||||
v.findViewById(R.id.contact_chat_secured).setTag(contactAddress);
|
||||
} else {
|
||||
v.findViewById(R.id.contact_chat).setTag(value);
|
||||
v.findViewById(R.id.contact_chat_secured).setTag(value);
|
||||
}
|
||||
|
||||
if (v.findViewById(R.id.friendLinphone).getVisibility() == View.VISIBLE
|
||||
&& mContact.hasPresenceModelForUriOrTelCapability(
|
||||
noa.getValue(), FriendCapability.LimeX3Dh)) {
|
||||
v.findViewById(R.id.contact_chat_secured).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
v.findViewById(R.id.contact_chat_secured).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (getResources().getBoolean(R.bool.disable_chat)) {
|
||||
v.findViewById(R.id.contact_chat).setVisibility(View.GONE);
|
||||
v.findViewById(R.id.contact_chat_secured).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
controls.addView(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactsUpdated() {
|
||||
LinphoneContact contact =
|
||||
ContactsManager.getInstance().findContactFromAndroidId(mContact.getAndroidId());
|
||||
if (contact != null) {
|
||||
changeDisplayedContact(contact);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
ContactsManager.getInstance().addContactsListener(this);
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CONTACT_DETAIL);
|
||||
}
|
||||
displayContact(mInflater, mView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.removeListener(mChatRoomCreationListener);
|
||||
}
|
||||
ContactsManager.getInstance().removeContactsListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
|
||||
if (id == R.id.editContact) {
|
||||
ContactsManager.getInstance().editContact(getActivity(), mContact, null);
|
||||
} else if (id == R.id.deleteContact) {
|
||||
final Dialog dialog =
|
||||
LinphoneActivity.instance().displayDialog(getString(R.string.delete_text));
|
||||
Button delete = dialog.findViewById(R.id.dialog_delete_button);
|
||||
Button cancel = dialog.findViewById(R.id.dialog_cancel_button);
|
||||
|
||||
delete.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mContact.delete();
|
||||
// To ensure removed contact won't appear in the contacts list anymore
|
||||
ContactsManager.getInstance().fetchContactsAsync();
|
||||
LinphoneActivity.instance().displayContacts(false);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
cancel.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
} else if (id == R.id.back) {
|
||||
getFragmentManager().popBackStackImmediate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,676 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
ContactEditorFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.ContactsContract.DisplayPhoto;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.mediastream.Version;
|
||||
import org.linphone.utils.FileUtils;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ContactEditorFragment extends Fragment {
|
||||
private static final int ADD_PHOTO = 1337;
|
||||
private static final int PHOTO_SIZE = 128;
|
||||
|
||||
private View mView;
|
||||
private ImageView mCancel, mDeleteContact, mOk;
|
||||
private ImageView mAddNumber, mAddSipAddress, mContactPicture;
|
||||
private LinearLayout mPhoneNumbersSection, mSipAddressesSection;
|
||||
private EditText mFirstName, mLastName, mOrganization;
|
||||
private LayoutInflater mInflater;
|
||||
private boolean mIsNewContact;
|
||||
private LinphoneContact mContact;
|
||||
private List<LinphoneNumberOrAddress> mNumbersAndAddresses;
|
||||
private int mFirstSipAddressIndex = -1;
|
||||
private LinearLayout mSipAddresses, mNumbers;
|
||||
private String mNewSipOrNumberToAdd, mNewDisplayName;
|
||||
private Uri mPickedPhotoForContactUri;
|
||||
private byte[] mPhotoToAdd;
|
||||
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mInflater = inflater;
|
||||
|
||||
mContact = null;
|
||||
mIsNewContact = true;
|
||||
|
||||
if (getArguments() != null) {
|
||||
Serializable obj = getArguments().getSerializable("Contact");
|
||||
if (obj != null) {
|
||||
mContact = (LinphoneContact) obj;
|
||||
mContact.createRawLinphoneContactFromExistingAndroidContactIfNeeded(
|
||||
mContact.getFullName());
|
||||
mIsNewContact = false;
|
||||
if (getArguments().getString("NewSipAdress") != null) {
|
||||
mNewSipOrNumberToAdd = getArguments().getString("NewSipAdress");
|
||||
}
|
||||
if (getArguments().getString("NewDisplayName") != null) {
|
||||
mNewDisplayName = getArguments().getString("NewDisplayName");
|
||||
}
|
||||
} else if (getArguments().getString("NewSipAdress") != null) {
|
||||
mNewSipOrNumberToAdd = getArguments().getString("NewSipAdress");
|
||||
if (getArguments().getString("NewDisplayName") != null) {
|
||||
mNewDisplayName = getArguments().getString("NewDisplayName");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mView = inflater.inflate(R.layout.contact_edit, container, false);
|
||||
|
||||
mPhoneNumbersSection = mView.findViewById(R.id.phone_numbers);
|
||||
if (getResources().getBoolean(R.bool.hide_phone_numbers_in_editor)
|
||||
|| !ContactsManager.getInstance().hasReadContactsAccess()) {
|
||||
// Currently linphone friends don't support phone mNumbers, so hide them
|
||||
mPhoneNumbersSection.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mSipAddressesSection = mView.findViewById(R.id.sip_addresses);
|
||||
if (getResources().getBoolean(R.bool.hide_sip_addresses_in_editor)) {
|
||||
mSipAddressesSection.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mDeleteContact = mView.findViewById(R.id.delete_contact);
|
||||
|
||||
mCancel = mView.findViewById(R.id.cancel);
|
||||
mCancel.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getFragmentManager().popBackStackImmediate();
|
||||
}
|
||||
});
|
||||
|
||||
mOk = mView.findViewById(R.id.ok);
|
||||
mOk.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mIsNewContact) {
|
||||
boolean areAllFielsEmpty = true;
|
||||
for (LinphoneNumberOrAddress nounoa : mNumbersAndAddresses) {
|
||||
if (nounoa.getValue() != null && !nounoa.getValue().equals("")) {
|
||||
areAllFielsEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (areAllFielsEmpty) {
|
||||
getFragmentManager().popBackStackImmediate();
|
||||
return;
|
||||
}
|
||||
mContact = LinphoneContact.createContact();
|
||||
}
|
||||
|
||||
mContact.setFirstNameAndLastName(
|
||||
mFirstName.getText().toString(),
|
||||
mLastName.getText().toString(),
|
||||
true);
|
||||
|
||||
if (mPhotoToAdd != null) {
|
||||
mContact.setPhoto(mPhotoToAdd);
|
||||
}
|
||||
|
||||
for (LinphoneNumberOrAddress noa : mNumbersAndAddresses) {
|
||||
if (noa.getValue() == null || noa.getValue().isEmpty()) {
|
||||
if (noa.getOldValue() != null && !noa.getOldValue().isEmpty()) {
|
||||
Log.i("[Contact Editor] Removing number " + noa.getOldValue());
|
||||
mContact.removeNumberOrAddress(noa);
|
||||
}
|
||||
} else {
|
||||
if (noa.getOldValue() != null
|
||||
&& noa.getOldValue().equals(noa.getValue())) {
|
||||
Log.i(
|
||||
"[Contact Editor] Keeping existing number "
|
||||
+ noa.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (noa.isSIPAddress()) {
|
||||
noa.setValue(
|
||||
LinphoneUtils.getFullAddressFromUsername(
|
||||
noa.getValue()));
|
||||
}
|
||||
Log.i("[Contact Editor] Adding new number " + noa.getValue());
|
||||
mContact.addOrUpdateNumberOrAddress(noa);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mOrganization.getText().toString().isEmpty() || !mIsNewContact) {
|
||||
mContact.setOrganization(mOrganization.getText().toString(), true);
|
||||
}
|
||||
|
||||
mContact.save();
|
||||
|
||||
if (mIsNewContact) {
|
||||
// Ensure fetch will be done so the new contact appears in the contacts
|
||||
// list: contacts content observer may not be notified if contacts sync
|
||||
// is disabled at system level
|
||||
ContactsManager.getInstance().fetchContactsAsync();
|
||||
}
|
||||
|
||||
getFragmentManager().popBackStackImmediate();
|
||||
if (mIsNewContact || LinphoneActivity.instance().isTablet()) {
|
||||
LinphoneActivity.instance().displayContact(mContact, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mLastName = mView.findViewById(R.id.contactLastName);
|
||||
// Hack to display keyboard when touching focused edittext on Nexus One
|
||||
if (Version.sdkStrictlyBelow(Version.API11_HONEYCOMB_30)) {
|
||||
mLastName.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager)
|
||||
LinphoneActivity.instance()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
mLastName.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
mOk.setEnabled(
|
||||
mLastName.getText().length() > 0
|
||||
|| mFirstName.getText().length() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
mFirstName = mView.findViewById(R.id.contactFirstName);
|
||||
mFirstName.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
mOk.setEnabled(
|
||||
mFirstName.getText().length() > 0
|
||||
|| mLastName.getText().length() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
mOrganization = mView.findViewById(R.id.contactOrganization);
|
||||
boolean isOrgVisible = getResources().getBoolean(R.bool.display_contact_organization);
|
||||
if (!isOrgVisible) {
|
||||
mOrganization.setVisibility(View.GONE);
|
||||
mView.findViewById(R.id.contactOrganizationTitle).setVisibility(View.GONE);
|
||||
} else {
|
||||
if (!mIsNewContact) {
|
||||
mOrganization.setText(mContact.getOrganization());
|
||||
}
|
||||
}
|
||||
|
||||
if (!mIsNewContact) {
|
||||
String fn = mContact.getFirstName();
|
||||
String ln = mContact.getLastName();
|
||||
if (fn != null || ln != null) {
|
||||
mFirstName.setText(fn);
|
||||
mLastName.setText(ln);
|
||||
} else {
|
||||
mLastName.setText(mContact.getFullName());
|
||||
mFirstName.setText("");
|
||||
}
|
||||
|
||||
mDeleteContact.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final Dialog dialog =
|
||||
LinphoneActivity.instance()
|
||||
.displayDialog(getString(R.string.delete_text));
|
||||
Button delete = dialog.findViewById(R.id.dialog_delete_button);
|
||||
Button cancel = dialog.findViewById(R.id.dialog_cancel_button);
|
||||
|
||||
delete.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mContact.delete();
|
||||
LinphoneActivity.instance().displayContacts(false);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
cancel.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mDeleteContact.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
mContactPicture = mView.findViewById(R.id.contact_picture);
|
||||
if (mContact != null) {
|
||||
ContactAvatar.displayAvatar(mContact, mView.findViewById(R.id.avatar_layout));
|
||||
} else {
|
||||
ContactAvatar.displayAvatar("", mView.findViewById(R.id.avatar_layout));
|
||||
}
|
||||
|
||||
mContactPicture.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
pickImage();
|
||||
LinphoneActivity.instance().checkAndRequestCameraPermission();
|
||||
}
|
||||
});
|
||||
|
||||
mNumbersAndAddresses = new ArrayList<>();
|
||||
mSipAddresses = initSipAddressFields(mContact);
|
||||
mNumbers = initNumbersFields(mContact);
|
||||
|
||||
mAddSipAddress = mView.findViewById(R.id.add_address_field);
|
||||
if (getResources().getBoolean(R.bool.allow_only_one_sip_address)) {
|
||||
mAddSipAddress.setVisibility(View.GONE);
|
||||
}
|
||||
mAddSipAddress.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
addEmptyRowToAllowNewNumberOrAddress(mSipAddresses, true);
|
||||
}
|
||||
});
|
||||
|
||||
mAddNumber = mView.findViewById(R.id.add_number_field);
|
||||
if (getResources().getBoolean(R.bool.allow_only_one_phone_number)) {
|
||||
mAddNumber.setVisibility(View.GONE);
|
||||
}
|
||||
mAddNumber.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
addEmptyRowToAllowNewNumberOrAddress(mNumbers, false);
|
||||
}
|
||||
});
|
||||
|
||||
mLastName.requestFocus();
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Force hide keyboard
|
||||
getActivity()
|
||||
.getWindow()
|
||||
.setSoftInputMode(
|
||||
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
|
||||
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
// Force hide keyboard
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
View view = getActivity().getCurrentFocus();
|
||||
if (imm != null && view != null) {
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void pickImage() {
|
||||
mPickedPhotoForContactUri = null;
|
||||
final List<Intent> cameraIntents = new ArrayList<>();
|
||||
final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
File file =
|
||||
new File(
|
||||
FileUtils.getStorageDirectory(LinphoneActivity.instance()),
|
||||
getString(R.string.temp_photo_name));
|
||||
mPickedPhotoForContactUri = Uri.fromFile(file);
|
||||
captureIntent.putExtra("outputX", PHOTO_SIZE);
|
||||
captureIntent.putExtra("outputY", PHOTO_SIZE);
|
||||
captureIntent.putExtra("aspectX", 0);
|
||||
captureIntent.putExtra("aspectY", 0);
|
||||
captureIntent.putExtra("scale", true);
|
||||
captureIntent.putExtra("return-data", false);
|
||||
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mPickedPhotoForContactUri);
|
||||
cameraIntents.add(captureIntent);
|
||||
|
||||
final Intent galleryIntent = new Intent();
|
||||
galleryIntent.setType("image/*");
|
||||
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
|
||||
final Intent chooserIntent =
|
||||
Intent.createChooser(galleryIntent, getString(R.string.image_picker_title));
|
||||
chooserIntent.putExtra(
|
||||
Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[] {}));
|
||||
|
||||
startActivityForResult(chooserIntent, ADD_PHOTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == ADD_PHOTO && resultCode == Activity.RESULT_OK) {
|
||||
if (data != null && data.getExtras() != null && data.getExtras().get("data") != null) {
|
||||
Bitmap bm = (Bitmap) data.getExtras().get("data");
|
||||
editContactPicture(null, bm);
|
||||
} else if (data != null && data.getData() != null) {
|
||||
Uri selectedImageUri = data.getData();
|
||||
try {
|
||||
Bitmap selectedImage =
|
||||
MediaStore.Images.Media.getBitmap(
|
||||
LinphoneManager.getInstance().getContext().getContentResolver(),
|
||||
selectedImageUri);
|
||||
selectedImage =
|
||||
Bitmap.createScaledBitmap(selectedImage, PHOTO_SIZE, PHOTO_SIZE, false);
|
||||
editContactPicture(null, selectedImage);
|
||||
} catch (IOException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
} else if (mPickedPhotoForContactUri != null) {
|
||||
String filePath = mPickedPhotoForContactUri.getPath();
|
||||
editContactPicture(filePath, null);
|
||||
} else {
|
||||
File file =
|
||||
new File(
|
||||
FileUtils.getStorageDirectory(LinphoneActivity.instance()),
|
||||
getString(R.string.temp_photo_name));
|
||||
if (file.exists()) {
|
||||
mPickedPhotoForContactUri = Uri.fromFile(file);
|
||||
String filePath = mPickedPhotoForContactUri.getPath();
|
||||
editContactPicture(filePath, null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void editContactPicture(String filePath, Bitmap image) {
|
||||
if (image == null) {
|
||||
image = BitmapFactory.decodeFile(filePath);
|
||||
}
|
||||
|
||||
Bitmap scaledPhoto;
|
||||
int size = getThumbnailSize();
|
||||
if (size > 0) {
|
||||
scaledPhoto = Bitmap.createScaledBitmap(image, size, size, false);
|
||||
} else {
|
||||
scaledPhoto = Bitmap.createBitmap(image);
|
||||
}
|
||||
image.recycle();
|
||||
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
scaledPhoto.compress(Bitmap.CompressFormat.PNG, 0, stream);
|
||||
mContactPicture.setImageBitmap(scaledPhoto);
|
||||
mPhotoToAdd = stream.toByteArray();
|
||||
}
|
||||
|
||||
private int getThumbnailSize() {
|
||||
int value = -1;
|
||||
Cursor c =
|
||||
LinphoneActivity.instance()
|
||||
.getContentResolver()
|
||||
.query(
|
||||
DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
|
||||
new String[] {DisplayPhoto.THUMBNAIL_MAX_DIM},
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
try {
|
||||
c.moveToFirst();
|
||||
value = c.getInt(0);
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
}
|
||||
c.close();
|
||||
return value;
|
||||
}
|
||||
|
||||
private LinearLayout initNumbersFields(final LinphoneContact contact) {
|
||||
LinearLayout controls = mView.findViewById(R.id.controls_numbers);
|
||||
controls.removeAllViews();
|
||||
|
||||
if (contact != null) {
|
||||
for (LinphoneNumberOrAddress numberOrAddress : contact.getNumbersOrAddresses()) {
|
||||
if (!numberOrAddress.isSIPAddress()) {
|
||||
View view = displayNumberOrAddress(controls, numberOrAddress.getValue(), false);
|
||||
if (view != null) controls.addView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mNewSipOrNumberToAdd != null) {
|
||||
boolean isSip =
|
||||
LinphoneUtils.isStrictSipAddress(mNewSipOrNumberToAdd)
|
||||
|| !LinphoneUtils.isNumberAddress(mNewSipOrNumberToAdd);
|
||||
if (!isSip) {
|
||||
View view = displayNumberOrAddress(controls, mNewSipOrNumberToAdd, false);
|
||||
if (view != null) controls.addView(view);
|
||||
}
|
||||
}
|
||||
|
||||
if (mNewDisplayName != null) {
|
||||
EditText lastNameEditText = mView.findViewById(R.id.contactLastName);
|
||||
if (mView != null) lastNameEditText.setText(mNewDisplayName);
|
||||
}
|
||||
|
||||
if (controls.getChildCount() == 0) {
|
||||
addEmptyRowToAllowNewNumberOrAddress(controls, false);
|
||||
}
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
private LinearLayout initSipAddressFields(final LinphoneContact contact) {
|
||||
LinearLayout controls = mView.findViewById(R.id.controls_sip_address);
|
||||
controls.removeAllViews();
|
||||
|
||||
if (contact != null) {
|
||||
for (LinphoneNumberOrAddress numberOrAddress : contact.getNumbersOrAddresses()) {
|
||||
if (numberOrAddress.isSIPAddress()) {
|
||||
View view = displayNumberOrAddress(controls, numberOrAddress.getValue(), true);
|
||||
if (view != null) controls.addView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mNewSipOrNumberToAdd != null) {
|
||||
boolean isSip =
|
||||
LinphoneUtils.isStrictSipAddress(mNewSipOrNumberToAdd)
|
||||
|| !LinphoneUtils.isNumberAddress(mNewSipOrNumberToAdd);
|
||||
if (isSip) {
|
||||
View view = displayNumberOrAddress(controls, mNewSipOrNumberToAdd, true);
|
||||
if (view != null) controls.addView(view);
|
||||
}
|
||||
}
|
||||
|
||||
if (controls.getChildCount() == 0) {
|
||||
addEmptyRowToAllowNewNumberOrAddress(controls, true);
|
||||
}
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
private View displayNumberOrAddress(
|
||||
final LinearLayout controls, String numberOrAddress, boolean isSIP) {
|
||||
String displayNumberOrAddress = numberOrAddress;
|
||||
if (isSIP) {
|
||||
if (mFirstSipAddressIndex == -1) {
|
||||
mFirstSipAddressIndex = controls.getChildCount();
|
||||
}
|
||||
displayNumberOrAddress =
|
||||
LinphoneUtils.getDisplayableUsernameFromAddress(numberOrAddress);
|
||||
}
|
||||
if ((getResources().getBoolean(R.bool.hide_phone_numbers_in_editor) && !isSIP)
|
||||
|| (getResources().getBoolean(R.bool.hide_sip_addresses_in_editor) && isSIP)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LinphoneNumberOrAddress tempNounoa;
|
||||
if (mIsNewContact || mNewSipOrNumberToAdd != null) {
|
||||
tempNounoa = new LinphoneNumberOrAddress(numberOrAddress, isSIP);
|
||||
} else {
|
||||
tempNounoa = new LinphoneNumberOrAddress(numberOrAddress, isSIP, numberOrAddress);
|
||||
}
|
||||
final LinphoneNumberOrAddress nounoa = tempNounoa;
|
||||
mNumbersAndAddresses.add(nounoa);
|
||||
|
||||
final View view = mInflater.inflate(R.layout.contact_edit_row, null);
|
||||
|
||||
final EditText noa = view.findViewById(R.id.numoraddr);
|
||||
if (!isSIP) {
|
||||
noa.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
}
|
||||
noa.setText(displayNumberOrAddress);
|
||||
noa.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
nounoa.setValue(noa.getText().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
ImageView delete = view.findViewById(R.id.delete_field);
|
||||
if ((getResources().getBoolean(R.bool.allow_only_one_phone_number) && !isSIP)
|
||||
|| (getResources().getBoolean(R.bool.allow_only_one_sip_address) && isSIP)) {
|
||||
delete.setVisibility(View.GONE);
|
||||
}
|
||||
delete.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mContact != null) {
|
||||
mContact.removeNumberOrAddress(nounoa);
|
||||
}
|
||||
mNumbersAndAddresses.remove(nounoa);
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
private void addEmptyRowToAllowNewNumberOrAddress(
|
||||
final LinearLayout controls, final boolean isSip) {
|
||||
final View view = mInflater.inflate(R.layout.contact_edit_row, null);
|
||||
final LinphoneNumberOrAddress nounoa = new LinphoneNumberOrAddress(null, isSip);
|
||||
|
||||
final EditText noa = view.findViewById(R.id.numoraddr);
|
||||
mNumbersAndAddresses.add(nounoa);
|
||||
noa.setHint(isSip ? getString(R.string.sip_address) : getString(R.string.phone_number));
|
||||
if (!isSip) {
|
||||
noa.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
noa.setHint(R.string.phone_number);
|
||||
} else {
|
||||
noa.setHint(R.string.sip_address);
|
||||
}
|
||||
noa.requestFocus();
|
||||
noa.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
nounoa.setValue(noa.getText().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
final ImageView delete = view.findViewById(R.id.delete_field);
|
||||
if ((getResources().getBoolean(R.bool.allow_only_one_phone_number) && !isSip)
|
||||
|| (getResources().getBoolean(R.bool.allow_only_one_sip_address) && isSip)) {
|
||||
delete.setVisibility(View.GONE);
|
||||
}
|
||||
delete.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mNumbersAndAddresses.remove(nounoa);
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
controls.addView(view);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
ContactViewHolder.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.linphone.R;
|
||||
|
||||
public class ContactViewHolder extends RecyclerView.ViewHolder
|
||||
implements View.OnClickListener, View.OnLongClickListener {
|
||||
public final CheckBox delete;
|
||||
public final ImageView linphoneFriend;
|
||||
public final TextView name;
|
||||
public final LinearLayout separator;
|
||||
public final TextView separatorText;
|
||||
public final RelativeLayout avatarLayout;
|
||||
public final TextView organization;
|
||||
private final ClickListener mListener;
|
||||
|
||||
public ContactViewHolder(View view, ClickListener listener) {
|
||||
super(view);
|
||||
|
||||
delete = view.findViewById(R.id.delete);
|
||||
linphoneFriend = view.findViewById(R.id.friendLinphone);
|
||||
name = view.findViewById(R.id.name);
|
||||
separator = view.findViewById(R.id.separator);
|
||||
separatorText = view.findViewById(R.id.separator_text);
|
||||
avatarLayout = view.findViewById(R.id.avatar_layout);
|
||||
organization = view.findViewById(R.id.contactOrganization);
|
||||
// friendStatus = view.findViewById(R.id.friendStatus);
|
||||
mListener = listener;
|
||||
view.setOnClickListener(this);
|
||||
view.setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mListener != null) {
|
||||
mListener.onItemClicked(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onLongClick(View v) {
|
||||
if (mListener != null) {
|
||||
return mListener.onItemLongClicked(getAdapterPosition());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public interface ClickListener {
|
||||
void onItemClicked(int position);
|
||||
|
||||
boolean onItemLongClicked(int position);
|
||||
}
|
||||
}
|
||||
173
app/src/main/java/org/linphone/contacts/ContactsAdapter.java
Normal file
173
app/src/main/java/org/linphone/contacts/ContactsAdapter.java
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
ContactsAdapter.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SectionIndexer;
|
||||
import androidx.annotation.NonNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.linphone.R;
|
||||
import org.linphone.utils.SelectableAdapter;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class ContactsAdapter extends SelectableAdapter<ContactViewHolder>
|
||||
implements SectionIndexer {
|
||||
private List<LinphoneContact> mContacts;
|
||||
private String[] mSections;
|
||||
private ArrayList<String> mSectionsList;
|
||||
private Map<String, Integer> mMap = new LinkedHashMap<>();
|
||||
private final ContactViewHolder.ClickListener mClickListener;
|
||||
private final Context mContext;
|
||||
private boolean mIsSearchMode;
|
||||
|
||||
ContactsAdapter(
|
||||
Context context,
|
||||
List<LinphoneContact> contactsList,
|
||||
ContactViewHolder.ClickListener clickListener,
|
||||
SelectableHelper helper) {
|
||||
super(helper);
|
||||
mContext = context;
|
||||
updateDataSet(contactsList);
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ContactViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.contact_cell, parent, false);
|
||||
return new ContactViewHolder(v, mClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ContactViewHolder holder, final int position) {
|
||||
LinphoneContact contact = (LinphoneContact) getItem(position);
|
||||
|
||||
holder.name.setText(contact.getFullName());
|
||||
|
||||
if (!mIsSearchMode) {
|
||||
String fullName = contact.getFullName();
|
||||
if (fullName != null && !fullName.isEmpty()) {
|
||||
holder.separatorText.setText(String.valueOf(fullName.charAt(0)));
|
||||
}
|
||||
}
|
||||
holder.separator.setVisibility(
|
||||
mIsSearchMode
|
||||
|| (!mIsSearchMode
|
||||
&& getPositionForSection(getSectionForPosition(position))
|
||||
!= position)
|
||||
? View.GONE
|
||||
: View.VISIBLE);
|
||||
holder.linphoneFriend.setVisibility(contact.isInFriendList() ? View.VISIBLE : View.GONE);
|
||||
|
||||
ContactAvatar.displayAvatar(contact, holder.avatarLayout);
|
||||
|
||||
boolean isOrgVisible =
|
||||
mContext.getResources().getBoolean(R.bool.display_contact_organization);
|
||||
String org = contact.getOrganization();
|
||||
if (org != null && !org.isEmpty() && isOrgVisible) {
|
||||
holder.organization.setText(org);
|
||||
holder.organization.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.organization.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
holder.delete.setVisibility(isEditionEnabled() ? View.VISIBLE : View.INVISIBLE);
|
||||
holder.delete.setChecked(isSelected(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mContacts.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
if (position >= getItemCount()) return null;
|
||||
return mContacts.get(position);
|
||||
}
|
||||
|
||||
public void setIsSearchMode(boolean set) {
|
||||
mIsSearchMode = set;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void updateDataSet(List<LinphoneContact> contactsList) {
|
||||
mContacts = contactsList;
|
||||
|
||||
mMap = new LinkedHashMap<>();
|
||||
String prevLetter = null;
|
||||
for (int i = 0; i < mContacts.size(); i++) {
|
||||
LinphoneContact contact = mContacts.get(i);
|
||||
String fullName = contact.getFullName();
|
||||
if (fullName == null || fullName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String firstLetter = fullName.substring(0, 1).toUpperCase(Locale.getDefault());
|
||||
if (!firstLetter.equals(prevLetter)) {
|
||||
prevLetter = firstLetter;
|
||||
mMap.put(firstLetter, i);
|
||||
}
|
||||
}
|
||||
mSectionsList = new ArrayList<>(mMap.keySet());
|
||||
mSections = new String[mSectionsList.size()];
|
||||
mSectionsList.toArray(mSections);
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getSections() {
|
||||
return mSections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionForSection(int sectionIndex) {
|
||||
if (sectionIndex >= mSections.length || sectionIndex < 0) {
|
||||
return 0;
|
||||
}
|
||||
return mMap.get(mSections[sectionIndex]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionForPosition(int position) {
|
||||
if (position >= mContacts.size() || position < 0) {
|
||||
return 0;
|
||||
}
|
||||
LinphoneContact contact = mContacts.get(position);
|
||||
String fullName = contact.getFullName();
|
||||
if (fullName == null || fullName.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
String letter = fullName.substring(0, 1).toUpperCase(Locale.getDefault());
|
||||
return mSectionsList.indexOf(letter);
|
||||
}
|
||||
}
|
||||
418
app/src/main/java/org/linphone/contacts/ContactsFragment.java
Normal file
418
app/src/main/java/org/linphone/contacts/ContactsFragment.java
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
ContactsFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
|
||||
public class ContactsFragment extends Fragment
|
||||
implements OnItemClickListener,
|
||||
ContactsUpdatedListener,
|
||||
ContactViewHolder.ClickListener,
|
||||
SelectableHelper.DeleteListener {
|
||||
private RecyclerView mContactsList;
|
||||
private TextView mNoSipContact, mNoContact;
|
||||
private ImageView mAllContacts, mLinphoneContacts, mNewContact;
|
||||
private boolean mOnlyDisplayLinphoneContacts;
|
||||
private View mAllContactsSelected, mLinphoneContactsSelected;
|
||||
private int mLastKnownPosition;
|
||||
private boolean mEditOnClick = false, mEditConsumed = false, mOnlyDisplayChatAddress = false;
|
||||
private String mSipAddressToAdd, mDisplayName = null;
|
||||
private SearchView mSearchView;
|
||||
private ProgressBar mContactsFetchInProgress;
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
private Context mContext;
|
||||
private SelectableHelper mSelectionHelper;
|
||||
private ContactsAdapter mContactAdapter;
|
||||
private SwipeRefreshLayout mContactsRefresher;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.contacts_list, container, false);
|
||||
mContext = getActivity().getApplicationContext();
|
||||
mSelectionHelper = new SelectableHelper(view, this);
|
||||
mSelectionHelper.setDialogMessage(R.string.delete_contacts_text);
|
||||
|
||||
if (getArguments() != null) {
|
||||
mEditOnClick = getArguments().getBoolean("EditOnClick");
|
||||
mSipAddressToAdd = getArguments().getString("SipAddress");
|
||||
if (getArguments().getString("DisplayName") != null) {
|
||||
mDisplayName = getArguments().getString("DisplayName");
|
||||
}
|
||||
mOnlyDisplayChatAddress = getArguments().getBoolean("ChatAddressOnly");
|
||||
|
||||
if (getArguments().getBoolean("EditOnClick")) {
|
||||
Toast.makeText(
|
||||
LinphoneActivity.instance(),
|
||||
R.string.toast_choose_contact_for_edition,
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
getArguments().clear();
|
||||
}
|
||||
|
||||
mNoSipContact = view.findViewById(R.id.noSipContact);
|
||||
mNoContact = view.findViewById(R.id.noContact);
|
||||
mContactsList = view.findViewById(R.id.contactsList);
|
||||
|
||||
mAllContacts = view.findViewById(R.id.all_contacts);
|
||||
mLinphoneContacts = view.findViewById(R.id.linphone_contacts);
|
||||
mAllContactsSelected = view.findViewById(R.id.all_contacts_select);
|
||||
mLinphoneContactsSelected = view.findViewById(R.id.linphone_contacts_select);
|
||||
mContactsFetchInProgress = view.findViewById(R.id.contactsFetchInProgress);
|
||||
mNewContact = view.findViewById(R.id.newContact);
|
||||
mContactsRefresher = view.findViewById(R.id.contactsListRefresher);
|
||||
|
||||
mContactsRefresher.setOnRefreshListener(
|
||||
new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
ContactsManager.getInstance().fetchContactsAsync();
|
||||
}
|
||||
});
|
||||
|
||||
mAllContacts.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mOnlyDisplayLinphoneContacts = false;
|
||||
mAllContactsSelected.setVisibility(View.VISIBLE);
|
||||
mAllContacts.setEnabled(false);
|
||||
mLinphoneContacts.setEnabled(true);
|
||||
mLinphoneContactsSelected.setVisibility(View.INVISIBLE);
|
||||
changeContactsAdapter();
|
||||
}
|
||||
});
|
||||
|
||||
mLinphoneContacts.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mAllContactsSelected.setVisibility(View.INVISIBLE);
|
||||
mLinphoneContactsSelected.setVisibility(View.VISIBLE);
|
||||
mLinphoneContacts.setEnabled(false);
|
||||
mAllContacts.setEnabled(true);
|
||||
mOnlyDisplayLinphoneContacts = true;
|
||||
changeContactsAdapter();
|
||||
}
|
||||
});
|
||||
|
||||
mNewContact.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mEditConsumed = true;
|
||||
ContactsManager.getInstance()
|
||||
.createContact(getActivity(), mDisplayName, mSipAddressToAdd);
|
||||
}
|
||||
});
|
||||
|
||||
if (getResources().getBoolean(R.bool.hide_non_linphone_contacts)) {
|
||||
mAllContacts.setEnabled(false);
|
||||
mLinphoneContacts.setEnabled(false);
|
||||
mOnlyDisplayLinphoneContacts = true;
|
||||
mAllContacts.setOnClickListener(null);
|
||||
mLinphoneContacts.setOnClickListener(null);
|
||||
mLinphoneContacts.setVisibility(View.INVISIBLE);
|
||||
mLinphoneContactsSelected.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mAllContacts.setEnabled(mOnlyDisplayLinphoneContacts);
|
||||
mLinphoneContacts.setEnabled(!mAllContacts.isEnabled());
|
||||
}
|
||||
mNewContact.setEnabled(LinphoneManager.getLc().getCallsNb() == 0);
|
||||
|
||||
if (!ContactsManager.getInstance().contactsFetchedOnce()) {
|
||||
if (ContactsManager.getInstance().hasReadContactsAccess()) {
|
||||
mContactsFetchInProgress.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
LinphoneActivity.instance().checkAndRequestReadContactsPermission();
|
||||
}
|
||||
} else {
|
||||
if (!mOnlyDisplayLinphoneContacts
|
||||
&& ContactsManager.getInstance().getContacts().size() == 0) {
|
||||
mNoContact.setVisibility(View.VISIBLE);
|
||||
} else if (mOnlyDisplayLinphoneContacts
|
||||
&& ContactsManager.getInstance().getSIPContacts().size() == 0) {
|
||||
mNoSipContact.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
mSearchView = view.findViewById(R.id.searchField);
|
||||
mSearchView.setOnQueryTextListener(
|
||||
new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
searchContacts(newText);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mLayoutManager = new LinearLayoutManager(mContext);
|
||||
mContactsList.setLayoutManager(mLayoutManager);
|
||||
|
||||
DividerItemDecoration dividerItemDecoration =
|
||||
new DividerItemDecoration(
|
||||
mContactsList.getContext(), mLayoutManager.getOrientation());
|
||||
dividerItemDecoration.setDrawable(
|
||||
getActivity().getResources().getDrawable(R.drawable.divider));
|
||||
mContactsList.addItemDecoration(dividerItemDecoration);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void displayFirstContact() {
|
||||
if (mContactsList != null
|
||||
&& mContactsList.getAdapter() != null
|
||||
&& mContactsList.getAdapter().getItemCount() > 0) {
|
||||
ContactsAdapter mAdapt = (ContactsAdapter) mContactsList.getAdapter();
|
||||
LinphoneActivity.instance().displayContact((LinphoneContact) mAdapt.getItem(0), false);
|
||||
} else {
|
||||
LinphoneActivity.instance().displayEmptyFragment();
|
||||
}
|
||||
}
|
||||
|
||||
private void searchContacts(String search) {
|
||||
boolean isEditionEnabled = false;
|
||||
if (search == null || search.length() == 0) {
|
||||
changeContactsAdapter();
|
||||
return;
|
||||
}
|
||||
changeContactsToggle();
|
||||
|
||||
List<LinphoneContact> listContact;
|
||||
|
||||
if (mOnlyDisplayLinphoneContacts) {
|
||||
listContact = ContactsManager.getInstance().getSIPContacts(search);
|
||||
} else {
|
||||
listContact = ContactsManager.getInstance().getContacts(search);
|
||||
}
|
||||
if (mContactAdapter != null && mContactAdapter.isEditionEnabled()) {
|
||||
isEditionEnabled = true;
|
||||
}
|
||||
|
||||
mContactAdapter = new ContactsAdapter(mContext, listContact, this, mSelectionHelper);
|
||||
mContactAdapter.setIsSearchMode(true);
|
||||
|
||||
// mContactsList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
|
||||
mSelectionHelper.setAdapter(mContactAdapter);
|
||||
if (isEditionEnabled) {
|
||||
mSelectionHelper.enterEditionMode();
|
||||
}
|
||||
mContactsList.setAdapter(mContactAdapter);
|
||||
}
|
||||
|
||||
private void changeContactsAdapter() {
|
||||
changeContactsToggle();
|
||||
List<LinphoneContact> listContact;
|
||||
|
||||
mNoSipContact.setVisibility(View.GONE);
|
||||
mNoContact.setVisibility(View.GONE);
|
||||
mContactsList.setVisibility(View.VISIBLE);
|
||||
boolean isEditionEnabled = false;
|
||||
String query = mSearchView.getQuery().toString();
|
||||
if (query.equals("")) {
|
||||
if (mOnlyDisplayLinphoneContacts) {
|
||||
listContact = ContactsManager.getInstance().getSIPContacts();
|
||||
} else {
|
||||
listContact = ContactsManager.getInstance().getContacts();
|
||||
}
|
||||
} else {
|
||||
if (mOnlyDisplayLinphoneContacts) {
|
||||
listContact = ContactsManager.getInstance().getSIPContacts(query);
|
||||
} else {
|
||||
listContact = ContactsManager.getInstance().getContacts(query);
|
||||
}
|
||||
}
|
||||
|
||||
if (mContactAdapter != null && mContactAdapter.isEditionEnabled()) {
|
||||
isEditionEnabled = true;
|
||||
}
|
||||
|
||||
mContactAdapter = new ContactsAdapter(mContext, listContact, this, mSelectionHelper);
|
||||
|
||||
mSelectionHelper.setAdapter(mContactAdapter);
|
||||
|
||||
if (isEditionEnabled) {
|
||||
mSelectionHelper.enterEditionMode();
|
||||
}
|
||||
mContactsList.setAdapter(mContactAdapter);
|
||||
|
||||
mContactAdapter.notifyDataSetChanged();
|
||||
|
||||
if (!mOnlyDisplayLinphoneContacts && mContactAdapter.getItemCount() == 0) {
|
||||
mNoContact.setVisibility(View.VISIBLE);
|
||||
} else if (mOnlyDisplayLinphoneContacts && mContactAdapter.getItemCount() == 0) {
|
||||
mNoSipContact.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void changeContactsToggle() {
|
||||
if (mOnlyDisplayLinphoneContacts
|
||||
&& !getResources().getBoolean(R.bool.hide_non_linphone_contacts)) {
|
||||
mAllContacts.setEnabled(true);
|
||||
mAllContactsSelected.setVisibility(View.INVISIBLE);
|
||||
mLinphoneContacts.setEnabled(false);
|
||||
mLinphoneContactsSelected.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mAllContacts.setEnabled(false);
|
||||
mAllContactsSelected.setVisibility(View.VISIBLE);
|
||||
mLinphoneContacts.setEnabled(true);
|
||||
mLinphoneContactsSelected.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
|
||||
LinphoneContact contact = (LinphoneContact) adapter.getItemAtPosition(position);
|
||||
if (mEditOnClick) {
|
||||
mEditConsumed = true;
|
||||
ContactsManager.getInstance().editContact(getActivity(), contact, mSipAddressToAdd);
|
||||
} else {
|
||||
mLastKnownPosition = mLayoutManager.findFirstVisibleItemPosition();
|
||||
LinphoneActivity.instance().displayContact(contact, mOnlyDisplayChatAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClicked(int position) {
|
||||
LinphoneContact contact = (LinphoneContact) mContactAdapter.getItem(position);
|
||||
|
||||
if (mContactAdapter.isEditionEnabled()) {
|
||||
mContactAdapter.toggleSelection(position);
|
||||
|
||||
} else if (mEditOnClick) {
|
||||
mEditConsumed = true;
|
||||
ContactsManager.getInstance().editContact(getActivity(), contact, mSipAddressToAdd);
|
||||
} else {
|
||||
mLastKnownPosition = mLayoutManager.findFirstVisibleItemPosition();
|
||||
LinphoneActivity.instance().displayContact(contact, mOnlyDisplayChatAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClicked(int position) {
|
||||
if (!mContactAdapter.isEditionEnabled()) {
|
||||
mSelectionHelper.enterEditionMode();
|
||||
}
|
||||
mContactAdapter.toggleSelection(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
ContactsManager.getInstance().addContactsListener(this);
|
||||
|
||||
if (mEditConsumed) {
|
||||
mEditOnClick = false;
|
||||
mSipAddressToAdd = null;
|
||||
}
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CONTACTS_LIST);
|
||||
mOnlyDisplayLinphoneContacts =
|
||||
ContactsManager.getInstance().isLinphoneContactsPrefered()
|
||||
|| getResources().getBoolean(R.bool.hide_non_linphone_contacts);
|
||||
}
|
||||
changeContactsToggle();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
ContactsManager.getInstance().removeContactsListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactsUpdated() {
|
||||
if (!LinphoneActivity.isInstanciated()
|
||||
|| (LinphoneActivity.instance().getCurrentFragment()
|
||||
!= FragmentsAvailable.CONTACTS_LIST
|
||||
&& !LinphoneActivity.instance().isTablet())) return;
|
||||
if (mContactAdapter != null) {
|
||||
mContactAdapter.updateDataSet(
|
||||
mOnlyDisplayLinphoneContacts
|
||||
? ContactsManager.getInstance().getSIPContacts()
|
||||
: ContactsManager.getInstance().getContacts());
|
||||
mContactAdapter.notifyDataSetChanged();
|
||||
|
||||
if (mContactAdapter.getItemCount() > 0) {
|
||||
mNoContact.setVisibility(View.GONE);
|
||||
mNoSipContact.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
mContactsFetchInProgress.setVisibility(View.GONE);
|
||||
mContactsRefresher.setRefreshing(false);
|
||||
}
|
||||
|
||||
private void invalidate() {
|
||||
if (mSearchView != null && mSearchView.getQuery().toString().length() > 0) {
|
||||
searchContacts(mSearchView.getQuery().toString());
|
||||
} else {
|
||||
changeContactsAdapter();
|
||||
}
|
||||
mContactsList.scrollToPosition(mLastKnownPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteSelection(Object[] objectsToDelete) {
|
||||
ArrayList<String> ids = new ArrayList<>();
|
||||
int size = mContactAdapter.getSelectedItemCount();
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
LinphoneContact contact = (LinphoneContact) objectsToDelete[i];
|
||||
if (contact.isAndroidContact()) {
|
||||
contact.deleteFriend();
|
||||
ids.add(contact.getAndroidId());
|
||||
} else {
|
||||
contact.delete();
|
||||
}
|
||||
}
|
||||
ContactsManager.getInstance().deleteMultipleContactsAtOnce(ids);
|
||||
}
|
||||
}
|
||||
517
app/src/main/java/org/linphone/contacts/ContactsManager.java
Normal file
517
app/src/main/java/org/linphone/contacts/ContactsManager.java
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
ContactsManager.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import static android.os.AsyncTask.THREAD_POOL_EXECUTOR;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.compatibility.Compatibility;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.Friend;
|
||||
import org.linphone.core.FriendList;
|
||||
import org.linphone.core.FriendListListener;
|
||||
import org.linphone.core.MagicSearch;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
public class ContactsManager extends ContentObserver implements FriendListListener {
|
||||
private static ContactsManager sInstance;
|
||||
|
||||
private List<LinphoneContact> mContacts, mSipContacts;
|
||||
private ArrayList<ContactsUpdatedListener> mContactsUpdatedListeners;
|
||||
private MagicSearch mMagicSearch;
|
||||
private boolean mContactsFetchedOnce = false;
|
||||
private Context mContext;
|
||||
private AsyncContactsLoader mLoadContactTask;
|
||||
private boolean mInitialized = false;
|
||||
|
||||
public static ContactsManager getInstance() {
|
||||
if (sInstance == null) sInstance = new ContactsManager();
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private ContactsManager() {
|
||||
super(LinphoneService.instance().handler);
|
||||
mContactsUpdatedListeners = new ArrayList<>();
|
||||
mContacts = new ArrayList<>();
|
||||
mSipContacts = new ArrayList<>();
|
||||
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) {
|
||||
mMagicSearch = LinphoneManager.getLcIfManagerNotDestroyedOrNull().createMagicSearch();
|
||||
mMagicSearch.setLimitedSearch(false); // Do not limit the number of results
|
||||
}
|
||||
}
|
||||
|
||||
public void addContactsListener(ContactsUpdatedListener listener) {
|
||||
mContactsUpdatedListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeContactsListener(ContactsUpdatedListener listener) {
|
||||
mContactsUpdatedListeners.remove(listener);
|
||||
}
|
||||
|
||||
public ArrayList<ContactsUpdatedListener> getContactsListeners() {
|
||||
return mContactsUpdatedListeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
onChange(selfChange, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
fetchContactsAsync();
|
||||
}
|
||||
|
||||
public synchronized List<LinphoneContact> getContacts() {
|
||||
return mContacts;
|
||||
}
|
||||
|
||||
synchronized void setContacts(List<LinphoneContact> c) {
|
||||
mContacts = c;
|
||||
}
|
||||
|
||||
public synchronized List<LinphoneContact> getSIPContacts() {
|
||||
return mSipContacts;
|
||||
}
|
||||
|
||||
synchronized void setSipContacts(List<LinphoneContact> c) {
|
||||
mSipContacts = c;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (mLoadContactTask != null) {
|
||||
mLoadContactTask.cancel(true);
|
||||
}
|
||||
// LinphoneContact has a Friend field and Friend can have a LinphoneContact has userData
|
||||
// Friend also keeps a ref on the Core, so we have to clean them
|
||||
for (LinphoneContact c : mContacts) {
|
||||
c.setFriend(null);
|
||||
}
|
||||
mContacts.clear();
|
||||
for (LinphoneContact c : mSipContacts) {
|
||||
c.setFriend(null);
|
||||
}
|
||||
mSipContacts.clear();
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
for (FriendList list : lc.getFriendsLists()) {
|
||||
list.removeListener(this);
|
||||
}
|
||||
}
|
||||
sInstance = null;
|
||||
}
|
||||
|
||||
public void fetchContactsAsync() {
|
||||
if (mLoadContactTask != null) {
|
||||
mLoadContactTask.cancel(true);
|
||||
}
|
||||
if (!hasReadContactsAccess()) {
|
||||
Log.w("[Contacts Manager] Can't fetch contact without READ permission");
|
||||
return;
|
||||
}
|
||||
mLoadContactTask = new AsyncContactsLoader(mContext);
|
||||
mContactsFetchedOnce = true;
|
||||
mLoadContactTask.executeOnExecutor(THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
public void editContact(Context context, LinphoneContact contact, String valueToAdd) {
|
||||
if (context.getResources().getBoolean(R.bool.use_native_contact_editor)) {
|
||||
Intent intent = new Intent(Intent.ACTION_EDIT);
|
||||
Uri contactUri = contact.getAndroidLookupUri();
|
||||
intent.setDataAndType(contactUri, ContactsContract.Contacts.CONTENT_ITEM_TYPE);
|
||||
intent.putExtra(
|
||||
"finishActivityOnSaveCompleted", true); // So after save will go back here
|
||||
if (valueToAdd != null) {
|
||||
intent.putExtra(ContactsContract.Intents.Insert.IM_HANDLE, valueToAdd);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
LinphoneActivity.instance().editContact(contact, valueToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
public void createContact(Context context, String name, String valueToAdd) {
|
||||
if (context.getResources().getBoolean(R.bool.use_native_contact_editor)) {
|
||||
Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);
|
||||
intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
|
||||
intent.putExtra(
|
||||
"finishActivityOnSaveCompleted", true); // So after save will go back here
|
||||
if (name != null) {
|
||||
intent.putExtra(ContactsContract.Intents.Insert.NAME, name);
|
||||
}
|
||||
if (valueToAdd != null) {
|
||||
intent.putExtra(ContactsContract.Intents.Insert.IM_HANDLE, valueToAdd);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
LinphoneActivity.instance().addContact(name, valueToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
public MagicSearch getMagicSearch() {
|
||||
return mMagicSearch;
|
||||
}
|
||||
|
||||
public boolean contactsFetchedOnce() {
|
||||
return mContactsFetchedOnce;
|
||||
}
|
||||
|
||||
public List<LinphoneContact> getContacts(String search) {
|
||||
search = search.toLowerCase(Locale.getDefault());
|
||||
List<LinphoneContact> searchContactsBegin = new ArrayList<>();
|
||||
List<LinphoneContact> searchContactsContain = new ArrayList<>();
|
||||
for (LinphoneContact contact : getContacts()) {
|
||||
if (contact.getFullName() != null) {
|
||||
if (contact.getFullName().toLowerCase(Locale.getDefault()).startsWith(search)) {
|
||||
searchContactsBegin.add(contact);
|
||||
} else if (contact.getFullName()
|
||||
.toLowerCase(Locale.getDefault())
|
||||
.contains(search)) {
|
||||
searchContactsContain.add(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
searchContactsBegin.addAll(searchContactsContain);
|
||||
return searchContactsBegin;
|
||||
}
|
||||
|
||||
public List<LinphoneContact> getSIPContacts(String search) {
|
||||
search = search.toLowerCase(Locale.getDefault());
|
||||
List<LinphoneContact> searchContactsBegin = new ArrayList<>();
|
||||
List<LinphoneContact> searchContactsContain = new ArrayList<>();
|
||||
for (LinphoneContact contact : getSIPContacts()) {
|
||||
if (contact.getFullName() != null) {
|
||||
if (contact.getFullName().toLowerCase(Locale.getDefault()).startsWith(search)) {
|
||||
searchContactsBegin.add(contact);
|
||||
} else if (contact.getFullName()
|
||||
.toLowerCase(Locale.getDefault())
|
||||
.contains(search)) {
|
||||
searchContactsContain.add(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
searchContactsBegin.addAll(searchContactsContain);
|
||||
return searchContactsBegin;
|
||||
}
|
||||
|
||||
public void enableContactsAccess() {
|
||||
LinphonePreferences.instance().disableFriendsStorage();
|
||||
}
|
||||
|
||||
public boolean hasReadContactsAccess() {
|
||||
if (mContext == null) {
|
||||
return false;
|
||||
}
|
||||
boolean contactsR =
|
||||
(PackageManager.PERMISSION_GRANTED
|
||||
== mContext.getPackageManager()
|
||||
.checkPermission(
|
||||
android.Manifest.permission.READ_CONTACTS,
|
||||
mContext.getPackageName()));
|
||||
return contactsR
|
||||
&& !mContext.getResources().getBoolean(R.bool.force_use_of_linphone_friends);
|
||||
}
|
||||
|
||||
public boolean hasWriteContactsAccess() {
|
||||
if (mContext == null) {
|
||||
return false;
|
||||
}
|
||||
return (PackageManager.PERMISSION_GRANTED
|
||||
== mContext.getPackageManager()
|
||||
.checkPermission(
|
||||
Manifest.permission.WRITE_CONTACTS, mContext.getPackageName()));
|
||||
}
|
||||
|
||||
public boolean hasWriteSyncPermission() {
|
||||
if (mContext == null) {
|
||||
return false;
|
||||
}
|
||||
return (PackageManager.PERMISSION_GRANTED
|
||||
== mContext.getPackageManager()
|
||||
.checkPermission(
|
||||
Manifest.permission.WRITE_SYNC_SETTINGS,
|
||||
mContext.getPackageName()));
|
||||
}
|
||||
|
||||
public boolean isLinphoneContactsPrefered() {
|
||||
ProxyConfig lpc = LinphoneManager.getLc().getDefaultProxyConfig();
|
||||
return lpc != null
|
||||
&& lpc.getIdentityAddress()
|
||||
.getDomain()
|
||||
.equals(mContext.getString(R.string.default_domain));
|
||||
}
|
||||
|
||||
public void initializeContactManager(Context context) {
|
||||
mContext = context;
|
||||
|
||||
if (!mInitialized) {
|
||||
if (mContext.getResources().getBoolean(R.bool.use_linphone_tag)) {
|
||||
if (hasReadContactsAccess()
|
||||
&& hasWriteContactsAccess()
|
||||
&& hasWriteSyncPermission()) {
|
||||
if (LinphoneService.isReady()) {
|
||||
ContactsManager.getInstance().initializeSyncAccount();
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mContext != null && getContacts().size() == 0 && hasReadContactsAccess()) {
|
||||
fetchContactsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void makeContactAccountVisible() {
|
||||
ContentProviderClient client =
|
||||
mContext.getContentResolver()
|
||||
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(
|
||||
ContactsContract.Settings.ACCOUNT_NAME,
|
||||
mContext.getString(R.string.sync_account_name));
|
||||
values.put(
|
||||
ContactsContract.Settings.ACCOUNT_TYPE,
|
||||
mContext.getString(R.string.sync_account_type));
|
||||
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, true);
|
||||
try {
|
||||
client.insert(
|
||||
ContactsContract.Settings.CONTENT_URI
|
||||
.buildUpon()
|
||||
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
|
||||
.build(),
|
||||
values);
|
||||
Log.i("[Contacts Manager] Contacts account made visible");
|
||||
} catch (RemoteException e) {
|
||||
Log.e("[Contacts Manager] Couldn't make contacts account visible: " + e);
|
||||
}
|
||||
Compatibility.closeContentProviderClient(client);
|
||||
}
|
||||
|
||||
private void initializeSyncAccount() {
|
||||
AccountManager accountManager =
|
||||
(AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
|
||||
|
||||
Account[] accounts =
|
||||
accountManager.getAccountsByType(mContext.getString(R.string.sync_account_type));
|
||||
|
||||
if (accounts != null && accounts.length == 0) {
|
||||
Account newAccount =
|
||||
new Account(
|
||||
mContext.getString(R.string.sync_account_name),
|
||||
mContext.getString(R.string.sync_account_type));
|
||||
try {
|
||||
accountManager.addAccountExplicitly(newAccount, null, null);
|
||||
Log.i("[Contacts Manager] Contact account added");
|
||||
makeContactAccountVisible();
|
||||
} catch (Exception e) {
|
||||
Log.e("[Contacts Manager] Couldn't initialize sync account: " + e);
|
||||
}
|
||||
} else if (accounts != null) {
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
Log.i(
|
||||
"[Contacts Manager] Found account with name \""
|
||||
+ accounts[i].name
|
||||
+ "\" and type \""
|
||||
+ accounts[i].type
|
||||
+ "\"");
|
||||
makeContactAccountVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized LinphoneContact findContactFromAndroidId(String androidId) {
|
||||
if (androidId == null) return null;
|
||||
|
||||
for (LinphoneContact c : getContacts()) {
|
||||
if (c.getAndroidId() != null && c.getAndroidId().equals(androidId)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized LinphoneContact findContactFromAddress(Address address) {
|
||||
if (address == null) return null;
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
Friend lf = lc.findFriend(address);
|
||||
if (lf != null) {
|
||||
return (LinphoneContact) lf.getUserData();
|
||||
}
|
||||
return findContactFromPhoneNumber(address.getUsername());
|
||||
}
|
||||
|
||||
public synchronized LinphoneContact findContactFromPhoneNumber(String phoneNumber) {
|
||||
if (phoneNumber == null) return null;
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
ProxyConfig lpc = null;
|
||||
if (lc != null) {
|
||||
lpc = lc.getDefaultProxyConfig();
|
||||
}
|
||||
if (lpc == null) return null;
|
||||
String normalized = lpc.normalizePhoneNumber(phoneNumber);
|
||||
if (normalized == null) normalized = phoneNumber;
|
||||
|
||||
Address addr = lpc.normalizeSipUri(normalized);
|
||||
if (addr == null) {
|
||||
return null;
|
||||
}
|
||||
addr.setUriParam("user", "phone");
|
||||
Friend lf =
|
||||
lc.findFriend(
|
||||
addr); // Without this, the hashmap inside liblinphone won't find it...
|
||||
if (lf != null) {
|
||||
return (LinphoneContact) lf.getUserData();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAddressOrNumberForAndroidContact(ContentResolver resolver, Uri contactUri) {
|
||||
// Phone Numbers
|
||||
String[] projection = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER};
|
||||
Cursor c = resolver.query(contactUri, projection, null, null, null);
|
||||
if (c != null) {
|
||||
if (c.moveToNext()) {
|
||||
int numberIndex = c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
String number = c.getString(numberIndex);
|
||||
c.close();
|
||||
return number;
|
||||
}
|
||||
}
|
||||
c.close();
|
||||
|
||||
// SIP addresses
|
||||
projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
|
||||
c = resolver.query(contactUri, projection, null, null, null);
|
||||
if (c != null) {
|
||||
if (c.moveToNext()) {
|
||||
int numberIndex =
|
||||
c.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
|
||||
String address = c.getString(numberIndex);
|
||||
c.close();
|
||||
return address;
|
||||
}
|
||||
}
|
||||
c.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized boolean refreshSipContact(Friend lf) {
|
||||
LinphoneContact contact = (LinphoneContact) lf.getUserData();
|
||||
if (contact != null) {
|
||||
if (!mSipContacts.contains(contact)) {
|
||||
mSipContacts.add(contact);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void delete(String id) {
|
||||
ArrayList<String> ids = new ArrayList<>();
|
||||
ids.add(id);
|
||||
deleteMultipleContactsAtOnce(ids);
|
||||
}
|
||||
|
||||
public void deleteMultipleContactsAtOnce(List<String> ids) {
|
||||
String select = ContactsContract.Data.CONTACT_ID + " = ?";
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
|
||||
|
||||
for (String id : ids) {
|
||||
String[] args = new String[] {id};
|
||||
ops.add(
|
||||
ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI)
|
||||
.withSelection(select, args)
|
||||
.build());
|
||||
}
|
||||
|
||||
try {
|
||||
mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
} catch (Exception e) {
|
||||
Log.e("[Contacts Manager] " + e);
|
||||
}
|
||||
|
||||
// To ensure removed contacts won't appear in the contacts list anymore
|
||||
fetchContactsAsync();
|
||||
}
|
||||
|
||||
public String getString(int resourceID) {
|
||||
if (mContext == null) return null;
|
||||
return mContext.getString(resourceID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactCreated(FriendList list, Friend lf) {}
|
||||
|
||||
@Override
|
||||
public void onContactDeleted(FriendList list, Friend lf) {}
|
||||
|
||||
@Override
|
||||
public void onContactUpdated(FriendList list, Friend newFriend, Friend oldFriend) {}
|
||||
|
||||
@Override
|
||||
public void onSyncStatusChanged(FriendList list, FriendList.SyncStatus status, String msg) {}
|
||||
|
||||
@Override
|
||||
public void onPresenceReceived(FriendList list, Friend[] friends) {
|
||||
boolean updated = false;
|
||||
for (Friend lf : friends) {
|
||||
boolean newContact = ContactsManager.getInstance().refreshSipContact(lf);
|
||||
if (newContact) {
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
Collections.sort(mSipContacts);
|
||||
}
|
||||
|
||||
for (ContactsUpdatedListener listener : mContactsUpdatedListeners) {
|
||||
listener.onContactsUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,4 +21,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
public interface ContactsUpdatedListener {
|
||||
void onContactsUpdated();
|
||||
}
|
||||
}
|
||||
557
app/src/main/java/org/linphone/contacts/LinphoneContact.java
Normal file
557
app/src/main/java/org/linphone/contacts/LinphoneContact.java
Normal file
|
|
@ -0,0 +1,557 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
LinphoneContact.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.Friend;
|
||||
import org.linphone.core.FriendCapability;
|
||||
import org.linphone.core.FriendList;
|
||||
import org.linphone.core.PresenceBasicStatus;
|
||||
import org.linphone.core.PresenceModel;
|
||||
import org.linphone.core.SubscribePolicy;
|
||||
|
||||
public class LinphoneContact extends AndroidContact
|
||||
implements Serializable, Comparable<LinphoneContact> {
|
||||
private static final long serialVersionUID = 9015568163905205244L;
|
||||
|
||||
private transient Friend mFriend;
|
||||
private String mFullName, mFirstName, mLastName, mOrganization;
|
||||
private transient Uri mPhotoUri, mThumbnailUri;
|
||||
private List<LinphoneNumberOrAddress> mAddresses;
|
||||
private boolean mHasSipAddress;
|
||||
|
||||
public LinphoneContact() {
|
||||
super();
|
||||
mAddresses = new ArrayList<>();
|
||||
mAndroidId = null;
|
||||
mAndroidLookupKey = null;
|
||||
mThumbnailUri = null;
|
||||
mPhotoUri = null;
|
||||
mHasSipAddress = false;
|
||||
}
|
||||
|
||||
public static LinphoneContact createContact() {
|
||||
LinphoneContact contact = new LinphoneContact();
|
||||
if (ContactsManager.getInstance().hasReadContactsAccess()) {
|
||||
contact.createAndroidContact();
|
||||
} else {
|
||||
contact.createFriend();
|
||||
}
|
||||
return contact;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LinphoneContact contact) {
|
||||
String fullName =
|
||||
getFullName() != null ? getFullName().toUpperCase(Locale.getDefault()) : "";
|
||||
String contactFullName =
|
||||
contact.getFullName() != null
|
||||
? contact.getFullName().toUpperCase(Locale.getDefault())
|
||||
: "";
|
||||
|
||||
if (fullName.equals(contactFullName)) {
|
||||
if (getAndroidId() != null) {
|
||||
if (contact.getAndroidId() != null) {
|
||||
int idComp = getAndroidId().compareTo(contact.getAndroidId());
|
||||
if (idComp == 0) return 0;
|
||||
List<LinphoneNumberOrAddress> noas1 = getNumbersOrAddresses();
|
||||
List<LinphoneNumberOrAddress> noas2 = contact.getNumbersOrAddresses();
|
||||
if (noas1.size() == noas2.size()) {
|
||||
if (noas1.containsAll(noas2) && noas2.containsAll(noas1)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return Integer.compare(noas1.size(), noas2.size());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (contact.getAndroidId() != null) return 1;
|
||||
return 0;
|
||||
}
|
||||
return fullName.compareTo(contactFullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj.getClass() != LinphoneContact.class) return false;
|
||||
LinphoneContact contact = (LinphoneContact) obj;
|
||||
return (this.compareTo(contact) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
Name related
|
||||
*/
|
||||
|
||||
public String getFullName() {
|
||||
return mFullName;
|
||||
}
|
||||
|
||||
public void setFullName(String name) {
|
||||
mFullName = name;
|
||||
}
|
||||
|
||||
public void setFirstNameAndLastName(String fn, String ln, boolean commitChanges) {
|
||||
if (fn != null && fn.length() == 0 && ln != null && ln.length() == 0) return;
|
||||
if (fn != null && fn.equals(mFirstName) && ln != null && ln.equals(mLastName)) return;
|
||||
|
||||
if (commitChanges) {
|
||||
setName(fn, ln);
|
||||
}
|
||||
|
||||
mFirstName = fn;
|
||||
mLastName = ln;
|
||||
if (mFullName == null) {
|
||||
if (mFirstName != null
|
||||
&& mLastName != null
|
||||
&& mFirstName.length() > 0
|
||||
&& mLastName.length() > 0) {
|
||||
mFullName = mFirstName + " " + mLastName;
|
||||
} else if (mFirstName != null && mFirstName.length() > 0) {
|
||||
mFullName = mFirstName;
|
||||
} else if (mLastName != null && mLastName.length() > 0) {
|
||||
mFullName = mLastName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return mFirstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return mLastName;
|
||||
}
|
||||
|
||||
/*
|
||||
Organization related
|
||||
*/
|
||||
|
||||
public String getOrganization() {
|
||||
return mOrganization;
|
||||
}
|
||||
|
||||
public void setOrganization(String org, boolean commitChanges) {
|
||||
if ((org == null || org.isEmpty()) && (mOrganization == null || mOrganization.isEmpty()))
|
||||
return;
|
||||
if (org != null && org.equals(mOrganization)) return;
|
||||
|
||||
if (commitChanges) {
|
||||
setOrganization(org, mOrganization);
|
||||
}
|
||||
|
||||
mOrganization = org;
|
||||
}
|
||||
|
||||
/*
|
||||
Picture related
|
||||
*/
|
||||
|
||||
public boolean hasPhoto() {
|
||||
return mPhotoUri != null;
|
||||
}
|
||||
|
||||
public Uri getPhotoUri() {
|
||||
return mPhotoUri;
|
||||
}
|
||||
|
||||
private void setPhotoUri(Uri uri) {
|
||||
if (uri.equals(mPhotoUri)) return;
|
||||
mPhotoUri = uri;
|
||||
}
|
||||
|
||||
public Uri getThumbnailUri() {
|
||||
return mThumbnailUri;
|
||||
}
|
||||
|
||||
private void setThumbnailUri(Uri uri) {
|
||||
if (uri.equals(mThumbnailUri)) return;
|
||||
mThumbnailUri = uri;
|
||||
}
|
||||
|
||||
/*
|
||||
Number or address related
|
||||
*/
|
||||
|
||||
public void addNumberOrAddress(LinphoneNumberOrAddress noa) {
|
||||
if (noa == null) return;
|
||||
if (noa.isSIPAddress()) {
|
||||
mHasSipAddress = true;
|
||||
mAddresses.add(noa);
|
||||
} else {
|
||||
boolean found = false;
|
||||
// Check for duplicated phone numbers but with different formats
|
||||
for (LinphoneNumberOrAddress number : mAddresses) {
|
||||
if (!number.isSIPAddress()
|
||||
&& noa.getNormalizedPhone().equals(number.getNormalizedPhone())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
mAddresses.add(noa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<LinphoneNumberOrAddress> getNumbersOrAddresses() {
|
||||
return mAddresses;
|
||||
}
|
||||
|
||||
public boolean hasAddress(String address) {
|
||||
for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) {
|
||||
if (noa.isSIPAddress()) {
|
||||
String value = noa.getValue();
|
||||
if (address.startsWith(value) || value.equals("sip:" + address)) {
|
||||
// Startswith is to workaround the fact that the
|
||||
// address may have a ;gruu= at the end...
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasAddress() {
|
||||
return mHasSipAddress;
|
||||
}
|
||||
|
||||
public void removeNumberOrAddress(LinphoneNumberOrAddress noa) {
|
||||
if (noa != null && noa.getOldValue() != null) {
|
||||
|
||||
removeNumberOrAddress(noa.getOldValue(), noa.isSIPAddress());
|
||||
|
||||
if (isFriend()) {
|
||||
if (noa.isSIPAddress()) {
|
||||
if (!noa.getOldValue().startsWith("sip:")) {
|
||||
noa.setOldValue("sip:" + noa.getOldValue());
|
||||
}
|
||||
}
|
||||
LinphoneNumberOrAddress toRemove = null;
|
||||
for (LinphoneNumberOrAddress address : mAddresses) {
|
||||
if (noa.getOldValue().equals(address.getValue())
|
||||
&& noa.isSIPAddress() == address.isSIPAddress()) {
|
||||
toRemove = address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toRemove != null) {
|
||||
mAddresses.remove(toRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addOrUpdateNumberOrAddress(LinphoneNumberOrAddress noa) {
|
||||
if (noa != null && noa.getValue() != null) {
|
||||
|
||||
addNumberOrAddress(noa.getValue(), noa.getOldValue(), noa.isSIPAddress());
|
||||
|
||||
if (isFriend()) {
|
||||
if (noa.isSIPAddress()) {
|
||||
if (!noa.getValue().startsWith("sip:")) {
|
||||
noa.setValue("sip:" + noa.getValue());
|
||||
}
|
||||
}
|
||||
if (noa.getOldValue() != null) {
|
||||
if (noa.isSIPAddress()) {
|
||||
if (!noa.getOldValue().startsWith("sip:")) {
|
||||
noa.setOldValue("sip:" + noa.getOldValue());
|
||||
}
|
||||
}
|
||||
for (LinphoneNumberOrAddress address : mAddresses) {
|
||||
if (noa.getOldValue().equals(address.getValue())
|
||||
&& noa.isSIPAddress() == address.isSIPAddress()) {
|
||||
address.setValue(noa.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mAddresses.add(noa);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clearAddresses() {
|
||||
mAddresses.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
Friend related
|
||||
*/
|
||||
|
||||
public Friend getFriend() {
|
||||
return mFriend;
|
||||
}
|
||||
|
||||
private void createOrUpdateFriend() {
|
||||
boolean created = false;
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc == null) return;
|
||||
|
||||
if (!isFriend()) {
|
||||
mFriend = lc.createFriend();
|
||||
mFriend.enableSubscribes(false);
|
||||
mFriend.setIncSubscribePolicy(SubscribePolicy.SPDeny);
|
||||
if (isAndroidContact()) {
|
||||
mFriend.setRefKey(getAndroidId());
|
||||
}
|
||||
mFriend.setUserData(this);
|
||||
created = true;
|
||||
}
|
||||
if (isFriend()) {
|
||||
mFriend.edit();
|
||||
mFriend.setName(mFullName);
|
||||
if (mFriend.getVcard() != null) {
|
||||
mFriend.getVcard().setFamilyName(mLastName);
|
||||
mFriend.getVcard().setGivenName(mFirstName);
|
||||
}
|
||||
if (mOrganization != null) {
|
||||
mFriend.getVcard().setOrganization(mOrganization);
|
||||
}
|
||||
|
||||
if (!created) {
|
||||
for (Address address : mFriend.getAddresses()) {
|
||||
mFriend.removeAddress(address);
|
||||
}
|
||||
for (String phone : mFriend.getPhoneNumbers()) {
|
||||
mFriend.removePhoneNumber(phone);
|
||||
}
|
||||
}
|
||||
for (LinphoneNumberOrAddress noa : mAddresses) {
|
||||
if (noa.isSIPAddress()) {
|
||||
Address addr = lc.interpretUrl(noa.getValue());
|
||||
if (addr != null) {
|
||||
mFriend.addAddress(addr);
|
||||
}
|
||||
} else {
|
||||
mFriend.addPhoneNumber(noa.getValue());
|
||||
}
|
||||
}
|
||||
mFriend.done();
|
||||
}
|
||||
if (created) {
|
||||
lc.addFriend(mFriend);
|
||||
}
|
||||
|
||||
if (!ContactsManager.getInstance().hasReadContactsAccess()) {
|
||||
// This refresh is only needed if app has no contacts permission to refresh the list of
|
||||
// Friends.
|
||||
// Otherwise contacts will be refreshed due to changes in native contact and the handler
|
||||
// in ContactsManager
|
||||
ContactsManager.getInstance().fetchContactsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteFriend() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (mFriend != null && lc != null) {
|
||||
for (FriendList list : lc.getFriendsLists()) {
|
||||
list.removeFriend(mFriend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void createOrUpdateFriendFromNativeContact() {
|
||||
if (isAndroidContact()) {
|
||||
createOrUpdateFriend();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFriend() {
|
||||
return mFriend != null;
|
||||
}
|
||||
|
||||
public void setFriend(Friend f) {
|
||||
if (mFriend != null && (f == null || f != mFriend)) {
|
||||
mFriend.setUserData(null);
|
||||
}
|
||||
mFriend = f;
|
||||
if (mFriend != null) {
|
||||
mFriend.setUserData(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInFriendList() {
|
||||
if (mFriend == null) return false;
|
||||
for (LinphoneNumberOrAddress noa : mAddresses) {
|
||||
PresenceModel pm = mFriend.getPresenceModelForUriOrTel(noa.getValue());
|
||||
if (pm != null && pm.getBasicStatus().equals(PresenceBasicStatus.Open)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getContactFromPresenceModelForUriOrTel(String uri) {
|
||||
if (mFriend != null && mFriend.getPresenceModelForUriOrTel(uri) != null) {
|
||||
return mFriend.getPresenceModelForUriOrTel(uri).getContact();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public PresenceBasicStatus getBasicStatusFromPresenceModelForUriOrTel(String uri) {
|
||||
if (mFriend != null && mFriend.getPresenceModelForUriOrTel(uri) != null) {
|
||||
return mFriend.getPresenceModelForUriOrTel(uri).getBasicStatus();
|
||||
}
|
||||
return PresenceBasicStatus.Closed;
|
||||
}
|
||||
|
||||
public boolean hasPresenceModelForUriOrTelCapability(String uri, FriendCapability capability) {
|
||||
if (mFriend != null && mFriend.getPresenceModelForUriOrTel(uri) != null) {
|
||||
return mFriend.getPresenceModelForUriOrTel(uri).hasCapability(capability);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createFriend() {
|
||||
LinphoneContact contact = new LinphoneContact();
|
||||
Friend friend = LinphoneManager.getLc().createFriend();
|
||||
// Disable subscribes for now
|
||||
friend.enableSubscribes(false);
|
||||
friend.setIncSubscribePolicy(SubscribePolicy.SPDeny);
|
||||
contact.mFriend = friend;
|
||||
friend.setUserData(contact);
|
||||
}
|
||||
|
||||
/*
|
||||
Contact related
|
||||
*/
|
||||
|
||||
protected void setAndroidId(String id) {
|
||||
super.setAndroidId(id);
|
||||
setThumbnailUri(getContactThumbnailPictureUri());
|
||||
setPhotoUri(getContactPictureUri());
|
||||
}
|
||||
|
||||
public void syncValuesFromFriend() {
|
||||
if (isFriend()) {
|
||||
mAddresses = new ArrayList<>();
|
||||
mFullName = mFriend.getName();
|
||||
mLastName = mFriend.getVcard().getFamilyName();
|
||||
mFirstName = mFriend.getVcard().getGivenName();
|
||||
mThumbnailUri = null;
|
||||
mPhotoUri = null;
|
||||
mHasSipAddress = mFriend.getAddress() != null;
|
||||
mOrganization = mFriend.getVcard().getOrganization();
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null && lc.vcardSupported()) {
|
||||
for (Address addr : mFriend.getAddresses()) {
|
||||
if (addr != null) {
|
||||
addNumberOrAddress(
|
||||
new LinphoneNumberOrAddress(addr.asStringUriOnly(), true));
|
||||
}
|
||||
}
|
||||
for (String tel : mFriend.getPhoneNumbers()) {
|
||||
if (tel != null) {
|
||||
addNumberOrAddress(new LinphoneNumberOrAddress(tel, false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Address addr = mFriend.getAddress();
|
||||
addNumberOrAddress(new LinphoneNumberOrAddress(addr.asStringUriOnly(), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void syncValuesFromAndroidContact(Context context) {
|
||||
Cursor c =
|
||||
context.getContentResolver()
|
||||
.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
AsyncContactsLoader.PROJECTION,
|
||||
ContactsContract.Data.IN_VISIBLE_GROUP
|
||||
+ " == 1 AND "
|
||||
+ ContactsContract.Data.CONTACT_ID
|
||||
+ " == "
|
||||
+ mAndroidId,
|
||||
null,
|
||||
null);
|
||||
if (c != null) {
|
||||
mAddresses = new ArrayList<>();
|
||||
while (c.moveToNext()) {
|
||||
syncValuesFromAndroidCusor(c);
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void syncValuesFromAndroidCusor(Cursor c) {
|
||||
String displayName =
|
||||
c.getString(c.getColumnIndex(ContactsContract.Data.DISPLAY_NAME_PRIMARY));
|
||||
String mime = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE));
|
||||
String data1 = c.getString(c.getColumnIndex("data1"));
|
||||
String data2 = c.getString(c.getColumnIndex("data2"));
|
||||
String data3 = c.getString(c.getColumnIndex("data3"));
|
||||
String data4 = c.getString(c.getColumnIndex("data4"));
|
||||
String lookupKey = c.getString(c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
|
||||
|
||||
setAndroidLookupKey(lookupKey);
|
||||
setFullName(displayName);
|
||||
|
||||
if (ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mime)) {
|
||||
addNumberOrAddress(new LinphoneNumberOrAddress(data1, data4));
|
||||
} else if (ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE.equals(mime)
|
||||
|| LinphoneManager.getInstance()
|
||||
.getContext()
|
||||
.getString(R.string.linphone_address_mime_type)
|
||||
.equals(mime)) {
|
||||
addNumberOrAddress(new LinphoneNumberOrAddress(data1, true));
|
||||
} else if (ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE.equals(mime)) {
|
||||
setOrganization(data1, false);
|
||||
} else if (ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(mime)) {
|
||||
setFirstNameAndLastName(data2, data3, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
saveChangesCommited();
|
||||
syncValuesFromAndroidContact(LinphoneActivity.instance());
|
||||
createOrUpdateFriend();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
deleteAndroidContact();
|
||||
|
||||
if (isFriend()) {
|
||||
deleteFriend();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasFriendCapability(FriendCapability capability) {
|
||||
if (!isFriend()) return false;
|
||||
|
||||
return getFriend().hasCapability(capability);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,34 +24,34 @@ import java.io.Serializable;
|
|||
public class LinphoneNumberOrAddress implements Serializable, Comparable<LinphoneNumberOrAddress> {
|
||||
private static final long serialVersionUID = -2301689469730072896L;
|
||||
|
||||
private boolean isSIPAddress;
|
||||
private String value, oldValueForUpdatePurpose;
|
||||
private String normalizedPhone;
|
||||
private final boolean mIsSIPAddress;
|
||||
private String mValue, mOldValueForUpdatePurpose;
|
||||
private final String mNormalizedPhone;
|
||||
|
||||
public LinphoneNumberOrAddress(String v, boolean isSIP) {
|
||||
value = v;
|
||||
isSIPAddress = isSIP;
|
||||
oldValueForUpdatePurpose = null;
|
||||
normalizedPhone = null;
|
||||
mValue = v;
|
||||
mIsSIPAddress = isSIP;
|
||||
mOldValueForUpdatePurpose = null;
|
||||
mNormalizedPhone = null;
|
||||
}
|
||||
|
||||
public LinphoneNumberOrAddress(String v, String normalizedV) {
|
||||
value = v;
|
||||
normalizedPhone = normalizedV != null ? normalizedV : v;
|
||||
isSIPAddress = false;
|
||||
oldValueForUpdatePurpose = null;
|
||||
mValue = v;
|
||||
mNormalizedPhone = normalizedV != null ? normalizedV : v;
|
||||
mIsSIPAddress = false;
|
||||
mOldValueForUpdatePurpose = null;
|
||||
}
|
||||
|
||||
public LinphoneNumberOrAddress(String v, boolean isSip, String old) {
|
||||
this(v, isSip);
|
||||
oldValueForUpdatePurpose = old;
|
||||
mOldValueForUpdatePurpose = old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LinphoneNumberOrAddress noa) {
|
||||
if (value != null) {
|
||||
if (mValue != null) {
|
||||
if (noa.isSIPAddress() && isSIPAddress()) {
|
||||
return value.compareTo(noa.getValue());
|
||||
return mValue.compareTo(noa.getValue());
|
||||
} else if (!noa.isSIPAddress() && !isSIPAddress()) {
|
||||
return getNormalizedPhone().compareTo(noa.getNormalizedPhone());
|
||||
}
|
||||
|
|
@ -67,24 +67,26 @@ public class LinphoneNumberOrAddress implements Serializable, Comparable<Linphon
|
|||
}
|
||||
|
||||
public boolean isSIPAddress() {
|
||||
return isSIPAddress;
|
||||
return mIsSIPAddress;
|
||||
}
|
||||
|
||||
public String getOldValue() {
|
||||
return oldValueForUpdatePurpose;
|
||||
return mOldValueForUpdatePurpose;
|
||||
}
|
||||
|
||||
public void setOldValue(String v) {
|
||||
oldValueForUpdatePurpose = v;
|
||||
mOldValueForUpdatePurpose = v;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void setValue(String v) {
|
||||
value = v;
|
||||
mValue = v;
|
||||
}
|
||||
|
||||
public String getNormalizedPhone() { return normalizedPhone != null ? normalizedPhone : value; }
|
||||
public String getNormalizedPhone() {
|
||||
return mNormalizedPhone != null ? mNormalizedPhone : mValue;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
SearchContactViewHolder.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.linphone.R;
|
||||
|
||||
public class SearchContactViewHolder extends RecyclerView.ViewHolder
|
||||
implements View.OnClickListener {
|
||||
public final TextView name;
|
||||
public final TextView address;
|
||||
public final ImageView linphoneContact;
|
||||
public final ImageView isSelect;
|
||||
public final RelativeLayout avatarLayout;
|
||||
public final View disabled;
|
||||
|
||||
private final ClickListener mListener;
|
||||
|
||||
public SearchContactViewHolder(View view, ClickListener listener) {
|
||||
super(view);
|
||||
|
||||
name = view.findViewById(R.id.contact_name);
|
||||
address = view.findViewById(R.id.contact_address);
|
||||
linphoneContact = view.findViewById(R.id.contact_linphone);
|
||||
isSelect = view.findViewById(R.id.contact_is_select);
|
||||
avatarLayout = view.findViewById(R.id.avatar_layout);
|
||||
disabled = view.findViewById(R.id.disabled);
|
||||
|
||||
mListener = listener;
|
||||
view.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mListener != null) {
|
||||
mListener.onItemClicked(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
public interface ClickListener {
|
||||
void onItemClicked(int position);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
package org.linphone.contacts;
|
||||
|
||||
/*
|
||||
SearchContactsAdapter.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.FriendCapability;
|
||||
import org.linphone.core.PresenceBasicStatus;
|
||||
import org.linphone.core.PresenceModel;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.SearchResult;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactViewHolder> {
|
||||
private List<SearchResult> mContacts;
|
||||
private ArrayList<ContactAddress> mContactsSelected;
|
||||
private boolean mOnlySipContact = false;
|
||||
private SearchContactViewHolder.ClickListener mListener;
|
||||
private final boolean mIsOnlyOnePersonSelection;
|
||||
private String mPreviousSearch;
|
||||
private boolean mSecurityEnabled;
|
||||
|
||||
public SearchContactsAdapter(
|
||||
SearchContactViewHolder.ClickListener clickListener,
|
||||
boolean hideSelectionMark,
|
||||
boolean isSecurityEnabled) {
|
||||
mIsOnlyOnePersonSelection = hideSelectionMark;
|
||||
mListener = clickListener;
|
||||
setContactsSelectedList(null);
|
||||
mPreviousSearch = null;
|
||||
mSecurityEnabled = isSecurityEnabled;
|
||||
mContacts = new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<SearchResult> getContacts() {
|
||||
return mContacts;
|
||||
}
|
||||
|
||||
public void setOnlySipContact(boolean enable) {
|
||||
mOnlySipContact = enable;
|
||||
}
|
||||
|
||||
public void setSecurityEnabled(boolean enable) {
|
||||
mSecurityEnabled = enable;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SearchContactViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.search_contact_cell, parent, false);
|
||||
return new SearchContactViewHolder(v, mListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SearchContactViewHolder holder, int position) {
|
||||
SearchResult searchResult = getItem(position);
|
||||
|
||||
LinphoneContact contact;
|
||||
if (searchResult.getFriend() != null && searchResult.getFriend().getUserData() != null) {
|
||||
contact = (LinphoneContact) searchResult.getFriend().getUserData();
|
||||
} else {
|
||||
if (searchResult.getAddress() == null) {
|
||||
contact =
|
||||
ContactsManager.getInstance()
|
||||
.findContactFromPhoneNumber(searchResult.getPhoneNumber());
|
||||
} else {
|
||||
contact =
|
||||
ContactsManager.getInstance()
|
||||
.findContactFromAddress(searchResult.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
final String numberOrAddress =
|
||||
(searchResult.getPhoneNumber() != null)
|
||||
? searchResult.getPhoneNumber()
|
||||
: searchResult.getAddress().asStringUriOnly();
|
||||
|
||||
holder.name.setVisibility(View.GONE);
|
||||
if (contact != null && contact.getFullName() != null) {
|
||||
holder.name.setVisibility(View.VISIBLE);
|
||||
holder.name.setText(contact.getFullName());
|
||||
} else if (searchResult.getAddress() != null) {
|
||||
if (searchResult.getAddress().getUsername() != null) {
|
||||
holder.name.setVisibility(View.VISIBLE);
|
||||
holder.name.setText(searchResult.getAddress().getUsername());
|
||||
} else if (searchResult.getAddress().getDisplayName() != null) {
|
||||
holder.name.setVisibility(View.VISIBLE);
|
||||
holder.name.setText(searchResult.getAddress().getDisplayName());
|
||||
}
|
||||
} else if (searchResult.getAddress() != null) {
|
||||
holder.name.setVisibility(View.VISIBLE);
|
||||
holder.name.setText(
|
||||
(searchResult.getAddress().getDisplayName() != null)
|
||||
? searchResult.getAddress().getDisplayName()
|
||||
: searchResult.getAddress().getUsername());
|
||||
}
|
||||
|
||||
holder.disabled.setVisibility(View.GONE);
|
||||
if (contact != null) {
|
||||
if (contact.getFullName() == null
|
||||
&& contact.getFirstName() == null
|
||||
&& contact.getLastName() == null) {
|
||||
contact.setFullName(holder.name.getText().toString());
|
||||
}
|
||||
ContactAvatar.displayAvatar(
|
||||
contact,
|
||||
contact.hasFriendCapability(FriendCapability.LimeX3Dh),
|
||||
holder.avatarLayout);
|
||||
|
||||
if ((!mIsOnlyOnePersonSelection
|
||||
&& !searchResult.hasCapability(FriendCapability.GroupChat))
|
||||
|| (mSecurityEnabled
|
||||
&& !searchResult.hasCapability(FriendCapability.LimeX3Dh))) {
|
||||
// Disable row, contact doesn't have the required capabilities
|
||||
holder.disabled.setVisibility(View.VISIBLE);
|
||||
} else if (mSecurityEnabled || !mIsOnlyOnePersonSelection) {
|
||||
ProxyConfig lpc =
|
||||
LinphoneManager.getLcIfManagerNotDestroyedOrNull().getDefaultProxyConfig();
|
||||
if (lpc != null
|
||||
&& searchResult.getAddress() != null
|
||||
&& lpc.getIdentityAddress().weakEqual(searchResult.getAddress())) {
|
||||
// Disable row, we can't use our own address in a group chat room
|
||||
holder.disabled.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(holder.name.getText().toString(), holder.avatarLayout);
|
||||
}
|
||||
|
||||
holder.address.setText(numberOrAddress);
|
||||
if (holder.linphoneContact != null) {
|
||||
holder.linphoneContact.setVisibility(View.GONE);
|
||||
if (searchResult.getFriend() != null
|
||||
&& contact != null
|
||||
&& contact.getBasicStatusFromPresenceModelForUriOrTel(numberOrAddress)
|
||||
== PresenceBasicStatus.Open) {
|
||||
holder.linphoneContact.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
if (holder.isSelect != null) {
|
||||
if (isContactSelected(searchResult)) {
|
||||
holder.isSelect.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.isSelect.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
if (mIsOnlyOnePersonSelection) {
|
||||
holder.isSelect.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public synchronized boolean isContactSelected(SearchResult sr) {
|
||||
for (ContactAddress c : mContactsSelected) {
|
||||
Address addr = c.getAddress();
|
||||
if (addr != null && sr.getAddress() != null) {
|
||||
if (addr.weakEqual(sr.getAddress())) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (c.getPhoneNumber() != null && sr.getPhoneNumber() != null) {
|
||||
if (c.getPhoneNumber().compareTo(sr.getPhoneNumber()) == 0) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized ArrayList<ContactAddress> getContactsSelectedList() {
|
||||
return mContactsSelected;
|
||||
}
|
||||
|
||||
public synchronized void setContactsSelectedList(ArrayList<ContactAddress> contactsList) {
|
||||
if (contactsList == null) {
|
||||
mContactsSelected = new ArrayList<>();
|
||||
} else {
|
||||
mContactsSelected = contactsList;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean toggleContactSelection(ContactAddress ca) {
|
||||
if (mContactsSelected.contains(ca)) {
|
||||
mContactsSelected.remove(ca);
|
||||
return false;
|
||||
} else {
|
||||
mContactsSelected.add(ca);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private SearchResult getItem(int position) {
|
||||
return mContacts.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mContacts.size();
|
||||
}
|
||||
|
||||
public void searchContacts(String search) {
|
||||
List<SearchResult> result = new ArrayList<>();
|
||||
|
||||
if (mPreviousSearch != null) {
|
||||
if (mPreviousSearch.length() > search.length()) {
|
||||
ContactsManager.getInstance().getMagicSearch().resetSearchCache();
|
||||
}
|
||||
}
|
||||
mPreviousSearch = search;
|
||||
|
||||
String domain = "";
|
||||
ProxyConfig prx = LinphoneManager.getLc().getDefaultProxyConfig();
|
||||
if (prx != null) domain = prx.getDomain();
|
||||
SearchResult[] searchResults =
|
||||
ContactsManager.getInstance()
|
||||
.getMagicSearch()
|
||||
.getContactListFromFilter(search, mOnlySipContact ? domain : "");
|
||||
|
||||
for (SearchResult sr : searchResults) {
|
||||
if (LinphoneActivity.instance()
|
||||
.getResources()
|
||||
.getBoolean(R.bool.hide_sip_contacts_without_presence)) {
|
||||
if (sr.getFriend() != null) {
|
||||
PresenceModel pm =
|
||||
sr.getFriend()
|
||||
.getPresenceModelForUriOrTel(sr.getAddress().asStringUriOnly());
|
||||
if (pm != null && pm.getBasicStatus().equals(PresenceBasicStatus.Open)) {
|
||||
result.add(sr);
|
||||
} else {
|
||||
pm = sr.getFriend().getPresenceModelForUriOrTel(sr.getPhoneNumber());
|
||||
if (pm != null && pm.getBasicStatus().equals(PresenceBasicStatus.Open)) {
|
||||
result.add(sr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.add(sr);
|
||||
}
|
||||
}
|
||||
|
||||
mContacts = result;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package org.linphone.firebase;
|
|||
|
||||
/*
|
||||
FirebaseMessaging.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
Copyright (C) 2017-2019 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
|
|
@ -19,20 +19,29 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.LinphoneUtils;
|
||||
import org.linphone.mediastream.Log;
|
||||
|
||||
import static android.content.Intent.ACTION_MAIN;
|
||||
|
||||
import android.content.Intent;
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
public class FirebaseMessaging extends FirebaseMessagingService {
|
||||
public FirebaseMessaging() {
|
||||
public FirebaseMessaging() {}
|
||||
|
||||
@Override
|
||||
public void onNewToken(final String token) {
|
||||
android.util.Log.i("FirebaseIdService", "[Push Notification] Refreshed token: " + token);
|
||||
|
||||
LinphoneUtils.dispatchOnUIThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LinphonePreferences.instance().setPushNotificationRegistrationID(token);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -45,17 +54,6 @@ public class FirebaseMessaging extends FirebaseMessagingService {
|
|||
intent.setClass(this, LinphoneService.class);
|
||||
intent.putExtra("PushNotification", true);
|
||||
startService(intent);
|
||||
} else if (LinphoneManager.isInstanciated() && LinphoneManager.getLc().getCallsNb() == 0) {
|
||||
LinphoneUtils.dispatchOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i("[Push Notification] Push notification received with LinphoneManager still alive");
|
||||
if (LinphoneManager.isInstanciated() && LinphoneManager.getLc().getCallsNb() == 0) {
|
||||
LinphoneManager.getLc().setNetworkReachable(false);
|
||||
LinphoneManager.getLc().setNetworkReachable(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package org.linphone.firebase;
|
||||
|
||||
/*
|
||||
FirebasePushHelper.java
|
||||
Copyright (C) 2019 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.iid.InstanceIdResult;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.PushNotificationUtils;
|
||||
|
||||
public class FirebasePushHelper implements PushNotificationUtils.PushHelperInterface {
|
||||
public FirebasePushHelper() {}
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
Log.i(
|
||||
"[Push Notification] firebase push sender id "
|
||||
+ context.getString(R.string.gcm_defaultSenderId));
|
||||
try {
|
||||
FirebaseInstanceId.getInstance()
|
||||
.getInstanceId()
|
||||
.addOnCompleteListener(
|
||||
new OnCompleteListener<InstanceIdResult>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<InstanceIdResult> task) {
|
||||
if (!task.isSuccessful()) {
|
||||
Log.e(
|
||||
"[Push Notification] firebase getInstanceId failed: "
|
||||
+ task.getException());
|
||||
return;
|
||||
}
|
||||
String token = task.getResult().getToken();
|
||||
Log.i("[Push Notification] firebase token is: " + token);
|
||||
LinphonePreferences.instance()
|
||||
.setPushNotificationRegistrationID(token);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("[Push Notification] firebase not available.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(Context context) {
|
||||
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
|
||||
int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context);
|
||||
return resultCode == ConnectionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
188
app/src/main/java/org/linphone/fragments/AboutFragment.java
Normal file
188
app/src/main/java/org/linphone/fragments/AboutFragment.java
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package org.linphone.fragments;
|
||||
/*
|
||||
AboutFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import org.linphone.BuildConfig;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.Core.LogCollectionUploadState;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
public class AboutFragment extends Fragment implements OnClickListener {
|
||||
private View mSendLogButton = null;
|
||||
private View mResetLogButton = null;
|
||||
private CoreListenerStub mListener;
|
||||
private ProgressDialog mProgress;
|
||||
private boolean mUploadInProgress;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.about, container, false);
|
||||
|
||||
TextView aboutVersion = view.findViewById(R.id.about_android_version);
|
||||
TextView aboutLiblinphoneVersion = view.findViewById(R.id.about_liblinphone_sdk_version);
|
||||
aboutLiblinphoneVersion.setText(
|
||||
String.format(
|
||||
getString(R.string.about_liblinphone_sdk_version),
|
||||
getString(R.string.linphone_sdk_version)
|
||||
+ " ("
|
||||
+ getString(R.string.linphone_sdk_branch)
|
||||
+ ")"));
|
||||
// We can't access a library's BuildConfig, so we have to set it as a resource
|
||||
aboutVersion.setText(
|
||||
String.format(
|
||||
getString(R.string.about_version),
|
||||
BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")"));
|
||||
|
||||
TextView privacyPolicy = view.findViewById(R.id.privacy_policy_link);
|
||||
privacyPolicy.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent browserIntent =
|
||||
new Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse(getString(R.string.about_privacy_policy_link)));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
});
|
||||
|
||||
TextView license = view.findViewById(R.id.about_text);
|
||||
license.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent browserIntent =
|
||||
new Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse(getString(R.string.about_license_link)));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
});
|
||||
|
||||
mSendLogButton = view.findViewById(R.id.send_log);
|
||||
mSendLogButton.setOnClickListener(this);
|
||||
mSendLogButton.setVisibility(
|
||||
LinphonePreferences.instance().isDebugEnabled() ? View.VISIBLE : View.GONE);
|
||||
|
||||
mResetLogButton = view.findViewById(R.id.reset_log);
|
||||
mResetLogButton.setOnClickListener(this);
|
||||
mResetLogButton.setVisibility(
|
||||
LinphonePreferences.instance().isDebugEnabled() ? View.VISIBLE : View.GONE);
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onLogCollectionUploadProgressIndication(
|
||||
Core lc, int offset, int total) {}
|
||||
|
||||
@Override
|
||||
public void onLogCollectionUploadStateChanged(
|
||||
Core lc, LogCollectionUploadState state, String info) {
|
||||
if (state == LogCollectionUploadState.InProgress) {
|
||||
displayUploadLogsInProgress();
|
||||
} else if (state == LogCollectionUploadState.Delivered
|
||||
|| state == LogCollectionUploadState.NotDelivered) {
|
||||
mUploadInProgress = false;
|
||||
if (mProgress != null) mProgress.dismiss();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void displayUploadLogsInProgress() {
|
||||
if (mUploadInProgress) {
|
||||
return;
|
||||
}
|
||||
mUploadInProgress = true;
|
||||
|
||||
mProgress = ProgressDialog.show(LinphoneActivity.instance(), null, null);
|
||||
Drawable d =
|
||||
new ColorDrawable(ContextCompat.getColor(getActivity(), R.color.light_grey_color));
|
||||
d.setAlpha(200);
|
||||
mProgress
|
||||
.getWindow()
|
||||
.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT);
|
||||
mProgress.getWindow().setBackgroundDrawable(d);
|
||||
mProgress.setContentView(R.layout.wait_layout);
|
||||
mProgress.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.ABOUT);
|
||||
}
|
||||
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (v == mSendLogButton) {
|
||||
if (lc != null) {
|
||||
lc.uploadLogCollection();
|
||||
}
|
||||
} else if (v == mResetLogButton) {
|
||||
if (lc != null) {
|
||||
lc.resetLogCollection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,33 +30,36 @@ import android.view.View.OnClickListener;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.activities.LinphoneActivity;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.mediastream.Log;
|
||||
import org.linphone.ui.AddressAware;
|
||||
import org.linphone.ui.AddressText;
|
||||
import org.linphone.ui.CallButton;
|
||||
import org.linphone.ui.EraseButton;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.views.AddressAware;
|
||||
import org.linphone.views.AddressText;
|
||||
import org.linphone.views.CallButton;
|
||||
import org.linphone.views.EraseButton;
|
||||
|
||||
public class DialerFragment extends Fragment {
|
||||
private static DialerFragment instance;
|
||||
private static boolean isCallTransferOngoing = false;
|
||||
private static DialerFragment sInstance;
|
||||
private static boolean sIsCallTransferOngoing = false;
|
||||
|
||||
private AddressAware numpad;
|
||||
private AddressAware mNumpad;
|
||||
private AddressText mAddress;
|
||||
private CallButton mCall;
|
||||
private ImageView mAddContact;
|
||||
private OnClickListener addContactListener, cancelListener, transferListener;
|
||||
private boolean shouldEmptyAddressField = true;
|
||||
private OnClickListener mAddContactListener, mCancelListener, mTransferListener;
|
||||
|
||||
/** @return null if not ready yet */
|
||||
public static DialerFragment instance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.dialer, container, false);
|
||||
|
||||
mAddress = view.findViewById(R.id.address);
|
||||
|
|
@ -67,141 +70,143 @@ public class DialerFragment extends Fragment {
|
|||
|
||||
mCall = view.findViewById(R.id.call);
|
||||
mCall.setAddressWidget(mAddress);
|
||||
if (LinphoneActivity.isInstanciated() && LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null && LinphoneManager.getLcIfManagerNotDestroyedOrNull().getCallsNb() > 0) {
|
||||
if (isCallTransferOngoing) {
|
||||
if (LinphoneActivity.isInstanciated()
|
||||
&& LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null
|
||||
&& LinphoneManager.getLcIfManagerNotDestroyedOrNull().getCallsNb() > 0) {
|
||||
if (sIsCallTransferOngoing) {
|
||||
mCall.setImageResource(R.drawable.call_transfer);
|
||||
} else {
|
||||
mCall.setImageResource(R.drawable.call_add);
|
||||
}
|
||||
} else {
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null && LinphoneManager.getLcIfManagerNotDestroyedOrNull().getVideoActivationPolicy().getAutomaticallyInitiate()) {
|
||||
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null
|
||||
&& LinphoneManager.getLcIfManagerNotDestroyedOrNull()
|
||||
.getVideoActivationPolicy()
|
||||
.getAutomaticallyInitiate()) {
|
||||
mCall.setImageResource(R.drawable.call_video_start);
|
||||
} else {
|
||||
mCall.setImageResource(R.drawable.call_audio_start);
|
||||
}
|
||||
}
|
||||
|
||||
numpad = view.findViewById(R.id.numpad);
|
||||
if (numpad != null) {
|
||||
numpad.setAddressWidget(mAddress);
|
||||
mNumpad = view.findViewById(R.id.numpad);
|
||||
if (mNumpad != null) {
|
||||
mNumpad.setAddressWidget(mAddress);
|
||||
}
|
||||
|
||||
mAddContact = view.findViewById(R.id.add_contact);
|
||||
mAddContact.setEnabled(!(LinphoneActivity.isInstanciated() && LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null && LinphoneManager.getLc().getCallsNb() > 0));
|
||||
mAddContact.setEnabled(
|
||||
!(LinphoneActivity.isInstanciated()
|
||||
&& LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null
|
||||
&& LinphoneManager.getLc().getCallsNb() > 0));
|
||||
|
||||
addContactListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LinphoneActivity.instance().displayContactsForEdition(mAddress.getText().toString());
|
||||
}
|
||||
};
|
||||
cancelListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LinphoneActivity.instance().resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
|
||||
}
|
||||
};
|
||||
transferListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Core lc = LinphoneManager.getLc();
|
||||
if (lc.getCurrentCall() == null) {
|
||||
return;
|
||||
}
|
||||
lc.transferCall(lc.getCurrentCall(), mAddress.getText().toString());
|
||||
isCallTransferOngoing = false;
|
||||
LinphoneActivity.instance().resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
|
||||
}
|
||||
};
|
||||
mAddContactListener =
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LinphoneActivity.instance()
|
||||
.displayContactsForEdition(mAddress.getText().toString());
|
||||
}
|
||||
};
|
||||
mCancelListener =
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LinphoneActivity.instance()
|
||||
.resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
|
||||
}
|
||||
};
|
||||
mTransferListener =
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Core lc = LinphoneManager.getLc();
|
||||
if (lc.getCurrentCall() == null) {
|
||||
return;
|
||||
}
|
||||
lc.transferCall(lc.getCurrentCall(), mAddress.getText().toString());
|
||||
sIsCallTransferOngoing = false;
|
||||
LinphoneActivity.instance()
|
||||
.resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
|
||||
}
|
||||
};
|
||||
|
||||
resetLayout(isCallTransferOngoing);
|
||||
resetLayout();
|
||||
|
||||
if (getArguments() != null) {
|
||||
shouldEmptyAddressField = false;
|
||||
String number = getArguments().getString("SipUri");
|
||||
String displayName = getArguments().getString("DisplayName");
|
||||
String photo = getArguments().getString("PhotoUri");
|
||||
mAddress.setText(number);
|
||||
if (displayName != null) {
|
||||
mAddress.setDisplayedName(displayName);
|
||||
}
|
||||
}
|
||||
|
||||
instance = this;
|
||||
sInstance = this;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if not ready yet
|
||||
*/
|
||||
public static DialerFragment instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
instance = null;
|
||||
sInstance = null;
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
instance = this;
|
||||
sInstance = this;
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.DIALER);
|
||||
LinphoneActivity.instance().updateDialerFragment(this);
|
||||
LinphoneActivity.instance().updateDialerFragment();
|
||||
LinphoneActivity.instance().showStatusBar();
|
||||
LinphoneActivity.instance().hideTabBar(false);
|
||||
}
|
||||
|
||||
boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||
boolean isOrientationLandscape =
|
||||
getResources().getConfiguration().orientation
|
||||
== Configuration.ORIENTATION_LANDSCAPE;
|
||||
if (isOrientationLandscape && !getResources().getBoolean(R.bool.isTablet)) {
|
||||
((LinearLayout) numpad).setVisibility(View.GONE);
|
||||
((LinearLayout) mNumpad).setVisibility(View.GONE);
|
||||
} else {
|
||||
((LinearLayout) numpad).setVisibility(View.VISIBLE);
|
||||
((LinearLayout) mNumpad).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (shouldEmptyAddressField) {
|
||||
mAddress.setText("");
|
||||
} else {
|
||||
shouldEmptyAddressField = true;
|
||||
}
|
||||
resetLayout(isCallTransferOngoing);
|
||||
resetLayout();
|
||||
|
||||
String addressWaitingToBeCalled = LinphoneActivity.instance().mAddressWaitingToBeCalled;
|
||||
String addressWaitingToBeCalled = LinphoneActivity.instance().addressWaitingToBeCalled;
|
||||
if (addressWaitingToBeCalled != null) {
|
||||
mAddress.setText(addressWaitingToBeCalled);
|
||||
if (getResources().getBoolean(R.bool.automatically_start_intercepted_outgoing_gsm_call)) {
|
||||
if (getResources()
|
||||
.getBoolean(R.bool.automatically_start_intercepted_outgoing_gsm_call)) {
|
||||
newOutgoingCall(addressWaitingToBeCalled);
|
||||
}
|
||||
LinphoneActivity.instance().mAddressWaitingToBeCalled = null;
|
||||
LinphoneActivity.instance().addressWaitingToBeCalled = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void resetLayout(boolean callTransfer) {
|
||||
public void resetLayout() {
|
||||
if (!LinphoneActivity.isInstanciated()) {
|
||||
return;
|
||||
}
|
||||
isCallTransferOngoing = LinphoneActivity.instance().isCallTransfer();
|
||||
sIsCallTransferOngoing = LinphoneActivity.instance().isCallTransfer();
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lc.getCallsNb() > 0) {
|
||||
if (isCallTransferOngoing) {
|
||||
if (sIsCallTransferOngoing) {
|
||||
mCall.setImageResource(R.drawable.call_transfer);
|
||||
mCall.setExternalClickListener(transferListener);
|
||||
mCall.setExternalClickListener(mTransferListener);
|
||||
} else {
|
||||
mCall.setImageResource(R.drawable.call_add);
|
||||
mCall.resetClickListener();
|
||||
}
|
||||
mAddContact.setEnabled(true);
|
||||
mAddContact.setImageResource(R.drawable.call_alt_back);
|
||||
mAddContact.setOnClickListener(cancelListener);
|
||||
mAddContact.setImageResource(R.drawable.call_back);
|
||||
mAddContact.setOnClickListener(mCancelListener);
|
||||
} else {
|
||||
if (LinphoneManager.getLc().getVideoActivationPolicy().getAutomaticallyInitiate()) {
|
||||
mCall.setImageResource(R.drawable.call_video_start);
|
||||
|
|
@ -209,18 +214,20 @@ public class DialerFragment extends Fragment {
|
|||
mCall.setImageResource(R.drawable.call_audio_start);
|
||||
}
|
||||
mAddContact.setEnabled(false);
|
||||
mAddContact.setImageResource(R.drawable.contact_add_button);
|
||||
mAddContact.setOnClickListener(addContactListener);
|
||||
mAddContact.setImageResource(R.drawable.contact_add);
|
||||
mAddContact.setOnClickListener(mAddContactListener);
|
||||
enableDisableAddContact();
|
||||
}
|
||||
}
|
||||
|
||||
public void enableDisableAddContact() {
|
||||
mAddContact.setEnabled(LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null && LinphoneManager.getLc().getCallsNb() > 0 || !mAddress.getText().toString().equals(""));
|
||||
mAddContact.setEnabled(
|
||||
LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null
|
||||
&& LinphoneManager.getLc().getCallsNb() > 0
|
||||
|| !mAddress.getText().toString().equals(""));
|
||||
}
|
||||
|
||||
public void displayTextInAddressBar(String numberOrSipAddress) {
|
||||
shouldEmptyAddressField = false;
|
||||
mAddress.setText(numberOrSipAddress);
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +245,11 @@ public class DialerFragment extends Fragment {
|
|||
mAddress.setText(intent.getData().getSchemeSpecificPart());
|
||||
} else {
|
||||
Uri contactUri = intent.getData();
|
||||
String address = ContactsManager.getAddressOrNumberForAndroidContact(LinphoneService.instance().getContentResolver(), contactUri);
|
||||
String address =
|
||||
ContactsManager.getInstance()
|
||||
.getAddressOrNumberForAndroidContact(
|
||||
LinphoneService.instance().getContentResolver(),
|
||||
contactUri);
|
||||
if (address != null) {
|
||||
mAddress.setText(address);
|
||||
} else {
|
||||
|
|
@ -24,17 +24,14 @@ import android.os.Bundle;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.linphone.R;
|
||||
|
||||
public class EmptyFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
|
||||
View view = inflater.inflate(R.layout.empty_fragment, container, false);
|
||||
return view;
|
||||
return inflater.inflate(R.layout.empty_fragment, container, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -31,12 +31,15 @@ public enum FragmentsAvailable {
|
|||
ABOUT,
|
||||
ACCOUNT_SETTINGS,
|
||||
SETTINGS,
|
||||
SETTINGS_SUBLEVEL,
|
||||
CHAT_LIST,
|
||||
CHAT,
|
||||
CREATE_CHAT,
|
||||
INFO_GROUP_CHAT,
|
||||
GROUP_CHAT,
|
||||
MESSAGE_IMDN;
|
||||
MESSAGE_IMDN,
|
||||
CONTACT_DEVICES,
|
||||
RECORDING_LIST;
|
||||
|
||||
public boolean shouldAddItselfToTheRightOf(FragmentsAvailable fragment) {
|
||||
switch (this) {
|
||||
|
|
@ -44,20 +47,33 @@ public enum FragmentsAvailable {
|
|||
return fragment == HISTORY_LIST || fragment == HISTORY_DETAIL;
|
||||
|
||||
case CONTACT_DETAIL:
|
||||
return fragment == CONTACTS_LIST || fragment == CONTACT_EDITOR || fragment == CONTACT_DETAIL;
|
||||
return fragment == CONTACTS_LIST
|
||||
|| fragment == CONTACT_EDITOR
|
||||
|| fragment == CONTACT_DETAIL;
|
||||
|
||||
case CONTACT_EDITOR:
|
||||
return fragment == CONTACTS_LIST || fragment == CONTACT_DETAIL || fragment == CONTACT_EDITOR;
|
||||
return fragment == CONTACTS_LIST
|
||||
|| fragment == CONTACT_DETAIL
|
||||
|| fragment == CONTACT_EDITOR;
|
||||
|
||||
case CHAT:
|
||||
return fragment == CHAT_LIST || fragment == CHAT;
|
||||
|
||||
case GROUP_CHAT:
|
||||
return fragment == CHAT_LIST || fragment == GROUP_CHAT;
|
||||
return fragment == CHAT_LIST
|
||||
|| fragment == GROUP_CHAT
|
||||
|| fragment == INFO_GROUP_CHAT
|
||||
|| fragment == CREATE_CHAT;
|
||||
|
||||
case MESSAGE_IMDN:
|
||||
return fragment == GROUP_CHAT || fragment == MESSAGE_IMDN;
|
||||
|
||||
case SETTINGS_SUBLEVEL:
|
||||
return fragment == SETTINGS || fragment == SETTINGS_SUBLEVEL;
|
||||
|
||||
case CONTACT_DEVICES:
|
||||
return fragment == GROUP_CHAT || fragment == CONTACT_DEVICES;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
509
app/src/main/java/org/linphone/fragments/StatusFragment.java
Normal file
509
app/src/main/java/org/linphone/fragments/StatusFragment.java
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
package org.linphone.fragments;
|
||||
/*
|
||||
StatusFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.assistant.AssistantActivity;
|
||||
import org.linphone.call.CallActivity;
|
||||
import org.linphone.call.CallIncomingActivity;
|
||||
import org.linphone.call.CallOutgoingActivity;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.Content;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.CoreListenerStub;
|
||||
import org.linphone.core.Event;
|
||||
import org.linphone.core.MediaEncryption;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.RegistrationState;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
|
||||
public class StatusFragment extends Fragment {
|
||||
private final Handler mRefreshHandler = new Handler();
|
||||
private TextView mStatusText, mVoicemailCount;
|
||||
private ImageView mStatusLed, mCallQuality, mEncryption, mMenu, mVoicemail;
|
||||
private Runnable mCallQualityUpdater;
|
||||
private boolean mIsInCall, mIsAttached = false;
|
||||
private CoreListenerStub mListener;
|
||||
private Dialog mZrtpDialog = null;
|
||||
private int mDisplayedQuality = -1;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.status, container, false);
|
||||
|
||||
mStatusText = view.findViewById(R.id.status_text);
|
||||
mStatusLed = view.findViewById(R.id.status_led);
|
||||
mCallQuality = view.findViewById(R.id.call_quality);
|
||||
mEncryption = view.findViewById(R.id.encryption);
|
||||
mMenu = view.findViewById(R.id.side_menu_button);
|
||||
mVoicemail = view.findViewById(R.id.voicemail);
|
||||
mVoicemailCount = view.findViewById(R.id.voicemail_count);
|
||||
|
||||
// We create it once to not delay the first display
|
||||
populateSliderContent();
|
||||
|
||||
mListener =
|
||||
new CoreListenerStub() {
|
||||
@Override
|
||||
public void onRegistrationStateChanged(
|
||||
final Core lc,
|
||||
final ProxyConfig proxy,
|
||||
final RegistrationState state,
|
||||
String smessage) {
|
||||
if (!mIsAttached || !LinphoneService.isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lc.getProxyConfigList() == null) {
|
||||
mStatusLed.setImageResource(R.drawable.led_disconnected);
|
||||
mStatusText.setText(getString(R.string.no_account));
|
||||
} else {
|
||||
mStatusLed.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (lc.getDefaultProxyConfig() != null
|
||||
&& lc.getDefaultProxyConfig().equals(proxy)) {
|
||||
mStatusLed.setImageResource(getStatusIconResource(state));
|
||||
mStatusText.setText(getStatusIconText(state));
|
||||
} else if (lc.getDefaultProxyConfig() == null) {
|
||||
mStatusLed.setImageResource(getStatusIconResource(state));
|
||||
mStatusText.setText(getStatusIconText(state));
|
||||
}
|
||||
|
||||
try {
|
||||
mStatusText.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Core core =
|
||||
LinphoneManager
|
||||
.getLcIfManagerNotDestroyedOrNull();
|
||||
if (core != null) {
|
||||
core.refreshRegisters();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IllegalStateException ise) {
|
||||
Log.e(ise);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotifyReceived(
|
||||
Core lc, Event ev, String eventName, Content content) {
|
||||
|
||||
if (!content.getType().equals("application")) return;
|
||||
if (!content.getSubtype().equals("simple-message-summary")) return;
|
||||
|
||||
if (content.getSize() == 0) return;
|
||||
|
||||
int unreadCount = 0;
|
||||
String data = content.getStringBuffer().toLowerCase();
|
||||
String[] voiceMail = data.split("voice-message: ");
|
||||
if (voiceMail.length >= 2) {
|
||||
final String[] intToParse = voiceMail[1].split("/", 0);
|
||||
try {
|
||||
unreadCount = Integer.parseInt(intToParse[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
|
||||
}
|
||||
if (unreadCount > 0) {
|
||||
mVoicemailCount.setText(String.valueOf(unreadCount));
|
||||
mVoicemail.setVisibility(View.VISIBLE);
|
||||
mVoicemailCount.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mVoicemail.setVisibility(View.GONE);
|
||||
mVoicemailCount.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mIsAttached = true;
|
||||
Activity activity = getActivity();
|
||||
|
||||
if (activity instanceof LinphoneActivity) {
|
||||
((LinphoneActivity) activity).updateStatusFragment(this);
|
||||
} else if (activity instanceof CallActivity) {
|
||||
((CallActivity) activity).updateStatusFragment(this);
|
||||
} else if (activity instanceof AssistantActivity) {
|
||||
((AssistantActivity) activity).updateStatusFragment(this);
|
||||
}
|
||||
mIsInCall =
|
||||
activity instanceof CallActivity
|
||||
|| activity instanceof CallIncomingActivity
|
||||
|| activity instanceof CallOutgoingActivity;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setCoreListener() {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
|
||||
ProxyConfig lpc = lc.getDefaultProxyConfig();
|
||||
if (lpc != null) {
|
||||
mListener.onRegistrationStateChanged(lc, lpc, lpc.getState(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mIsAttached = false;
|
||||
}
|
||||
|
||||
// NORMAL STATUS BAR
|
||||
|
||||
private void populateSliderContent() {
|
||||
if (LinphoneManager.isInstanciated() && LinphoneManager.getLc() != null) {
|
||||
mVoicemailCount.setVisibility(View.GONE);
|
||||
|
||||
if (mIsInCall && mIsAttached) {
|
||||
// Call call = LinphoneManager.getLc().getCurrentCall();
|
||||
// initCallStatsRefresher(call, callStats);
|
||||
} else if (!mIsInCall) {
|
||||
mVoicemailCount.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (LinphoneManager.getLc().getProxyConfigList().length == 0) {
|
||||
mStatusLed.setImageResource(R.drawable.led_disconnected);
|
||||
mStatusText.setText(getString(R.string.no_account));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetAccountStatus() {
|
||||
if (LinphoneManager.getLc().getProxyConfigList().length == 0) {
|
||||
mStatusLed.setImageResource(R.drawable.led_disconnected);
|
||||
mStatusText.setText(getString(R.string.no_account));
|
||||
}
|
||||
}
|
||||
|
||||
public void enableSideMenu(boolean enabled) {
|
||||
mMenu.setEnabled(enabled);
|
||||
}
|
||||
|
||||
private int getStatusIconResource(RegistrationState state) {
|
||||
try {
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
boolean defaultAccountConnected =
|
||||
(lc != null
|
||||
&& lc.getDefaultProxyConfig() != null
|
||||
&& lc.getDefaultProxyConfig().getState() == RegistrationState.Ok);
|
||||
if (state == RegistrationState.Ok && defaultAccountConnected) {
|
||||
return R.drawable.led_connected;
|
||||
} else if (state == RegistrationState.Progress) {
|
||||
return R.drawable.led_inprogress;
|
||||
} else if (state == RegistrationState.Failed) {
|
||||
return R.drawable.led_error;
|
||||
} else {
|
||||
return R.drawable.led_disconnected;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
return R.drawable.led_disconnected;
|
||||
}
|
||||
|
||||
private String getStatusIconText(RegistrationState state) {
|
||||
Context context = getActivity();
|
||||
if (!mIsAttached && LinphoneActivity.isInstanciated())
|
||||
context = LinphoneActivity.instance();
|
||||
else if (!mIsAttached && LinphoneService.isReady()) context = LinphoneService.instance();
|
||||
|
||||
try {
|
||||
if (state == RegistrationState.Ok
|
||||
&& LinphoneManager.getLcIfManagerNotDestroyedOrNull()
|
||||
.getDefaultProxyConfig()
|
||||
.getState()
|
||||
== RegistrationState.Ok) {
|
||||
return context.getString(R.string.status_connected);
|
||||
} else if (state == RegistrationState.Progress) {
|
||||
return context.getString(R.string.status_in_progress);
|
||||
} else if (state == RegistrationState.Failed) {
|
||||
return context.getString(R.string.status_error);
|
||||
} else {
|
||||
return context.getString(R.string.status_not_connected);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
return context.getString(R.string.status_not_connected);
|
||||
}
|
||||
|
||||
// INCALL STATUS BAR
|
||||
private void startCallQuality() {
|
||||
mCallQuality.setVisibility(View.VISIBLE);
|
||||
mRefreshHandler.postDelayed(
|
||||
mCallQualityUpdater =
|
||||
new Runnable() {
|
||||
final Call mCurrentCall = LinphoneManager.getLc().getCurrentCall();
|
||||
|
||||
public void run() {
|
||||
if (mCurrentCall == null) {
|
||||
mCallQualityUpdater = null;
|
||||
return;
|
||||
}
|
||||
float newQuality = mCurrentCall.getCurrentQuality();
|
||||
updateQualityOfSignalIcon(newQuality);
|
||||
|
||||
if (mIsInCall) {
|
||||
mRefreshHandler.postDelayed(this, 1000);
|
||||
} else mCallQualityUpdater = null;
|
||||
}
|
||||
},
|
||||
1000);
|
||||
}
|
||||
|
||||
private void updateQualityOfSignalIcon(float quality) {
|
||||
int iQuality = (int) quality;
|
||||
|
||||
if (iQuality == mDisplayedQuality) return;
|
||||
if (quality >= 4) // Good Quality
|
||||
{
|
||||
mCallQuality.setImageResource(R.drawable.call_quality_indicator_4);
|
||||
} else if (quality >= 3) // Average quality
|
||||
{
|
||||
mCallQuality.setImageResource(R.drawable.call_quality_indicator_3);
|
||||
} else if (quality >= 2) // Low quality
|
||||
{
|
||||
mCallQuality.setImageResource(R.drawable.call_quality_indicator_2);
|
||||
} else if (quality >= 1) // Very low quality
|
||||
{
|
||||
mCallQuality.setImageResource(R.drawable.call_quality_indicator_1);
|
||||
} else // Worst quality
|
||||
{
|
||||
mCallQuality.setImageResource(R.drawable.call_quality_indicator_0);
|
||||
}
|
||||
mDisplayedQuality = iQuality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
ProxyConfig lpc = lc.getDefaultProxyConfig();
|
||||
if (lpc != null) {
|
||||
mListener.onRegistrationStateChanged(lc, lpc, lpc.getState(), null);
|
||||
}
|
||||
|
||||
Call call = lc.getCurrentCall();
|
||||
if (mIsInCall && (call != null || lc.getConferenceSize() > 1 || lc.getCallsNb() > 0)) {
|
||||
if (call != null) {
|
||||
startCallQuality();
|
||||
refreshStatusItems(call);
|
||||
}
|
||||
mMenu.setVisibility(View.INVISIBLE);
|
||||
mCallQuality.setVisibility(View.VISIBLE);
|
||||
|
||||
// We are obviously connected
|
||||
if (lc.getDefaultProxyConfig() == null) {
|
||||
mStatusLed.setImageResource(R.drawable.led_disconnected);
|
||||
mStatusText.setText(getString(R.string.no_account));
|
||||
} else {
|
||||
mStatusLed.setImageResource(
|
||||
getStatusIconResource(lc.getDefaultProxyConfig().getState()));
|
||||
mStatusText.setText(getStatusIconText(lc.getDefaultProxyConfig().getState()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mStatusText.setVisibility(View.VISIBLE);
|
||||
mEncryption.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
|
||||
if (mCallQualityUpdater != null) {
|
||||
mRefreshHandler.removeCallbacks(mCallQualityUpdater);
|
||||
mCallQualityUpdater = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshStatusItems(final Call call) {
|
||||
if (call != null) {
|
||||
mVoicemailCount.setVisibility(View.GONE);
|
||||
MediaEncryption mediaEncryption = call.getCurrentParams().getMediaEncryption();
|
||||
|
||||
mEncryption.setVisibility(View.VISIBLE);
|
||||
if (mediaEncryption == MediaEncryption.SRTP
|
||||
|| (mediaEncryption == MediaEncryption.ZRTP
|
||||
&& call.getAuthenticationTokenVerified())
|
||||
|| mediaEncryption == MediaEncryption.DTLS) {
|
||||
mEncryption.setImageResource(R.drawable.security_ok);
|
||||
} else if (mediaEncryption == MediaEncryption.ZRTP
|
||||
&& !call.getAuthenticationTokenVerified()) {
|
||||
mEncryption.setImageResource(R.drawable.security_pending);
|
||||
} else {
|
||||
mEncryption.setImageResource(R.drawable.security_ko);
|
||||
// Do not show the unsecure icon if user doesn't want to do call mEncryption
|
||||
if (LinphonePreferences.instance().getMediaEncryption() == MediaEncryption.None) {
|
||||
mEncryption.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaEncryption == MediaEncryption.ZRTP) {
|
||||
mEncryption.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showZRTPDialog(call);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mEncryption.setOnClickListener(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void showZRTPDialog(final Call call) {
|
||||
if (getActivity() == null) {
|
||||
Log.w("Can't display ZRTP popup, no Activity");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mZrtpDialog == null || !mZrtpDialog.isShowing()) {
|
||||
String token = call.getAuthenticationToken();
|
||||
|
||||
if (token == null) {
|
||||
Log.w("Can't display ZRTP popup, no token !");
|
||||
return;
|
||||
}
|
||||
if (token.length() < 4) {
|
||||
Log.w("Can't display ZRTP popup, token is invalid (" + token + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
mZrtpDialog = new Dialog(getActivity());
|
||||
mZrtpDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
mZrtpDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
mZrtpDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
mZrtpDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
Drawable d =
|
||||
new ColorDrawable(
|
||||
ContextCompat.getColor(getActivity(), R.color.dark_grey_color));
|
||||
d.setAlpha(200);
|
||||
mZrtpDialog.setContentView(R.layout.dialog);
|
||||
mZrtpDialog
|
||||
.getWindow()
|
||||
.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT);
|
||||
mZrtpDialog.getWindow().setBackgroundDrawable(d);
|
||||
String zrtpToRead, zrtpToListen;
|
||||
|
||||
if (call.getDir().equals(Call.Dir.Incoming)) {
|
||||
zrtpToRead = token.substring(0, 2);
|
||||
zrtpToListen = token.substring(2);
|
||||
} else {
|
||||
zrtpToListen = token.substring(0, 2);
|
||||
zrtpToRead = token.substring(2);
|
||||
}
|
||||
|
||||
TextView localSas = mZrtpDialog.findViewById(R.id.zrtp_sas_local);
|
||||
localSas.setText(zrtpToRead.toUpperCase());
|
||||
TextView remoteSas = mZrtpDialog.findViewById(R.id.zrtp_sas_remote);
|
||||
remoteSas.setText(zrtpToListen.toUpperCase());
|
||||
TextView message = mZrtpDialog.findViewById(R.id.dialog_message);
|
||||
message.setVisibility(View.GONE);
|
||||
mZrtpDialog.findViewById(R.id.dialog_zrtp_layout).setVisibility(View.VISIBLE);
|
||||
|
||||
TextView title = mZrtpDialog.findViewById(R.id.dialog_title);
|
||||
title.setText(getString(R.string.zrtp_dialog_title));
|
||||
title.setVisibility(View.VISIBLE);
|
||||
|
||||
Button delete = mZrtpDialog.findViewById(R.id.dialog_delete_button);
|
||||
delete.setText(R.string.deny);
|
||||
Button cancel = mZrtpDialog.findViewById(R.id.dialog_cancel_button);
|
||||
cancel.setVisibility(View.GONE);
|
||||
Button accept = mZrtpDialog.findViewById(R.id.dialog_ok_button);
|
||||
accept.setVisibility(View.VISIBLE);
|
||||
accept.setText(R.string.accept);
|
||||
|
||||
ImageView icon = mZrtpDialog.findViewById(R.id.dialog_icon);
|
||||
icon.setVisibility(View.VISIBLE);
|
||||
icon.setImageResource(R.drawable.security_2_indicator);
|
||||
|
||||
delete.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (call != null) {
|
||||
call.setAuthenticationTokenVerified(false);
|
||||
if (mEncryption != null) {
|
||||
mEncryption.setImageResource(R.drawable.security_ko);
|
||||
}
|
||||
}
|
||||
mZrtpDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
accept.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
call.setAuthenticationTokenVerified(true);
|
||||
if (mEncryption != null) {
|
||||
mEncryption.setImageResource(R.drawable.security_ok);
|
||||
}
|
||||
mZrtpDialog.dismiss();
|
||||
}
|
||||
});
|
||||
mZrtpDialog.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
185
app/src/main/java/org/linphone/history/HistoryAdapter.java
Normal file
185
app/src/main/java/org/linphone/history/HistoryAdapter.java
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
package org.linphone.history;
|
||||
|
||||
/*
|
||||
HistoryAdapter.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.CallLog;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.utils.SelectableAdapter;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class HistoryAdapter extends SelectableAdapter<HistoryViewHolder> {
|
||||
private final List<CallLog> mLogs;
|
||||
private final Context mContext;
|
||||
private final HistoryViewHolder.ClickListener mClickListener;
|
||||
|
||||
public HistoryAdapter(
|
||||
Context aContext,
|
||||
List<CallLog> logs,
|
||||
HistoryViewHolder.ClickListener listener,
|
||||
SelectableHelper helper) {
|
||||
super(helper);
|
||||
mLogs = logs;
|
||||
mContext = aContext;
|
||||
mClickListener = listener;
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mLogs.get(position);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HistoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.history_cell, parent, false);
|
||||
return new HistoryViewHolder(v, mClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final HistoryViewHolder holder, final int position) {
|
||||
final CallLog log = mLogs.get(position);
|
||||
long timestamp = log.getStartDate() * 1000;
|
||||
Address address;
|
||||
|
||||
holder.contact.setSelected(true); // For automated horizontal scrolling of long texts
|
||||
Calendar logTime = Calendar.getInstance();
|
||||
logTime.setTimeInMillis(timestamp);
|
||||
holder.separatorText.setText(timestampToHumanDate(logTime));
|
||||
holder.select.setVisibility(isEditionEnabled() ? View.VISIBLE : View.GONE);
|
||||
holder.select.setChecked(isSelected(position));
|
||||
|
||||
if (position > 0) {
|
||||
CallLog previousLog = mLogs.get(position - 1);
|
||||
long previousTimestamp = previousLog.getStartDate() * 1000;
|
||||
Calendar previousLogTime = Calendar.getInstance();
|
||||
previousLogTime.setTimeInMillis(previousTimestamp);
|
||||
|
||||
if (isSameDay(previousLogTime, logTime)) {
|
||||
holder.separator.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.separator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
holder.separator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (log.getDir() == Call.Dir.Incoming) {
|
||||
address = log.getFromAddress();
|
||||
if (log.getStatus() == Call.Status.Missed) {
|
||||
holder.callDirection.setImageResource(R.drawable.call_status_missed);
|
||||
} else {
|
||||
holder.callDirection.setImageResource(R.drawable.call_status_incoming);
|
||||
}
|
||||
} else {
|
||||
address = log.getToAddress();
|
||||
holder.callDirection.setImageResource(R.drawable.call_status_outgoing);
|
||||
}
|
||||
|
||||
LinphoneContact c = ContactsManager.getInstance().findContactFromAddress(address);
|
||||
String displayName = null;
|
||||
final String sipUri = (address != null) ? address.asString() : "";
|
||||
|
||||
if (c != null) {
|
||||
displayName = c.getFullName();
|
||||
}
|
||||
if (displayName == null) {
|
||||
holder.contact.setText(LinphoneUtils.getAddressDisplayName(sipUri));
|
||||
} else {
|
||||
holder.contact.setText(displayName);
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
ContactAvatar.displayAvatar(c, holder.avatarLayout);
|
||||
} else {
|
||||
ContactAvatar.displayAvatar(holder.contact.getText().toString(), holder.avatarLayout);
|
||||
}
|
||||
|
||||
holder.detail.setVisibility(isEditionEnabled() ? View.INVISIBLE : View.VISIBLE);
|
||||
holder.detail.setOnClickListener(
|
||||
!isEditionEnabled()
|
||||
? new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().displayHistoryDetail(sipUri, log);
|
||||
}
|
||||
}
|
||||
}
|
||||
: null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mLogs.size();
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private String timestampToHumanDate(Calendar cal) {
|
||||
SimpleDateFormat dateFormat;
|
||||
if (isToday(cal)) {
|
||||
return mContext.getString(R.string.today);
|
||||
} else if (isYesterday(cal)) {
|
||||
return mContext.getString(R.string.yesterday);
|
||||
} else {
|
||||
dateFormat =
|
||||
new SimpleDateFormat(
|
||||
mContext.getResources().getString(R.string.history_date_format));
|
||||
}
|
||||
|
||||
return dateFormat.format(cal.getTime());
|
||||
}
|
||||
|
||||
private boolean isSameDay(Calendar cal1, Calendar cal2) {
|
||||
if (cal1 == null || cal2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA)
|
||||
&& cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
|
||||
&& cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
|
||||
}
|
||||
|
||||
private boolean isToday(Calendar cal) {
|
||||
return isSameDay(cal, Calendar.getInstance());
|
||||
}
|
||||
|
||||
private boolean isYesterday(Calendar cal) {
|
||||
Calendar yesterday = Calendar.getInstance();
|
||||
yesterday.roll(Calendar.DAY_OF_MONTH, -1);
|
||||
return isSameDay(cal, yesterday);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
package org.linphone.history;
|
||||
|
||||
/*
|
||||
HistoryDetailFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.LinphoneContact;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.CallLog;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.ChatRoomBackend;
|
||||
import org.linphone.core.ChatRoomListenerStub;
|
||||
import org.linphone.core.ChatRoomParams;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.Factory;
|
||||
import org.linphone.core.FriendCapability;
|
||||
import org.linphone.core.ProxyConfig;
|
||||
import org.linphone.core.tools.Log;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.settings.LinphonePreferences;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
import org.linphone.views.ContactAvatar;
|
||||
|
||||
public class HistoryDetailFragment extends Fragment implements OnClickListener {
|
||||
private ImageView mDialBack, mChat, mAddToContacts, mGoToContact, mBack;
|
||||
private View mView;
|
||||
private TextView mContactName, mContactAddress;
|
||||
private String mSipUri, mDisplayName;
|
||||
private RelativeLayout mWaitLayout, mAvatarLayout, mChatSecured;
|
||||
private LinphoneContact mContact;
|
||||
private ChatRoom mChatRoom;
|
||||
private ChatRoomListenerStub mChatRoomCreationListener;
|
||||
private ListView mLogsList;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mSipUri = getArguments().getString("SipUri");
|
||||
mDisplayName = getArguments().getString("DisplayName");
|
||||
|
||||
mView = inflater.inflate(R.layout.history_detail, container, false);
|
||||
|
||||
mWaitLayout = mView.findViewById(R.id.waitScreen);
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
|
||||
mDialBack = mView.findViewById(R.id.call);
|
||||
mDialBack.setOnClickListener(this);
|
||||
|
||||
mBack = mView.findViewById(R.id.back);
|
||||
if (getResources().getBoolean(R.bool.isTablet)) {
|
||||
mBack.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mBack.setOnClickListener(this);
|
||||
}
|
||||
|
||||
mChat = mView.findViewById(R.id.chat);
|
||||
mChat.setOnClickListener(this);
|
||||
|
||||
mChatSecured = mView.findViewById(R.id.chat_secured);
|
||||
mChatSecured.setOnClickListener(this);
|
||||
|
||||
if (getResources().getBoolean(R.bool.disable_chat)) {
|
||||
mChat.setVisibility(View.GONE);
|
||||
mChatSecured.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mAddToContacts = mView.findViewById(R.id.add_contact);
|
||||
mAddToContacts.setOnClickListener(this);
|
||||
|
||||
mGoToContact = mView.findViewById(R.id.goto_contact);
|
||||
mGoToContact.setOnClickListener(this);
|
||||
|
||||
mAvatarLayout = mView.findViewById(R.id.avatar_layout);
|
||||
mContactName = mView.findViewById(R.id.contact_name);
|
||||
mContactAddress = mView.findViewById(R.id.contact_address);
|
||||
|
||||
mChatRoomCreationListener =
|
||||
new ChatRoomListenerStub() {
|
||||
@Override
|
||||
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
|
||||
if (newState == ChatRoom.State.Created) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
cr.getLocalAddress().asStringUriOnly(),
|
||||
cr.getPeerAddress().asStringUriOnly(),
|
||||
null);
|
||||
} else if (newState == ChatRoom.State.CreationFailed) {
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
LinphoneActivity.instance().displayChatRoomError();
|
||||
Log.e(
|
||||
"Group mChat room for address "
|
||||
+ cr.getPeerAddress()
|
||||
+ " has failed !");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mLogsList = mView.findViewById(R.id.logs_list);
|
||||
displayHistory();
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
||||
private void displayHistory() {
|
||||
Address lAddress = Factory.instance().createAddress(mSipUri);
|
||||
mChatSecured.setVisibility(View.GONE);
|
||||
|
||||
if (lAddress != null) {
|
||||
CallLog[] logs =
|
||||
LinphoneManager.getLcIfManagerNotDestroyedOrNull()
|
||||
.getCallHistoryForAddress(lAddress);
|
||||
List<CallLog> logsList = Arrays.asList(logs);
|
||||
mLogsList.setAdapter(
|
||||
new HistoryLogAdapter(
|
||||
LinphoneActivity.instance(), R.layout.history_detail_cell, logsList));
|
||||
|
||||
mContactAddress.setText(LinphoneUtils.getDisplayableAddress(lAddress));
|
||||
mContact = ContactsManager.getInstance().findContactFromAddress(lAddress);
|
||||
|
||||
if (mContact != null) {
|
||||
mContactName.setText(mContact.getFullName());
|
||||
ContactAvatar.displayAvatar(mContact, mAvatarLayout);
|
||||
mAddToContacts.setVisibility(View.GONE);
|
||||
mGoToContact.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!getResources().getBoolean(R.bool.disable_chat)
|
||||
&& mContact.hasPresenceModelForUriOrTelCapability(
|
||||
mSipUri, FriendCapability.LimeX3Dh)) {
|
||||
mChatSecured.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
mContactName.setText(
|
||||
mDisplayName == null
|
||||
? LinphoneUtils.getAddressDisplayName(mSipUri)
|
||||
: mDisplayName);
|
||||
ContactAvatar.displayAvatar(
|
||||
LinphoneUtils.getAddressDisplayName(lAddress), mAvatarLayout);
|
||||
mAddToContacts.setVisibility(View.VISIBLE);
|
||||
mGoToContact.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
mContactAddress.setText(mSipUri);
|
||||
mContactName.setText(
|
||||
mDisplayName == null
|
||||
? LinphoneUtils.getAddressDisplayName(mSipUri)
|
||||
: mDisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.removeListener(mChatRoomCreationListener);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
public void changeDisplayedHistory(String sipUri, String displayName) {
|
||||
if (displayName == null) {
|
||||
displayName = LinphoneUtils.getUsernameFromAddress(sipUri);
|
||||
}
|
||||
|
||||
mSipUri = sipUri;
|
||||
mDisplayName = displayName;
|
||||
displayHistory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.HISTORY_DETAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
|
||||
if (id == R.id.back) {
|
||||
getFragmentManager().popBackStackImmediate();
|
||||
}
|
||||
if (id == R.id.call) {
|
||||
LinphoneActivity.instance().setAddresGoToDialerAndCall(mSipUri, mDisplayName);
|
||||
} else if (id == R.id.chat || id == R.id.chat_secured) {
|
||||
boolean isSecured = id == R.id.chat_secured;
|
||||
Core lc = LinphoneManager.getLc();
|
||||
Address participant = Factory.instance().createAddress(mSipUri);
|
||||
ChatRoom room =
|
||||
lc.findOneToOneChatRoom(
|
||||
lc.getDefaultProxyConfig().getContact(), participant, isSecured);
|
||||
if (room != null) {
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
room.getLocalAddress().asStringUriOnly(),
|
||||
room.getPeerAddress().asStringUriOnly(),
|
||||
null);
|
||||
} else {
|
||||
ProxyConfig lpc = lc.getDefaultProxyConfig();
|
||||
if (lpc != null
|
||||
&& lpc.getConferenceFactoryUri() != null
|
||||
&& (isSecured
|
||||
|| !LinphonePreferences.instance().useBasicChatRoomFor1To1())) {
|
||||
mWaitLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
ChatRoomParams params = lc.createDefaultChatRoomParams();
|
||||
params.enableEncryption(isSecured);
|
||||
params.enableGroup(false);
|
||||
// We don't want a basic chat room
|
||||
params.setBackend(ChatRoomBackend.FlexisipChat);
|
||||
|
||||
Address participants[] = new Address[1];
|
||||
participants[0] = participant;
|
||||
|
||||
mChatRoom =
|
||||
lc.createChatRoom(
|
||||
params,
|
||||
getString(R.string.dummy_group_chat_subject),
|
||||
participants);
|
||||
if (mChatRoom != null) {
|
||||
mChatRoom.addListener(mChatRoomCreationListener);
|
||||
} else {
|
||||
Log.w("[History Detail Fragment] createChatRoom returned null...");
|
||||
mWaitLayout.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
room = lc.getChatRoom(participant);
|
||||
LinphoneActivity.instance()
|
||||
.goToChat(
|
||||
room.getLocalAddress().asStringUriOnly(),
|
||||
room.getPeerAddress().asStringUriOnly(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
} else if (id == R.id.add_contact) {
|
||||
Address addr = Factory.instance().createAddress(mSipUri);
|
||||
if (addr != null) {
|
||||
String address =
|
||||
"sip:" + addr.getUsername() + "@" + addr.getDomain(); // Clean gruu param
|
||||
if (addr.getDisplayName() != null) {
|
||||
LinphoneActivity.instance()
|
||||
.displayContactsForEdition(address, addr.getDisplayName());
|
||||
} else {
|
||||
LinphoneActivity.instance().displayContactsForEdition(address);
|
||||
}
|
||||
}
|
||||
} else if (id == R.id.goto_contact) {
|
||||
LinphoneActivity.instance().displayContact(mContact, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package org.linphone.fragments;
|
||||
package org.linphone.history;
|
||||
|
||||
/*
|
||||
HistoryListFragment.java
|
||||
HistoryFragment.java
|
||||
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
|
|
@ -22,9 +22,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
|
@ -33,73 +30,78 @@ import android.widget.AdapterView;
|
|||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.activities.LinphoneActivity;
|
||||
import org.linphone.call.CallHistoryAdapter;
|
||||
import org.linphone.contacts.ContactsManager;
|
||||
import org.linphone.contacts.ContactsUpdatedListener;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.CallLog;
|
||||
import org.linphone.ui.SelectableHelper;
|
||||
import org.linphone.fragments.FragmentsAvailable;
|
||||
import org.linphone.utils.SelectableHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class HistoryListFragment extends Fragment implements OnClickListener, OnItemClickListener, CallHistoryAdapter.ViewHolder.ClickListener, ContactsUpdatedListener, SelectableHelper.DeleteListener {
|
||||
private RecyclerView historyList;
|
||||
private TextView noCallHistory, noMissedCallHistory;
|
||||
private ImageView missedCalls, allCalls, edit;
|
||||
private View allCallsSelected, missedCallsSelected;
|
||||
public class HistoryFragment extends Fragment
|
||||
implements OnClickListener,
|
||||
OnItemClickListener,
|
||||
HistoryViewHolder.ClickListener,
|
||||
ContactsUpdatedListener,
|
||||
SelectableHelper.DeleteListener {
|
||||
private RecyclerView mHistoryList;
|
||||
private TextView mNoCallHistory, mNoMissedCallHistory;
|
||||
private ImageView mMissedCalls, mAllCalls;
|
||||
private View mAllCallsSelected, mMissedCallsSelected;
|
||||
private boolean mOnlyDisplayMissedCalls;
|
||||
private List<CallLog> mLogs;
|
||||
private CallHistoryAdapter mHistoryAdapter;
|
||||
private HistoryAdapter mHistoryAdapter;
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
private Context mContext;
|
||||
private SelectableHelper mSelectionHelper;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.history, container, false);
|
||||
mContext = getActivity().getApplicationContext();
|
||||
mSelectionHelper = new SelectableHelper(view, this);
|
||||
|
||||
noCallHistory = view.findViewById(R.id.no_call_history);
|
||||
noMissedCallHistory = view.findViewById(R.id.no_missed_call_history);
|
||||
mNoCallHistory = view.findViewById(R.id.no_call_history);
|
||||
mNoMissedCallHistory = view.findViewById(R.id.no_missed_call_history);
|
||||
|
||||
historyList = view.findViewById(R.id.history_list);
|
||||
mHistoryList = view.findViewById(R.id.history_list);
|
||||
|
||||
mLayoutManager = new LinearLayoutManager(mContext);
|
||||
historyList.setLayoutManager(mLayoutManager);
|
||||
//Divider between items
|
||||
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(historyList.getContext(),
|
||||
mLayoutManager.getOrientation());
|
||||
mHistoryList.setLayoutManager(mLayoutManager);
|
||||
// Divider between items
|
||||
DividerItemDecoration dividerItemDecoration =
|
||||
new DividerItemDecoration(
|
||||
mHistoryList.getContext(), mLayoutManager.getOrientation());
|
||||
dividerItemDecoration.setDrawable(mContext.getResources().getDrawable(R.drawable.divider));
|
||||
historyList.addItemDecoration(dividerItemDecoration);
|
||||
mHistoryList.addItemDecoration(dividerItemDecoration);
|
||||
|
||||
allCalls = view.findViewById(R.id.all_calls);
|
||||
allCalls.setOnClickListener(this);
|
||||
mAllCalls = view.findViewById(R.id.all_calls);
|
||||
mAllCalls.setOnClickListener(this);
|
||||
|
||||
allCallsSelected = view.findViewById(R.id.all_calls_select);
|
||||
mAllCallsSelected = view.findViewById(R.id.all_calls_select);
|
||||
|
||||
missedCalls = view.findViewById(R.id.missed_calls);
|
||||
missedCalls.setOnClickListener(this);
|
||||
mMissedCalls = view.findViewById(R.id.missed_calls);
|
||||
mMissedCalls.setOnClickListener(this);
|
||||
|
||||
missedCallsSelected = view.findViewById(R.id.missed_calls_select);
|
||||
mMissedCallsSelected = view.findViewById(R.id.missed_calls_select);
|
||||
|
||||
allCalls.setEnabled(false);
|
||||
mAllCalls.setEnabled(false);
|
||||
mOnlyDisplayMissedCalls = false;
|
||||
|
||||
edit = view.findViewById(R.id.edit);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
private void refresh() {
|
||||
mLogs = Arrays.asList(LinphoneManager.getLc().getCallLogs());
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +122,7 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
|
|||
|
||||
private void removeNotMissedCallsFromLogs() {
|
||||
if (mOnlyDisplayMissedCalls) {
|
||||
List<CallLog> missedCalls = new ArrayList<CallLog>();
|
||||
List<CallLog> missedCalls = new ArrayList<>();
|
||||
for (CallLog log : mLogs) {
|
||||
if (log.getStatus() == Call.Status.Missed) {
|
||||
missedCalls.add(log);
|
||||
|
|
@ -130,57 +132,57 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
|
|||
}
|
||||
}
|
||||
|
||||
private boolean hideHistoryListAndDisplayMessageIfEmpty() {
|
||||
private void hideHistoryListAndDisplayMessageIfEmpty() {
|
||||
removeNotMissedCallsFromLogs();
|
||||
mNoCallHistory.setVisibility(View.GONE);
|
||||
mNoMissedCallHistory.setVisibility(View.GONE);
|
||||
|
||||
if (mLogs.isEmpty()) {
|
||||
if (mOnlyDisplayMissedCalls) {
|
||||
noMissedCallHistory.setVisibility(View.VISIBLE);
|
||||
mNoMissedCallHistory.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
noCallHistory.setVisibility(View.VISIBLE);
|
||||
mNoCallHistory.setVisibility(View.VISIBLE);
|
||||
}
|
||||
historyList.setVisibility(View.GONE);
|
||||
edit.setEnabled(false);
|
||||
return true;
|
||||
mHistoryList.setVisibility(View.GONE);
|
||||
} else {
|
||||
noCallHistory.setVisibility(View.GONE);
|
||||
noMissedCallHistory.setVisibility(View.GONE);
|
||||
historyList.setVisibility(View.VISIBLE);
|
||||
edit.setEnabled(true);
|
||||
return false;
|
||||
mNoCallHistory.setVisibility(View.GONE);
|
||||
mNoMissedCallHistory.setVisibility(View.GONE);
|
||||
mHistoryList.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
ContactsManager.addContactsListener(this);
|
||||
ContactsManager.getInstance().addContactsListener(this);
|
||||
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.HISTORY_LIST);
|
||||
LinphoneActivity.instance().hideTabBar(false);
|
||||
LinphoneActivity.instance().displayMissedCalls(0);
|
||||
}
|
||||
|
||||
mLogs = Arrays.asList(LinphoneManager.getLc().getCallLogs());
|
||||
if (!hideHistoryListAndDisplayMessageIfEmpty()) {
|
||||
mHistoryAdapter = new CallHistoryAdapter(getActivity().getApplicationContext(), mLogs, this, mSelectionHelper);
|
||||
historyList.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setDialogMessage(R.string.chat_room_delete_dialog);
|
||||
}
|
||||
hideHistoryListAndDisplayMessageIfEmpty();
|
||||
mHistoryAdapter =
|
||||
new HistoryAdapter(
|
||||
getActivity().getApplicationContext(), mLogs, this, mSelectionHelper);
|
||||
mHistoryList.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setDialogMessage(R.string.call_log_delete_dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
ContactsManager.removeContactsListener(this);
|
||||
ContactsManager.getInstance().removeContactsListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactsUpdated() {
|
||||
if (!LinphoneActivity.isInstanciated() || LinphoneActivity.instance().getCurrentFragment() != FragmentsAvailable.HISTORY_LIST)
|
||||
return;
|
||||
CallHistoryAdapter adapter = (CallHistoryAdapter) historyList.getAdapter();
|
||||
if (!LinphoneActivity.isInstanciated()
|
||||
|| LinphoneActivity.instance().getCurrentFragment()
|
||||
!= FragmentsAvailable.HISTORY_LIST) return;
|
||||
HistoryAdapter adapter = (HistoryAdapter) mHistoryList.getAdapter();
|
||||
if (adapter != null) {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
|
@ -191,28 +193,25 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
|
|||
int id = v.getId();
|
||||
|
||||
if (id == R.id.all_calls) {
|
||||
allCalls.setEnabled(false);
|
||||
allCallsSelected.setVisibility(View.VISIBLE);
|
||||
missedCallsSelected.setVisibility(View.INVISIBLE);
|
||||
missedCalls.setEnabled(true);
|
||||
mAllCalls.setEnabled(false);
|
||||
mAllCallsSelected.setVisibility(View.VISIBLE);
|
||||
mMissedCallsSelected.setVisibility(View.INVISIBLE);
|
||||
mMissedCalls.setEnabled(true);
|
||||
mOnlyDisplayMissedCalls = false;
|
||||
refresh();
|
||||
}
|
||||
if (id == R.id.missed_calls) {
|
||||
allCalls.setEnabled(true);
|
||||
allCallsSelected.setVisibility(View.INVISIBLE);
|
||||
missedCallsSelected.setVisibility(View.VISIBLE);
|
||||
missedCalls.setEnabled(false);
|
||||
mAllCalls.setEnabled(true);
|
||||
mAllCallsSelected.setVisibility(View.INVISIBLE);
|
||||
mMissedCallsSelected.setVisibility(View.VISIBLE);
|
||||
mMissedCalls.setEnabled(false);
|
||||
mOnlyDisplayMissedCalls = true;
|
||||
}
|
||||
if (!hideHistoryListAndDisplayMessageIfEmpty()) {
|
||||
// historyList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
|
||||
mHistoryAdapter = new CallHistoryAdapter(mContext, mLogs, this, mSelectionHelper);
|
||||
historyList.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setDialogMessage(R.string.chat_room_delete_dialog);
|
||||
}
|
||||
|
||||
hideHistoryListAndDisplayMessageIfEmpty();
|
||||
mHistoryAdapter = new HistoryAdapter(mContext, mLogs, this, mSelectionHelper);
|
||||
mHistoryList.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setAdapter(mHistoryAdapter);
|
||||
mSelectionHelper.setDialogMessage(R.string.chat_room_delete_dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -247,7 +246,9 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
|
|||
} else {
|
||||
address = log.getToAddress();
|
||||
}
|
||||
LinphoneActivity.instance().setAddresGoToDialerAndCall(address.asStringUriOnly(), address.getDisplayName(), null);
|
||||
LinphoneActivity.instance()
|
||||
.setAddresGoToDialerAndCall(
|
||||
address.asStringUriOnly(), address.getDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -260,4 +261,4 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
|
|||
mHistoryAdapter.toggleSelection(position);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
115
app/src/main/java/org/linphone/history/HistoryLogAdapter.java
Normal file
115
app/src/main/java/org/linphone/history/HistoryLogAdapter.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package org.linphone.history;
|
||||
|
||||
/*
|
||||
HistoryLogAdapter.java
|
||||
Copyright (C) 2019 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.CallLog;
|
||||
import org.linphone.utils.LinphoneUtils;
|
||||
|
||||
class HistoryLogAdapter extends ArrayAdapter<CallLog> {
|
||||
private Context mContext;
|
||||
private final List<CallLog> mItems;
|
||||
private final int mResource;
|
||||
|
||||
HistoryLogAdapter(@NonNull Context context, int resource, @NonNull List<CallLog> objects) {
|
||||
super(context, resource, objects);
|
||||
mContext = context;
|
||||
mResource = resource;
|
||||
mItems = objects;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CallLog getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private String secondsToDisplayableString(int secs) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(0, 0, 0, 0, 0, secs);
|
||||
return dateFormat.format(cal.getTime());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
LayoutInflater inflater =
|
||||
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
View rowView = inflater.inflate(mResource, parent, false);
|
||||
CallLog callLog = getItem(position);
|
||||
|
||||
String callTime = secondsToDisplayableString(callLog.getDuration());
|
||||
String callDate = String.valueOf(callLog.getStartDate());
|
||||
String status;
|
||||
if (callLog.getDir() == Call.Dir.Outgoing) {
|
||||
status = mContext.getString(R.string.outgoing);
|
||||
} else {
|
||||
if (callLog.getStatus() == Call.Status.Missed) {
|
||||
status = mContext.getString(R.string.missed);
|
||||
} else {
|
||||
status = mContext.getString(R.string.incoming);
|
||||
}
|
||||
}
|
||||
|
||||
TextView date = rowView.findViewById(R.id.date);
|
||||
TextView time = rowView.findViewById(R.id.time);
|
||||
ImageView callDirection = rowView.findViewById(R.id.direction);
|
||||
|
||||
if (status.equals(mContext.getResources().getString(R.string.missed))) {
|
||||
callDirection.setImageResource(R.drawable.call_missed);
|
||||
} else if (status.equals(mContext.getResources().getString(R.string.incoming))) {
|
||||
callDirection.setImageResource(R.drawable.call_incoming);
|
||||
} else if (status.equals(mContext.getResources().getString(R.string.outgoing))) {
|
||||
callDirection.setImageResource(R.drawable.call_outgoing);
|
||||
}
|
||||
|
||||
time.setText(callTime == null ? "" : callTime);
|
||||
Long longDate = Long.parseLong(callDate);
|
||||
date.setText(
|
||||
LinphoneUtils.timestampToHumanDate(
|
||||
mContext,
|
||||
longDate,
|
||||
mContext.getString(R.string.history_detail_date_format)));
|
||||
|
||||
return rowView;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package org.linphone.history;
|
||||
|
||||
/*
|
||||
HistoryViewHolder.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.linphone.R;
|
||||
|
||||
public class HistoryViewHolder extends RecyclerView.ViewHolder
|
||||
implements View.OnClickListener, View.OnLongClickListener {
|
||||
public final TextView contact;
|
||||
public final ImageView detail;
|
||||
public final CheckBox select;
|
||||
public final ImageView callDirection;
|
||||
public final RelativeLayout avatarLayout;
|
||||
public final LinearLayout separator;
|
||||
public final TextView separatorText;
|
||||
|
||||
private final ClickListener mListener;
|
||||
|
||||
public HistoryViewHolder(View view, ClickListener listener) {
|
||||
super(view);
|
||||
contact = view.findViewById(R.id.sip_uri);
|
||||
detail = view.findViewById(R.id.detail);
|
||||
select = view.findViewById(R.id.delete);
|
||||
callDirection = view.findViewById(R.id.icon);
|
||||
avatarLayout = view.findViewById(R.id.avatar_layout);
|
||||
separator = view.findViewById(R.id.separator);
|
||||
separatorText = view.findViewById(R.id.separator_text);
|
||||
mListener = listener;
|
||||
view.setOnClickListener(this);
|
||||
view.setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mListener != null) {
|
||||
mListener.onItemClicked(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (mListener != null) {
|
||||
return mListener.onItemLongClicked(getAdapterPosition());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public interface ClickListener {
|
||||
void onItemClicked(int position);
|
||||
|
||||
boolean onItemLongClicked(int position);
|
||||
}
|
||||
}
|
||||
106
app/src/main/java/org/linphone/notifications/Notifiable.java
Normal file
106
app/src/main/java/org/linphone/notifications/Notifiable.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package org.linphone.notifications;
|
||||
|
||||
/*
|
||||
Notifiable.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Notifiable {
|
||||
private final int mNotificationId;
|
||||
private List<NotifiableMessage> mMessages;
|
||||
private boolean mIsGroup;
|
||||
private String mGroupTitle;
|
||||
private String mLocalIdentity;
|
||||
private String mMyself;
|
||||
private int iconId;
|
||||
private int textId;
|
||||
|
||||
public Notifiable(int id) {
|
||||
mNotificationId = id;
|
||||
mMessages = new ArrayList<>();
|
||||
mIsGroup = false;
|
||||
iconId = 0;
|
||||
textId = 0;
|
||||
}
|
||||
|
||||
public int getNotificationId() {
|
||||
return mNotificationId;
|
||||
}
|
||||
|
||||
public void resetMessages() {
|
||||
mMessages = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void addMessage(NotifiableMessage notifMessage) {
|
||||
mMessages.add(notifMessage);
|
||||
}
|
||||
|
||||
public List<NotifiableMessage> getMessages() {
|
||||
return mMessages;
|
||||
}
|
||||
|
||||
public boolean isGroup() {
|
||||
return mIsGroup;
|
||||
}
|
||||
|
||||
public void setIsGroup(boolean isGroup) {
|
||||
mIsGroup = isGroup;
|
||||
}
|
||||
|
||||
public String getGroupTitle() {
|
||||
return mGroupTitle;
|
||||
}
|
||||
|
||||
public void setGroupTitle(String title) {
|
||||
mGroupTitle = title;
|
||||
}
|
||||
|
||||
public String getMyself() {
|
||||
return mMyself;
|
||||
}
|
||||
|
||||
public void setMyself(String myself) {
|
||||
mMyself = myself;
|
||||
}
|
||||
|
||||
public String getLocalIdentity() {
|
||||
return mLocalIdentity;
|
||||
}
|
||||
|
||||
public void setLocalIdentity(String localIdentity) {
|
||||
mLocalIdentity = localIdentity;
|
||||
}
|
||||
|
||||
public int getIconResourceId() {
|
||||
return iconId;
|
||||
}
|
||||
|
||||
public void setIconResourceId(int id) {
|
||||
iconId = id;
|
||||
}
|
||||
|
||||
public int getTextResourceId() {
|
||||
return textId;
|
||||
}
|
||||
|
||||
public void setTextResourceId(int id) {
|
||||
textId = id;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package org.linphone.notifications;
|
||||
|
||||
/*
|
||||
NotifiableMessage.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
public class NotifiableMessage {
|
||||
private final String mMessage;
|
||||
private final String mSender;
|
||||
private final long mTime;
|
||||
private Bitmap mSenderBitmap;
|
||||
private final Uri mFilePath;
|
||||
private final String mFileMime;
|
||||
|
||||
public NotifiableMessage(
|
||||
String message, String sender, long time, Uri filePath, String fileMime) {
|
||||
mMessage = message;
|
||||
mSender = sender;
|
||||
mTime = time;
|
||||
mFilePath = filePath;
|
||||
mFileMime = fileMime;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return mSender;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return mTime;
|
||||
}
|
||||
|
||||
public Bitmap getSenderBitmap() {
|
||||
return mSenderBitmap;
|
||||
}
|
||||
|
||||
public void setSenderBitmap(Bitmap bm) {
|
||||
mSenderBitmap = bm;
|
||||
}
|
||||
|
||||
public Uri getFilePath() {
|
||||
return mFilePath;
|
||||
}
|
||||
|
||||
public String getFileMime() {
|
||||
return mFileMime;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
package org.linphone.notifications;
|
||||
|
||||
/*
|
||||
NotificationBroadcastReceiver.java
|
||||
Copyright (C) 2018 Belledonne Communications, Grenoble, France
|
||||
|
||||
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 2
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.RemoteInput;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import org.linphone.LinphoneActivity;
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.LinphoneService;
|
||||
import org.linphone.R;
|
||||
import org.linphone.compatibility.Compatibility;
|
||||
import org.linphone.core.Address;
|
||||
import org.linphone.core.Call;
|
||||
import org.linphone.core.ChatMessage;
|
||||
import org.linphone.core.ChatMessageListenerStub;
|
||||
import org.linphone.core.ChatRoom;
|
||||
import org.linphone.core.Core;
|
||||
import org.linphone.core.tools.Log;
|
||||
|
||||
public class NotificationBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
final int notifId = intent.getIntExtra(Compatibility.INTENT_NOTIF_ID, 0);
|
||||
final String localyIdentity = intent.getStringExtra(Compatibility.INTENT_LOCAL_IDENTITY);
|
||||
|
||||
if (intent.getAction().equals(Compatibility.INTENT_REPLY_NOTIF_ACTION)
|
||||
|| intent.getAction().equals(Compatibility.INTENT_MARK_AS_READ_ACTION)) {
|
||||
String remoteSipAddr =
|
||||
LinphoneService.instance()
|
||||
.getNotificationManager()
|
||||
.getSipUriForNotificationId(notifId);
|
||||
|
||||
Core core = LinphoneManager.getLc();
|
||||
if (core == null) {
|
||||
Log.e("[Notification Broadcast Receiver] Couldn't get Core instance");
|
||||
onError(context, notifId);
|
||||
return;
|
||||
}
|
||||
|
||||
Address remoteAddr = core.interpretUrl(remoteSipAddr);
|
||||
if (remoteAddr == null) {
|
||||
Log.e(
|
||||
"[Notification Broadcast Receiver] Couldn't interpret remote address "
|
||||
+ remoteSipAddr);
|
||||
onError(context, notifId);
|
||||
return;
|
||||
}
|
||||
|
||||
Address localAddr = core.interpretUrl(localyIdentity);
|
||||
if (localAddr == null) {
|
||||
Log.e(
|
||||
"[Notification Broadcast Receiver] Couldn't interpret local address "
|
||||
+ localyIdentity);
|
||||
onError(context, notifId);
|
||||
return;
|
||||
}
|
||||
|
||||
ChatRoom room = core.getChatRoom(remoteAddr, localAddr);
|
||||
if (room == null) {
|
||||
Log.e(
|
||||
"[Notification Broadcast Receiver] Couldn't find chat room for remote address "
|
||||
+ remoteSipAddr
|
||||
+ " and local address "
|
||||
+ localyIdentity);
|
||||
onError(context, notifId);
|
||||
return;
|
||||
}
|
||||
|
||||
room.markAsRead();
|
||||
if (LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance()
|
||||
.displayMissedChats(LinphoneManager.getInstance().getUnreadMessageCount());
|
||||
}
|
||||
|
||||
if (intent.getAction().equals(Compatibility.INTENT_REPLY_NOTIF_ACTION)) {
|
||||
final String reply = getMessageText(intent).toString();
|
||||
if (reply == null) {
|
||||
Log.e("[Notification Broadcast Receiver] Couldn't get reply text");
|
||||
onError(context, notifId);
|
||||
return;
|
||||
}
|
||||
|
||||
ChatMessage msg = room.createMessage(reply);
|
||||
msg.send();
|
||||
msg.addListener(
|
||||
new ChatMessageListenerStub() {
|
||||
@Override
|
||||
public void onMsgStateChanged(
|
||||
ChatMessage msg, ChatMessage.State state) {
|
||||
if (state == ChatMessage.State.Delivered) {
|
||||
Notification replied =
|
||||
Compatibility.createRepliedNotification(context, reply);
|
||||
LinphoneService.instance()
|
||||
.getNotificationManager()
|
||||
.sendNotification(notifId, replied);
|
||||
} else if (state == ChatMessage.State.NotDelivered) {
|
||||
Log.e(
|
||||
"[Notification Broadcast Receiver] Couldn't send reply, message is not delivered");
|
||||
onError(context, notifId);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LinphoneService.instance().getNotificationManager().dismissNotification(notifId);
|
||||
}
|
||||
} else if (intent.getAction().equals(Compatibility.INTENT_ANSWER_CALL_NOTIF_ACTION)
|
||||
|| intent.getAction().equals(Compatibility.INTENT_HANGUP_CALL_NOTIF_ACTION)) {
|
||||
String remoteAddr =
|
||||
LinphoneService.instance()
|
||||
.getNotificationManager()
|
||||
.getSipUriForCallNotificationId(notifId);
|
||||
|
||||
Core core = LinphoneManager.getLc();
|
||||
if (core == null) {
|
||||
Log.e("[Notification Broadcast Receiver] Couldn't get Core instance");
|
||||
return;
|
||||
}
|
||||
Call call = core.findCallFromUri(remoteAddr);
|
||||
if (call == null) {
|
||||
Log.e(
|
||||
"[Notification Broadcast Receiver] Couldn't find call from remote address "
|
||||
+ remoteAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent.getAction().equals(Compatibility.INTENT_ANSWER_CALL_NOTIF_ACTION)) {
|
||||
call.accept();
|
||||
} else {
|
||||
call.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onError(Context context, int notifId) {
|
||||
Notification replyError =
|
||||
Compatibility.createRepliedNotification(context, context.getString(R.string.error));
|
||||
LinphoneService.instance().getNotificationManager().sendNotification(notifId, replyError);
|
||||
}
|
||||
|
||||
private CharSequence getMessageText(Intent intent) {
|
||||
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
|
||||
if (remoteInput != null) {
|
||||
return remoteInput.getCharSequence(Compatibility.KEY_TEXT_REPLY);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue