Repository: android-rcs/rcsjta
Branch: master
Commit: f9e1e6108806
Files: 2092
Total size: 13.4 MB
Directory structure:
gitextract_mwevf30l/
├── .gitignore
├── README.md
├── RI/
│ ├── AndroidManifest.xml
│ ├── LICENSE-2.0.txt
│ ├── README.md
│ ├── build.gradle
│ ├── build.xml
│ ├── default.properties
│ ├── proguard-project.txt
│ ├── project.properties
│ ├── res/
│ │ ├── drawable/
│ │ │ ├── counter_circle.xml
│ │ │ ├── talk_item_rcs_in.xml
│ │ │ └── talk_item_rcs_out.xml
│ │ ├── layout/
│ │ │ ├── app_about.xml
│ │ │ ├── ask_permissions.xml
│ │ │ ├── audio_msg_record.xml
│ │ │ ├── capabilities_list.xml
│ │ │ ├── capabilities_list_item.xml
│ │ │ ├── capabilities_mine.xml
│ │ │ ├── capabilities_refresh.xml
│ │ │ ├── capabilities_request.xml
│ │ │ ├── chat_initiate_group.xml
│ │ │ ├── chat_initiate_single.xml
│ │ │ ├── chat_list.xml
│ │ │ ├── chat_message_log_item.xml
│ │ │ ├── chat_send_file.xml
│ │ │ ├── chat_service_config.xml
│ │ │ ├── chat_view.xml
│ │ │ ├── contact_list_item.xml
│ │ │ ├── contacts_blocking.xml
│ │ │ ├── contacts_rcs_list.xml
│ │ │ ├── contacts_vcard.xml
│ │ │ ├── delivery_info_item.xml
│ │ │ ├── delivery_info_list.xml
│ │ │ ├── extension_initiate_session.xml
│ │ │ ├── extension_messaging_session_view.xml
│ │ │ ├── extension_send_instant_message.xml
│ │ │ ├── extension_session_list.xml
│ │ │ ├── extension_streaming_session_view.xml
│ │ │ ├── filetransfer_custom_title.xml
│ │ │ ├── filetransfer_initiate.xml
│ │ │ ├── filetransfer_log_item.xml
│ │ │ ├── filetransfer_receive.xml
│ │ │ ├── filetransfer_send_multi_file.xml
│ │ │ ├── filetransfer_send_multi_file_item.xml
│ │ │ ├── filetransfer_service_config.xml
│ │ │ ├── fileupload_initiate.xml
│ │ │ ├── gchat_item_rcs_chat_in.xml
│ │ │ ├── gchat_item_rcs_chat_out.xml
│ │ │ ├── gchat_item_rcs_file_transfer_in.xml
│ │ │ ├── gchat_item_rcs_file_transfer_out.xml
│ │ │ ├── geoloc_display.xml
│ │ │ ├── geoloc_edit.xml
│ │ │ ├── geoloc_sharing_initiate.xml
│ │ │ ├── geoloc_sharing_receive.xml
│ │ │ ├── geoloc_show.xml
│ │ │ ├── groupchat_event_view_item.xml
│ │ │ ├── history_log_sharing.xml
│ │ │ ├── image_sharing_initiate.xml
│ │ │ ├── image_sharing_receive.xml
│ │ │ ├── intents_apps.xml
│ │ │ ├── ri_list.xml
│ │ │ ├── service_configuration.xml
│ │ │ ├── service_registration.xml
│ │ │ ├── service_status.xml
│ │ │ ├── sharing_log_geoloc_item.xml
│ │ │ ├── sharing_log_image_item.xml
│ │ │ ├── sharing_log_list.xml
│ │ │ ├── sharing_log_video_item.xml
│ │ │ ├── talk_item_rcs_chat_in.xml
│ │ │ ├── talk_item_rcs_chat_out.xml
│ │ │ ├── talk_item_rcs_file_transfer_in.xml
│ │ │ ├── talk_item_rcs_file_transfer_out.xml
│ │ │ ├── talk_log_group_item.xml
│ │ │ ├── talk_log_one_to_one_item.xml
│ │ │ ├── utils_smiley_menu_item.xml
│ │ │ ├── utils_spinner_item.xml
│ │ │ ├── video_sharing_incoming.xml
│ │ │ └── video_sharing_outgoing.xml
│ │ ├── menu/
│ │ │ ├── menu_1to1_talk.xml
│ │ │ ├── menu_1to1_talk_item.xml
│ │ │ ├── menu_ft.xml
│ │ │ ├── menu_gchat_item.xml
│ │ │ ├── menu_geoloc_sharing.xml
│ │ │ ├── menu_group_chat.xml
│ │ │ ├── menu_historylog.xml
│ │ │ ├── menu_image_sharing.xml
│ │ │ ├── menu_initiate_ft.xml
│ │ │ ├── menu_log.xml
│ │ │ ├── menu_log_item.xml
│ │ │ ├── menu_log_sharing_item.xml
│ │ │ ├── menu_mm_session.xml
│ │ │ └── menu_video_sharing.xml
│ │ └── values/
│ │ ├── filetotransfer.xml
│ │ ├── quicktexts.xml
│ │ ├── smileys.xml
│ │ └── strings.xml
│ └── src/
│ └── com/
│ └── gsma/
│ └── rcs/
│ └── ri/
│ ├── AboutRI.java
│ ├── DeviceBoot.java
│ ├── RI.java
│ ├── RcsServiceNotifManager.java
│ ├── RiApplication.java
│ ├── capabilities/
│ │ ├── CapabilitiesList.java
│ │ ├── MyCapabilities.java
│ │ ├── RequestAllCapabilities.java
│ │ ├── RequestCapabilities.java
│ │ └── TestCapabilitiesApi.java
│ ├── contacts/
│ │ ├── BlockingContact.java
│ │ ├── ContactVCard.java
│ │ ├── RcsContactsList.java
│ │ └── TestContactsApi.java
│ ├── extension/
│ │ ├── InitiateMultimediaSession.java
│ │ ├── InstantMessageReceiver.java
│ │ ├── MultiMediaSessionIntentService.java
│ │ ├── MultimediaSessionList.java
│ │ ├── SendInstantMessage.java
│ │ ├── SessionInvitationReceiver.java
│ │ ├── TestMultimediaSessionApi.java
│ │ ├── messaging/
│ │ │ ├── InitiateMessagingSession.java
│ │ │ ├── MessagingSessionList.java
│ │ │ ├── MessagingSessionUtils.java
│ │ │ └── MessagingSessionView.java
│ │ └── streaming/
│ │ ├── InitiateStreamingSession.java
│ │ ├── StreamingSessionList.java
│ │ ├── StreamingSessionUtils.java
│ │ └── StreamingSessionView.java
│ ├── intents/
│ │ └── TestIntentApps.java
│ ├── messaging/
│ │ ├── GroupDeliveryInfoList.java
│ │ ├── GroupTalkView.java
│ │ ├── OneToOneTalkView.java
│ │ ├── TalkList.java
│ │ ├── TalkListUpdate.java
│ │ ├── TestMessagingApi.java
│ │ ├── adapter/
│ │ │ ├── BasicViewHolder.java
│ │ │ ├── GroupDeliveryInfoCursorAdapter.java
│ │ │ ├── RcsChatInViewHolder.java
│ │ │ ├── RcsChatOutViewHolder.java
│ │ │ ├── RcsFileTransferInViewHolder.java
│ │ │ ├── RcsFileTransferOutViewHolder.java
│ │ │ ├── TalkCursorAdapter.java
│ │ │ ├── TalkListArrayAdapter.java
│ │ │ └── TalkListArrayItem.java
│ │ ├── chat/
│ │ │ ├── ChatCursorObserver.java
│ │ │ ├── ChatMessageDAO.java
│ │ │ ├── ChatMessageLogView.java
│ │ │ ├── ChatPendingIntentManager.java
│ │ │ ├── ChatServiceConfigActivity.java
│ │ │ ├── ISendFile.java
│ │ │ ├── IsComposingManager.java
│ │ │ ├── SendFile.java
│ │ │ ├── TestChatApi.java
│ │ │ ├── group/
│ │ │ │ ├── GroupChatDAO.java
│ │ │ │ ├── GroupChatIntentService.java
│ │ │ │ ├── GroupChatInvitationReceiver.java
│ │ │ │ ├── GroupChatMessageReceiver.java
│ │ │ │ ├── InitiateGroupChat.java
│ │ │ │ └── SendGroupFile.java
│ │ │ └── single/
│ │ │ ├── InitiateSingleChat.java
│ │ │ ├── SendSingleFile.java
│ │ │ ├── SingleChatIntentService.java
│ │ │ ├── SingleChatInvitationReceiver.java
│ │ │ └── UndeliveredMessageReceiver.java
│ │ ├── filetransfer/
│ │ │ ├── AudioMediaPlayer.java
│ │ │ ├── AudioMessageRecordActivity.java
│ │ │ ├── AudioRecorder.java
│ │ │ ├── FileTransferDAO.java
│ │ │ ├── FileTransferIntentService.java
│ │ │ ├── FileTransferInvitationReceiver.java
│ │ │ ├── FileTransferLogView.java
│ │ │ ├── FileTransferResumeReceiver.java
│ │ │ ├── FileTransferServiceConfigActivity.java
│ │ │ ├── InitiateFileTransfer.java
│ │ │ ├── ReceiveFileTransfer.java
│ │ │ ├── TestFileTransferApi.java
│ │ │ ├── UndeliveredFileReceiver.java
│ │ │ └── multi/
│ │ │ ├── FileTransferProperties.java
│ │ │ ├── ISendMultiFile.java
│ │ │ ├── SendMultiFile.java
│ │ │ ├── SendMultiFileGroupChat.java
│ │ │ └── SendMultiFileSingleChat.java
│ │ └── geoloc/
│ │ ├── DisplayGeoloc.java
│ │ ├── EditGeoloc.java
│ │ ├── SelectGeoloc.java
│ │ └── ShowGeoloc.java
│ ├── permissions/
│ │ └── PermissionsActivity.java
│ ├── service/
│ │ ├── RegistrationStatus.java
│ │ ├── ServiceConfigurationActivity.java
│ │ ├── ServiceStatus.java
│ │ └── TestServiceApi.java
│ ├── sharing/
│ │ ├── SharingListView.java
│ │ ├── TestSharingApi.java
│ │ ├── geoloc/
│ │ │ ├── GeolocSharingDAO.java
│ │ │ ├── GeolocSharingIntentService.java
│ │ │ ├── GeolocSharingInvitationReceiver.java
│ │ │ ├── GeolocSharingLogView.java
│ │ │ ├── InitiateGeolocSharing.java
│ │ │ └── ReceiveGeolocSharing.java
│ │ ├── image/
│ │ │ ├── ImageSharingDAO.java
│ │ │ ├── ImageSharingIntentService.java
│ │ │ ├── ImageSharingInvitationReceiver.java
│ │ │ ├── ImageSharingLogView.java
│ │ │ ├── InitiateImageSharing.java
│ │ │ └── ReceiveImageSharing.java
│ │ └── video/
│ │ ├── IncomingVideoSharing.java
│ │ ├── OutgoingVideoSharing.java
│ │ ├── VideoSharingDAO.java
│ │ ├── VideoSharingIntentService.java
│ │ ├── VideoSharingInvitationReceiver.java
│ │ ├── VideoSharingLogView.java
│ │ └── media/
│ │ ├── FifoBuffer.java
│ │ ├── OriginatingVideoPlayer.java
│ │ ├── TerminatingVideoPlayer.java
│ │ ├── VideoPlayerListener.java
│ │ ├── VideoSurface.java
│ │ └── VideoSurfaceView.java
│ ├── upload/
│ │ └── InitiateFileUpload.java
│ └── utils/
│ ├── AbstractMessageParser.java
│ ├── AndroidDatagramConnection.java
│ ├── BitmapCache.java
│ ├── BitmapLoader.java
│ ├── ContactListAdapter.java
│ ├── ContactUtil.java
│ ├── DatagramConnection.java
│ ├── FileUtils.java
│ ├── ImageBitmapLoader.java
│ ├── ImageUtils.java
│ ├── LogUtils.java
│ ├── NetworkRessourceManager.java
│ ├── RcsContactUtil.java
│ ├── RcsSessionUtil.java
│ ├── SmileyParser.java
│ ├── Smileys.java
│ └── Utils.java
├── SDK/
│ ├── concepts.jd
│ ├── demos.jd
│ ├── download/
│ │ └── index.jd
│ ├── getStarted.jd
│ ├── index.jd
│ ├── overview.jd
│ ├── releases/
│ │ ├── Albatros.jd
│ │ ├── Blackbird.jd
│ │ ├── Crane.jd
│ │ ├── albatros.xml
│ │ ├── blackbird.xml
│ │ ├── current.xml
│ │ └── joyn-sdk.txt
│ ├── samples/
│ │ ├── connectService.jd
│ │ ├── detectServices.jd
│ │ ├── index.jd
│ │ ├── initChat.jd
│ │ ├── listContacts.jd
│ │ └── serviceSupported.jd
│ ├── support.jd
│ ├── tools/
│ │ ├── getToKnow.jd
│ │ ├── index.jd
│ │ └── installJoynServices.jd
│ └── tutorials/
│ ├── index.jd
│ ├── multiApp.jd
│ ├── multiCapability.jd
│ ├── popUpApp.jd
│ └── ttsApp.jd
├── build.gradle
├── build.xml
├── core/
│ ├── .gitignore
│ ├── AndroidManifest.xml
│ ├── LICENSE-2.0.txt
│ ├── LICENSE-BOUNCYCASTLE.txt
│ ├── LICENSE-DNS.txt
│ ├── LICENSE-NIST.txt
│ ├── NOTICE.txt
│ ├── README.md
│ ├── ant.properties
│ ├── build.gradle
│ ├── build.xml
│ ├── default.properties
│ ├── findbugs-exclude.xml
│ ├── jarfiles.txt
│ ├── proguard-project.txt
│ ├── proguard.cfg
│ ├── project.properties
│ ├── res/
│ │ ├── layout/
│ │ │ ├── activity_permissions_alert_dialog.xml
│ │ │ ├── provisioning.xml
│ │ │ ├── provisioning_capabilities.xml
│ │ │ ├── provisioning_logger.xml
│ │ │ ├── provisioning_profile.xml
│ │ │ ├── provisioning_service.xml
│ │ │ ├── provisioning_stack.xml
│ │ │ ├── rcs_provisioning_about.xml
│ │ │ ├── rcs_provisioning_generate_profile.xml
│ │ │ ├── rcs_terms_and_conditions.xml
│ │ │ └── rcs_wifi_provisioning.xml
│ │ ├── menu/
│ │ │ └── menu_provisioning.xml
│ │ ├── values/
│ │ │ ├── dimens.xml
│ │ │ ├── rcs_core_strings.xml
│ │ │ ├── rcs_provisioning_strings.xml
│ │ │ ├── rcs_settings_strings.xml
│ │ │ └── styles.xml
│ │ ├── values-de/
│ │ │ └── rcs_core_strings.xml
│ │ ├── values-fr/
│ │ │ └── rcs_core_strings.xml
│ │ ├── values-w820dp/
│ │ │ └── dimens.xml
│ │ └── xml/
│ │ ├── rcs_core_account_preferences.xml
│ │ ├── rcs_core_authenticator.xml
│ │ ├── rcs_core_contacts.xml
│ │ └── rcs_core_syncadapter.xml
│ ├── src/
│ │ └── com/
│ │ ├── gsma/
│ │ │ └── rcs/
│ │ │ ├── addressbook/
│ │ │ │ ├── AccountChangedReceiver.java
│ │ │ │ ├── AddressBookEventListener.java
│ │ │ │ ├── AddressBookManager.java
│ │ │ │ ├── AuthenticationService.java
│ │ │ │ ├── LocaleManager.java
│ │ │ │ ├── RcsAccountException.java
│ │ │ │ ├── RcsAccountManager.java
│ │ │ │ ├── SetupRcsAccount.java
│ │ │ │ └── SyncAdapterService.java
│ │ │ ├── core/
│ │ │ │ ├── Core.java
│ │ │ │ ├── CoreException.java
│ │ │ │ ├── CoreListener.java
│ │ │ │ ├── FileAccessException.java
│ │ │ │ ├── ParseFailureException.java
│ │ │ │ ├── TerminalInfo.java
│ │ │ │ ├── access/
│ │ │ │ │ ├── MobileNetworkAccess.java
│ │ │ │ │ ├── NetworkAccess.java
│ │ │ │ │ └── WifiNetworkAccess.java
│ │ │ │ ├── content/
│ │ │ │ │ ├── AudioContent.java
│ │ │ │ │ ├── ContentManager.java
│ │ │ │ │ ├── FileContent.java
│ │ │ │ │ ├── GeolocContent.java
│ │ │ │ │ ├── LiveAudioContent.java
│ │ │ │ │ ├── LiveVideoContent.java
│ │ │ │ │ ├── MmContent.java
│ │ │ │ │ ├── PhotoContent.java
│ │ │ │ │ ├── VideoContent.java
│ │ │ │ │ └── VisitCardContent.java
│ │ │ │ └── ims/
│ │ │ │ ├── ImsError.java
│ │ │ │ ├── ImsModule.java
│ │ │ │ ├── network/
│ │ │ │ │ ├── ImsConnectionManager.java
│ │ │ │ │ ├── ImsNetworkInterface.java
│ │ │ │ │ ├── MobileNetworkInterface.java
│ │ │ │ │ ├── NetworkException.java
│ │ │ │ │ ├── WifiNetworkInterface.java
│ │ │ │ │ ├── gsm/
│ │ │ │ │ │ └── CallManager.java
│ │ │ │ │ ├── registration/
│ │ │ │ │ │ ├── GibaRegistrationProcedure.java
│ │ │ │ │ │ ├── HttpDigestRegistrationProcedure.java
│ │ │ │ │ │ ├── RegistrationManager.java
│ │ │ │ │ │ ├── RegistrationProcedure.java
│ │ │ │ │ │ └── RegistrationUtils.java
│ │ │ │ │ └── sip/
│ │ │ │ │ ├── FeatureTags.java
│ │ │ │ │ ├── Multipart.java
│ │ │ │ │ ├── SipManager.java
│ │ │ │ │ ├── SipMessageFactory.java
│ │ │ │ │ └── SipUtils.java
│ │ │ │ ├── protocol/
│ │ │ │ │ ├── PayloadException.java
│ │ │ │ │ ├── http/
│ │ │ │ │ │ ├── HttpAuthenticationAgent.java
│ │ │ │ │ │ ├── HttpDeleteRequest.java
│ │ │ │ │ │ ├── HttpGetRequest.java
│ │ │ │ │ │ ├── HttpPostRequest.java
│ │ │ │ │ │ ├── HttpPutRequest.java
│ │ │ │ │ │ ├── HttpRequest.java
│ │ │ │ │ │ └── HttpResponse.java
│ │ │ │ │ ├── msrp/
│ │ │ │ │ │ ├── ChunkReceiver.java
│ │ │ │ │ │ ├── ChunkSender.java
│ │ │ │ │ │ ├── DataChunks.java
│ │ │ │ │ │ ├── FifoBuffer.java
│ │ │ │ │ │ ├── MsrpClientConnection.java
│ │ │ │ │ │ ├── MsrpConnection.java
│ │ │ │ │ │ ├── MsrpConstants.java
│ │ │ │ │ │ ├── MsrpEventListener.java
│ │ │ │ │ │ ├── MsrpManager.java
│ │ │ │ │ │ ├── MsrpServerConnection.java
│ │ │ │ │ │ ├── MsrpSession.java
│ │ │ │ │ │ ├── MsrpTransaction.java
│ │ │ │ │ │ ├── MsrpUtils.java
│ │ │ │ │ │ ├── ReportTransaction.java
│ │ │ │ │ │ └── RequestTransaction.java
│ │ │ │ │ ├── rtp/
│ │ │ │ │ │ ├── CodecChain.java
│ │ │ │ │ │ ├── DummyPacketGenerator.java
│ │ │ │ │ │ ├── MediaRegistry.java
│ │ │ │ │ │ ├── MediaRtpReceiver.java
│ │ │ │ │ │ ├── MediaRtpSender.java
│ │ │ │ │ │ ├── Processor.java
│ │ │ │ │ │ ├── RtpUtils.java
│ │ │ │ │ │ ├── VideoRtpReceiver.java
│ │ │ │ │ │ ├── VideoRtpSender.java
│ │ │ │ │ │ ├── codec/
│ │ │ │ │ │ │ ├── Codec.java
│ │ │ │ │ │ │ ├── audio/
│ │ │ │ │ │ │ │ ├── AudioCodec.java
│ │ │ │ │ │ │ │ └── amr/
│ │ │ │ │ │ │ │ └── AMRWBConfig.java
│ │ │ │ │ │ │ └── video/
│ │ │ │ │ │ │ ├── VideoCodec.java
│ │ │ │ │ │ │ └── h264/
│ │ │ │ │ │ │ ├── H264Config.java
│ │ │ │ │ │ │ ├── H264RtpHeaders.java
│ │ │ │ │ │ │ ├── JavaDepacketizer.java
│ │ │ │ │ │ │ ├── JavaPacketizer.java
│ │ │ │ │ │ │ ├── NalUnitHeader.java
│ │ │ │ │ │ │ ├── NalUnitType.java
│ │ │ │ │ │ │ ├── decoder/
│ │ │ │ │ │ │ │ └── NativeH264Decoder.java
│ │ │ │ │ │ │ ├── encoder/
│ │ │ │ │ │ │ │ ├── NativeH264Encoder.java
│ │ │ │ │ │ │ │ └── NativeH264EncoderParams.java
│ │ │ │ │ │ │ └── profiles/
│ │ │ │ │ │ │ ├── H264Profile.java
│ │ │ │ │ │ │ ├── H264Profile1.java
│ │ │ │ │ │ │ ├── H264Profile1_1.java
│ │ │ │ │ │ │ ├── H264Profile1_2.java
│ │ │ │ │ │ │ ├── H264Profile1_3.java
│ │ │ │ │ │ │ ├── H264Profile1b.java
│ │ │ │ │ │ │ ├── H264TypeLevel.java
│ │ │ │ │ │ │ └── H264TypeProfile.java
│ │ │ │ │ │ ├── core/
│ │ │ │ │ │ │ ├── ReceptionReport.java
│ │ │ │ │ │ │ ├── RtcpAppPacket.java
│ │ │ │ │ │ │ ├── RtcpByePacket.java
│ │ │ │ │ │ │ ├── RtcpCompoundPacket.java
│ │ │ │ │ │ │ ├── RtcpPacket.java
│ │ │ │ │ │ │ ├── RtcpPacketReceiver.java
│ │ │ │ │ │ │ ├── RtcpPacketTransmitter.java
│ │ │ │ │ │ │ ├── RtcpPacketUtils.java
│ │ │ │ │ │ │ ├── RtcpReceiverReportPacket.java
│ │ │ │ │ │ │ ├── RtcpReport.java
│ │ │ │ │ │ │ ├── RtcpSdesBlock.java
│ │ │ │ │ │ │ ├── RtcpSdesItem.java
│ │ │ │ │ │ │ ├── RtcpSdesPacket.java
│ │ │ │ │ │ │ ├── RtcpSenderReportPacket.java
│ │ │ │ │ │ │ ├── RtcpSession.java
│ │ │ │ │ │ │ ├── RtcpStatisticsReceiver.java
│ │ │ │ │ │ │ ├── RtcpStatisticsTransmitter.java
│ │ │ │ │ │ │ ├── RtpExtensionHeader.java
│ │ │ │ │ │ │ ├── RtpPacket.java
│ │ │ │ │ │ │ ├── RtpPacketReceiver.java
│ │ │ │ │ │ │ ├── RtpPacketTransmitter.java
│ │ │ │ │ │ │ ├── RtpSource.java
│ │ │ │ │ │ │ ├── RtpStatisticsReceiver.java
│ │ │ │ │ │ │ └── RtpStatisticsTransmitter.java
│ │ │ │ │ │ ├── event/
│ │ │ │ │ │ │ ├── RtcpApplicationEvent.java
│ │ │ │ │ │ │ ├── RtcpByeEvent.java
│ │ │ │ │ │ │ ├── RtcpEvent.java
│ │ │ │ │ │ │ ├── RtcpEventListener.java
│ │ │ │ │ │ │ ├── RtcpReceiverReportEvent.java
│ │ │ │ │ │ │ ├── RtcpSdesEvent.java
│ │ │ │ │ │ │ └── RtcpSenderReportEvent.java
│ │ │ │ │ │ ├── format/
│ │ │ │ │ │ │ ├── DummyFormat.java
│ │ │ │ │ │ │ ├── Format.java
│ │ │ │ │ │ │ ├── audio/
│ │ │ │ │ │ │ │ ├── AmrWbAudioFormat.java
│ │ │ │ │ │ │ │ └── AudioFormat.java
│ │ │ │ │ │ │ ├── data/
│ │ │ │ │ │ │ │ └── DataFormat.java
│ │ │ │ │ │ │ └── video/
│ │ │ │ │ │ │ ├── CameraOptions.java
│ │ │ │ │ │ │ ├── H264VideoFormat.java
│ │ │ │ │ │ │ ├── Orientation.java
│ │ │ │ │ │ │ ├── VideoFormat.java
│ │ │ │ │ │ │ └── VideoOrientation.java
│ │ │ │ │ │ ├── media/
│ │ │ │ │ │ │ ├── MediaException.java
│ │ │ │ │ │ │ ├── MediaInput.java
│ │ │ │ │ │ │ ├── MediaListener.java
│ │ │ │ │ │ │ ├── MediaOutput.java
│ │ │ │ │ │ │ ├── MediaSample.java
│ │ │ │ │ │ │ └── VideoSample.java
│ │ │ │ │ │ ├── stream/
│ │ │ │ │ │ │ ├── DummyPacketSourceStream.java
│ │ │ │ │ │ │ ├── MediaCaptureStream.java
│ │ │ │ │ │ │ ├── MediaRendererStream.java
│ │ │ │ │ │ │ ├── ProcessorInputStream.java
│ │ │ │ │ │ │ ├── ProcessorOutputStream.java
│ │ │ │ │ │ │ ├── RtpInputStream.java
│ │ │ │ │ │ │ ├── RtpOutputStream.java
│ │ │ │ │ │ │ ├── RtpStreamListener.java
│ │ │ │ │ │ │ ├── VideoCaptureStream.java
│ │ │ │ │ │ │ └── VideoRendererStream.java
│ │ │ │ │ │ └── util/
│ │ │ │ │ │ ├── Buffer.java
│ │ │ │ │ │ ├── Packet.java
│ │ │ │ │ │ ├── RtpConstants.java
│ │ │ │ │ │ └── SystemTimeBase.java
│ │ │ │ │ ├── sdp/
│ │ │ │ │ │ ├── MediaAttribute.java
│ │ │ │ │ │ ├── MediaDescription.java
│ │ │ │ │ │ ├── Parser.java
│ │ │ │ │ │ ├── SdpParser.java
│ │ │ │ │ │ ├── SdpUtils.java
│ │ │ │ │ │ ├── SessionDescription.java
│ │ │ │ │ │ └── TimeDescription.java
│ │ │ │ │ └── sip/
│ │ │ │ │ ├── KeepAliveManager.java
│ │ │ │ │ ├── SipDialogPath.java
│ │ │ │ │ ├── SipEventListener.java
│ │ │ │ │ ├── SipInterface.java
│ │ │ │ │ ├── SipMessage.java
│ │ │ │ │ ├── SipRequest.java
│ │ │ │ │ ├── SipResponse.java
│ │ │ │ │ ├── SipTransactionContext.java
│ │ │ │ │ └── SipTransactionList.java
│ │ │ │ ├── security/
│ │ │ │ │ ├── Digest.java
│ │ │ │ │ ├── GeneralDigest.java
│ │ │ │ │ ├── HttpDigestMd5Authentication.java
│ │ │ │ │ ├── MD5Digest.java
│ │ │ │ │ └── cert/
│ │ │ │ │ ├── KeyStoreManager.java
│ │ │ │ │ ├── KeyStoreManagerException.java
│ │ │ │ │ └── X509KeyManagerWrapper.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── ContactInfo.java
│ │ │ │ │ ├── ImsService.java
│ │ │ │ │ ├── ImsServiceDispatcher.java
│ │ │ │ │ ├── ImsServiceError.java
│ │ │ │ │ ├── ImsServiceSession.java
│ │ │ │ │ ├── ImsSessionBasedServiceError.java
│ │ │ │ │ ├── ImsSessionListener.java
│ │ │ │ │ ├── SessionActivityManager.java
│ │ │ │ │ ├── SessionAuthenticationAgent.java
│ │ │ │ │ ├── SessionIdGenerator.java
│ │ │ │ │ ├── SessionNotEstablishedException.java
│ │ │ │ │ ├── SessionTimerManager.java
│ │ │ │ │ ├── SipIntentManager.java
│ │ │ │ │ ├── UpdateSessionManager.java
│ │ │ │ │ ├── UpdateSessionManagerListener.java
│ │ │ │ │ ├── capability/
│ │ │ │ │ │ ├── AnonymousFetchManager.java
│ │ │ │ │ │ ├── AnonymousFetchRequestTask.java
│ │ │ │ │ │ ├── Capabilities.java
│ │ │ │ │ │ ├── CapabilityError.java
│ │ │ │ │ │ ├── CapabilityService.java
│ │ │ │ │ │ ├── CapabilityUtils.java
│ │ │ │ │ │ ├── DiscoveryManager.java
│ │ │ │ │ │ ├── ExternalCapabilityMonitoring.java
│ │ │ │ │ │ ├── OptionsManager.java
│ │ │ │ │ │ ├── OptionsRequestTask.java
│ │ │ │ │ │ ├── PollingManager.java
│ │ │ │ │ │ └── SyncContactTask.java
│ │ │ │ │ ├── extension/
│ │ │ │ │ │ ├── ServiceExtensionManager.java
│ │ │ │ │ │ └── SupportedExtensionUpdater.java
│ │ │ │ │ ├── im/
│ │ │ │ │ │ ├── InstantMessagingService.java
│ │ │ │ │ │ ├── chat/
│ │ │ │ │ │ │ ├── ChatError.java
│ │ │ │ │ │ │ ├── ChatMessage.java
│ │ │ │ │ │ │ ├── ChatSession.java
│ │ │ │ │ │ │ ├── ChatSessionListener.java
│ │ │ │ │ │ │ ├── ChatUtils.java
│ │ │ │ │ │ │ ├── ContributionIdGenerator.java
│ │ │ │ │ │ │ ├── GroupChatAutoRejoinTask.java
│ │ │ │ │ │ │ ├── GroupChatInfo.java
│ │ │ │ │ │ │ ├── GroupChatSession.java
│ │ │ │ │ │ │ ├── GroupChatSessionListener.java
│ │ │ │ │ │ │ ├── OneToOneChatSession.java
│ │ │ │ │ │ │ ├── OneToOneChatSessionListener.java
│ │ │ │ │ │ │ ├── OriginatingAdhocGroupChatSession.java
│ │ │ │ │ │ │ ├── OriginatingOneToOneChatSession.java
│ │ │ │ │ │ │ ├── ParticipantInfoUtils.java
│ │ │ │ │ │ │ ├── RejoinGroupChatSession.java
│ │ │ │ │ │ │ ├── RestartGroupChatSession.java
│ │ │ │ │ │ │ ├── SessionUnavailableException.java
│ │ │ │ │ │ │ ├── TerminatingAdhocGroupChatSession.java
│ │ │ │ │ │ │ ├── TerminatingOneToOneChatSession.java
│ │ │ │ │ │ │ ├── cpim/
│ │ │ │ │ │ │ │ ├── CpimHeader.java
│ │ │ │ │ │ │ │ ├── CpimIdentity.java
│ │ │ │ │ │ │ │ ├── CpimMessage.java
│ │ │ │ │ │ │ │ └── CpimParser.java
│ │ │ │ │ │ │ ├── event/
│ │ │ │ │ │ │ │ ├── ConferenceEventSubscribeManager.java
│ │ │ │ │ │ │ │ ├── ConferenceInfoDocument.java
│ │ │ │ │ │ │ │ ├── ConferenceInfoParser.java
│ │ │ │ │ │ │ │ └── User.java
│ │ │ │ │ │ │ ├── geoloc/
│ │ │ │ │ │ │ │ ├── GeolocInfoDocument.java
│ │ │ │ │ │ │ │ └── GeolocInfoParser.java
│ │ │ │ │ │ │ ├── imdn/
│ │ │ │ │ │ │ │ ├── DeliveryExpirationManager.java
│ │ │ │ │ │ │ │ ├── ImdnDocument.java
│ │ │ │ │ │ │ │ ├── ImdnManager.java
│ │ │ │ │ │ │ │ ├── ImdnParser.java
│ │ │ │ │ │ │ │ └── ImdnUtils.java
│ │ │ │ │ │ │ ├── iscomposing/
│ │ │ │ │ │ │ │ ├── IsComposingInfo.java
│ │ │ │ │ │ │ │ ├── IsComposingManager.java
│ │ │ │ │ │ │ │ └── IsComposingParser.java
│ │ │ │ │ │ │ ├── resourcelist/
│ │ │ │ │ │ │ │ ├── ResourceListDocument.java
│ │ │ │ │ │ │ │ └── ResourceListParser.java
│ │ │ │ │ │ │ └── standfw/
│ │ │ │ │ │ │ ├── StoreAndForwardManager.java
│ │ │ │ │ │ │ ├── TerminatingStoreAndForwardOneToOneChatMessageSession.java
│ │ │ │ │ │ │ └── TerminatingStoreAndForwardOneToOneChatNotificationSession.java
│ │ │ │ │ │ └── filetransfer/
│ │ │ │ │ │ ├── FileSharingError.java
│ │ │ │ │ │ ├── FileSharingSession.java
│ │ │ │ │ │ ├── FileSharingSessionListener.java
│ │ │ │ │ │ ├── FileTransferUtils.java
│ │ │ │ │ │ ├── ImsFileSharingSession.java
│ │ │ │ │ │ ├── http/
│ │ │ │ │ │ │ ├── DownloadFromAcceptFileSharingSession.java
│ │ │ │ │ │ │ ├── DownloadFromInviteFileSharingSession.java
│ │ │ │ │ │ │ ├── DownloadFromResumeFileSharingSession.java
│ │ │ │ │ │ │ ├── FileNotDownloadedException.java
│ │ │ │ │ │ │ ├── FileTransferHttpInfoDocument.java
│ │ │ │ │ │ │ ├── FileTransferHttpResumeInfo.java
│ │ │ │ │ │ │ ├── FileTransferHttpResumeInfoParser.java
│ │ │ │ │ │ │ ├── FileTransferHttpThumbnail.java
│ │ │ │ │ │ │ ├── FileTransferXmlParser.java
│ │ │ │ │ │ │ ├── FtHttpResumeManager.java
│ │ │ │ │ │ │ ├── HttpDownloadManager.java
│ │ │ │ │ │ │ ├── HttpFileTransferSession.java
│ │ │ │ │ │ │ ├── HttpTransferEventListener.java
│ │ │ │ │ │ │ ├── HttpTransferManager.java
│ │ │ │ │ │ │ ├── HttpUploadManager.java
│ │ │ │ │ │ │ ├── HttpUploadTransferEventListener.java
│ │ │ │ │ │ │ ├── OriginatingHttpFileSharingSession.java
│ │ │ │ │ │ │ ├── OriginatingHttpGroupFileSharingSession.java
│ │ │ │ │ │ │ ├── ResumeUploadFileSharingSession.java
│ │ │ │ │ │ │ ├── ResumeUploadGroupFileSharingSession.java
│ │ │ │ │ │ │ └── TerminatingHttpFileSharingSession.java
│ │ │ │ │ │ └── msrp/
│ │ │ │ │ │ ├── OriginatingMsrpFileSharingSession.java
│ │ │ │ │ │ └── TerminatingMsrpFileSharingSession.java
│ │ │ │ │ ├── presence/
│ │ │ │ │ │ ├── FavoriteLink.java
│ │ │ │ │ │ ├── Geoloc.java
│ │ │ │ │ │ ├── PhotoIcon.java
│ │ │ │ │ │ ├── PresenceError.java
│ │ │ │ │ │ ├── PresenceInfo.java
│ │ │ │ │ │ ├── PresenceService.java
│ │ │ │ │ │ ├── PresenceSubscribeManager.java
│ │ │ │ │ │ ├── PresenceUtils.java
│ │ │ │ │ │ ├── PublishManager.java
│ │ │ │ │ │ ├── SubscribeManager.java
│ │ │ │ │ │ ├── WatcherInfoSubscribeManager.java
│ │ │ │ │ │ ├── directory/
│ │ │ │ │ │ │ ├── Entry.java
│ │ │ │ │ │ │ ├── Folder.java
│ │ │ │ │ │ │ └── XcapDirectoryParser.java
│ │ │ │ │ │ ├── pidf/
│ │ │ │ │ │ │ ├── Basic.java
│ │ │ │ │ │ │ ├── Contact.java
│ │ │ │ │ │ │ ├── Note.java
│ │ │ │ │ │ │ ├── OverridingWillingness.java
│ │ │ │ │ │ │ ├── Person.java
│ │ │ │ │ │ │ ├── PidfDocument.java
│ │ │ │ │ │ │ ├── PidfParser.java
│ │ │ │ │ │ │ ├── Service.java
│ │ │ │ │ │ │ ├── Status.java
│ │ │ │ │ │ │ ├── StatusIcon.java
│ │ │ │ │ │ │ ├── Tuple.java
│ │ │ │ │ │ │ └── geoloc/
│ │ │ │ │ │ │ └── Geopriv.java
│ │ │ │ │ │ ├── rlmi/
│ │ │ │ │ │ │ ├── ResourceInstance.java
│ │ │ │ │ │ │ ├── RlmiDocument.java
│ │ │ │ │ │ │ └── RlmiParser.java
│ │ │ │ │ │ ├── watcherinfo/
│ │ │ │ │ │ │ ├── Watcher.java
│ │ │ │ │ │ │ ├── WatcherInfoDocument.java
│ │ │ │ │ │ │ └── WatcherInfoParser.java
│ │ │ │ │ │ └── xdm/
│ │ │ │ │ │ ├── HttpAuthenticationAgent.java
│ │ │ │ │ │ ├── XcapResponseParser.java
│ │ │ │ │ │ └── XdmManager.java
│ │ │ │ │ ├── richcall/
│ │ │ │ │ │ ├── ContentSharingError.java
│ │ │ │ │ │ ├── ContentSharingSession.java
│ │ │ │ │ │ ├── RichcallService.java
│ │ │ │ │ │ ├── geoloc/
│ │ │ │ │ │ │ ├── GeolocTransferSession.java
│ │ │ │ │ │ │ ├── GeolocTransferSessionListener.java
│ │ │ │ │ │ │ ├── OriginatingGeolocTransferSession.java
│ │ │ │ │ │ │ └── TerminatingGeolocTransferSession.java
│ │ │ │ │ │ ├── image/
│ │ │ │ │ │ │ ├── ImageTransferSession.java
│ │ │ │ │ │ │ ├── ImageTransferSessionListener.java
│ │ │ │ │ │ │ ├── OriginatingImageTransferSession.java
│ │ │ │ │ │ │ └── TerminatingImageTransferSession.java
│ │ │ │ │ │ └── video/
│ │ │ │ │ │ ├── OriginatingVideoStreamingSession.java
│ │ │ │ │ │ ├── SdpOrientationExtension.java
│ │ │ │ │ │ ├── TerminatingVideoStreamingSession.java
│ │ │ │ │ │ ├── VideoCodecManager.java
│ │ │ │ │ │ ├── VideoSdpBuilder.java
│ │ │ │ │ │ ├── VideoStreamingSession.java
│ │ │ │ │ │ └── VideoStreamingSessionListener.java
│ │ │ │ │ ├── sip/
│ │ │ │ │ │ ├── EnrichCallingService.java
│ │ │ │ │ │ ├── GenericSipSession.java
│ │ │ │ │ │ ├── ImmManager.java
│ │ │ │ │ │ ├── SipService.java
│ │ │ │ │ │ ├── SipSessionError.java
│ │ │ │ │ │ ├── SipSessionListener.java
│ │ │ │ │ │ ├── messaging/
│ │ │ │ │ │ │ ├── GenericSipMsrpSession.java
│ │ │ │ │ │ │ ├── OriginatingSipMsrpSession.java
│ │ │ │ │ │ │ └── TerminatingSipMsrpSession.java
│ │ │ │ │ │ └── streaming/
│ │ │ │ │ │ ├── DataReceiver.java
│ │ │ │ │ │ ├── DataSender.java
│ │ │ │ │ │ ├── GenericSipRtpSession.java
│ │ │ │ │ │ ├── OriginatingSipRtpSession.java
│ │ │ │ │ │ └── TerminatingSipRtpSession.java
│ │ │ │ │ ├── terms/
│ │ │ │ │ │ ├── EndUserNotificationParser.java
│ │ │ │ │ │ ├── TermsAckParser.java
│ │ │ │ │ │ ├── TermsConditionsService.java
│ │ │ │ │ │ └── TermsRequestParser.java
│ │ │ │ │ └── upload/
│ │ │ │ │ ├── FileUploadSession.java
│ │ │ │ │ └── FileUploadSessionListener.java
│ │ │ │ └── userprofile/
│ │ │ │ ├── GibaUserProfileInterface.java
│ │ │ │ ├── SettingsUserProfileInterface.java
│ │ │ │ ├── UserProfile.java
│ │ │ │ └── UserProfileInterface.java
│ │ │ ├── platform/
│ │ │ │ ├── AndroidFactory.java
│ │ │ │ ├── FactoryException.java
│ │ │ │ ├── file/
│ │ │ │ │ ├── AndroidFileFactory.java
│ │ │ │ │ ├── FileDescription.java
│ │ │ │ │ └── FileFactory.java
│ │ │ │ ├── logger/
│ │ │ │ │ └── AndroidAppender.java
│ │ │ │ ├── network/
│ │ │ │ │ ├── AndroidDatagramConnection.java
│ │ │ │ │ ├── AndroidHttpConnection.java
│ │ │ │ │ ├── AndroidNetworkFactory.java
│ │ │ │ │ ├── AndroidSecureSocketConnection.java
│ │ │ │ │ ├── AndroidSocketConnection.java
│ │ │ │ │ ├── AndroidSocketServerConnection.java
│ │ │ │ │ ├── DatagramConnection.java
│ │ │ │ │ ├── HttpConnection.java
│ │ │ │ │ ├── NetworkFactory.java
│ │ │ │ │ ├── SocketConnection.java
│ │ │ │ │ └── SocketServerConnection.java
│ │ │ │ └── registry/
│ │ │ │ ├── AndroidRegistryFactory.java
│ │ │ │ └── RegistryFactory.java
│ │ │ ├── provider/
│ │ │ │ ├── ContentProviderBaseIdCreator.java
│ │ │ │ ├── CursorUtil.java
│ │ │ │ ├── DeleteTask.java
│ │ │ │ ├── LocalContentResolver.java
│ │ │ │ ├── UserProfilePersistedStorageUtil.java
│ │ │ │ ├── contact/
│ │ │ │ │ ├── ContactData.java
│ │ │ │ │ ├── ContactManager.java
│ │ │ │ │ ├── ContactManagerException.java
│ │ │ │ │ └── ContactProvider.java
│ │ │ │ ├── fthttp/
│ │ │ │ │ ├── FtHttpResume.java
│ │ │ │ │ ├── FtHttpResumeDownload.java
│ │ │ │ │ └── FtHttpResumeUpload.java
│ │ │ │ ├── history/
│ │ │ │ │ ├── GroupChatDequeueTask.java
│ │ │ │ │ ├── GroupChatTerminalExceptionTask.java
│ │ │ │ │ ├── HistoryConstants.java
│ │ │ │ │ ├── HistoryLog.java
│ │ │ │ │ ├── HistoryLogData.java
│ │ │ │ │ ├── HistoryMemberBaseIdCreator.java
│ │ │ │ │ ├── HistoryMemberDatabase.java
│ │ │ │ │ ├── HistoryProvider.java
│ │ │ │ │ ├── MultiDbProvider.java
│ │ │ │ │ ├── OneToOneChatDequeueTask.java
│ │ │ │ │ └── QueryHelper.java
│ │ │ │ ├── messaging/
│ │ │ │ │ ├── ChatMessagePersistedStorageAccessor.java
│ │ │ │ │ ├── ChatProvider.java
│ │ │ │ │ ├── DelayedDisplayNotificationDispatcher.java
│ │ │ │ │ ├── FileTransferData.java
│ │ │ │ │ ├── FileTransferDequeueTask.java
│ │ │ │ │ ├── FileTransferLog.java
│ │ │ │ │ ├── FileTransferPersistedStorageAccessor.java
│ │ │ │ │ ├── FileTransferProvider.java
│ │ │ │ │ ├── FileTransferStateAndReasonCode.java
│ │ │ │ │ ├── GroupChatData.java
│ │ │ │ │ ├── GroupChatDeleteTask.java
│ │ │ │ │ ├── GroupChatLog.java
│ │ │ │ │ ├── GroupChatMessageDeleteTask.java
│ │ │ │ │ ├── GroupChatPersistedStorageAccessor.java
│ │ │ │ │ ├── GroupDeliveryInfoData.java
│ │ │ │ │ ├── GroupDeliveryInfoLog.java
│ │ │ │ │ ├── GroupDeliveryInfoProvider.java
│ │ │ │ │ ├── GroupFileTransferDeleteTask.java
│ │ │ │ │ ├── IFileTransferLog.java
│ │ │ │ │ ├── IGroupChatLog.java
│ │ │ │ │ ├── IGroupDeliveryInfoLog.java
│ │ │ │ │ ├── IMessageLog.java
│ │ │ │ │ ├── MessageData.java
│ │ │ │ │ ├── MessageLog.java
│ │ │ │ │ ├── MessagingLog.java
│ │ │ │ │ ├── OneToOneChatMessageDeleteTask.java
│ │ │ │ │ ├── OneToOneChatMessageDequeueTask.java
│ │ │ │ │ ├── OneToOneFileTransferDeleteTask.java
│ │ │ │ │ ├── RecreateDeliveryExpirationAlarms.java
│ │ │ │ │ └── UpdateFileTransferStateAfterUngracefulTerminationTask.java
│ │ │ │ ├── settings/
│ │ │ │ │ ├── RcsSettings.java
│ │ │ │ │ ├── RcsSettingsData.java
│ │ │ │ │ └── RcsSettingsProvider.java
│ │ │ │ └── sharing/
│ │ │ │ ├── GeolocSharingData.java
│ │ │ │ ├── GeolocSharingDeleteTask.java
│ │ │ │ ├── GeolocSharingPersistedStorageAccessor.java
│ │ │ │ ├── GeolocSharingProvider.java
│ │ │ │ ├── GeolocSharingStateAndReasonCode.java
│ │ │ │ ├── ImageSharingData.java
│ │ │ │ ├── ImageSharingDeleteTask.java
│ │ │ │ ├── ImageSharingPersistedStorageAccessor.java
│ │ │ │ ├── ImageSharingProvider.java
│ │ │ │ ├── ImageSharingStateAndReasonCode.java
│ │ │ │ ├── RichCallHistory.java
│ │ │ │ ├── UpdateGeolocSharingStateAfterUngracefulTerminationTask.java
│ │ │ │ ├── UpdateImageSharingStateAfterUngracefulTerminationTask.java
│ │ │ │ ├── UpdateVideoSharingStateAfterUngracefulTerminationTask.java
│ │ │ │ ├── VideoSharingData.java
│ │ │ │ ├── VideoSharingDeleteTask.java
│ │ │ │ ├── VideoSharingPersistedStorageAccessor.java
│ │ │ │ ├── VideoSharingProvider.java
│ │ │ │ └── VideoSharingStateAndReasonCode.java
│ │ │ ├── provisioning/
│ │ │ │ ├── Parameter.java
│ │ │ │ ├── ProvisioningFailureReasons.java
│ │ │ │ ├── ProvisioningInfo.java
│ │ │ │ ├── ProvisioningParser.java
│ │ │ │ ├── TermsAndConditionsRequest.java
│ │ │ │ ├── https/
│ │ │ │ │ ├── EasyX509TrustManager.java
│ │ │ │ │ ├── HttpsProvisioningAlertDialog.java
│ │ │ │ │ ├── HttpsProvisioningConnection.java
│ │ │ │ │ ├── HttpsProvisioningManager.java
│ │ │ │ │ ├── HttpsProvisioningMsisdnInput.java
│ │ │ │ │ ├── HttpsProvisioningResult.java
│ │ │ │ │ ├── HttpsProvisioningSMS.java
│ │ │ │ │ ├── HttpsProvisioningService.java
│ │ │ │ │ ├── HttpsProvisioningUtils.java
│ │ │ │ │ └── ProvisioningPushSMSReceiver.java
│ │ │ │ └── local/
│ │ │ │ ├── CapabilitiesProvisioning.java
│ │ │ │ ├── IProvisioningFragment.java
│ │ │ │ ├── LoggerProvisioning.java
│ │ │ │ ├── ProfileProvisioning.java
│ │ │ │ ├── Provisioning.java
│ │ │ │ ├── ProvisioningHelper.java
│ │ │ │ ├── ServiceProvisioning.java
│ │ │ │ ├── SlidingTabLayout.java
│ │ │ │ ├── SlidingTabStrip.java
│ │ │ │ ├── StackProvisioning.java
│ │ │ │ └── ViewPagerAdapter.java
│ │ │ ├── service/
│ │ │ │ ├── CpuManager.java
│ │ │ │ ├── DequeueTask.java
│ │ │ │ ├── DeviceBoot.java
│ │ │ │ ├── DeviceShutdown.java
│ │ │ │ ├── GroupChatInviteQueuedParticipantsTask.java
│ │ │ │ ├── LauncherUtils.java
│ │ │ │ ├── RcsCoreService.java
│ │ │ │ ├── RcsServiceControlReceiver.java
│ │ │ │ ├── StartService.java
│ │ │ │ ├── api/
│ │ │ │ │ ├── CapabilityServiceImpl.java
│ │ │ │ │ ├── ChatMessageImpl.java
│ │ │ │ │ ├── ChatServiceConfigurationImpl.java
│ │ │ │ │ ├── ChatServiceImpl.java
│ │ │ │ │ ├── CommonServiceConfigurationImpl.java
│ │ │ │ │ ├── ContactServiceImpl.java
│ │ │ │ │ ├── ExceptionUtil.java
│ │ │ │ │ ├── FileTransferServiceConfigurationImpl.java
│ │ │ │ │ ├── FileTransferServiceImpl.java
│ │ │ │ │ ├── FileUploadImpl.java
│ │ │ │ │ ├── FileUploadServiceConfigurationImpl.java
│ │ │ │ │ ├── FileUploadServiceImpl.java
│ │ │ │ │ ├── FileUploadStorageAccessor.java
│ │ │ │ │ ├── GeolocSharingImpl.java
│ │ │ │ │ ├── GeolocSharingServiceImpl.java
│ │ │ │ │ ├── GroupChatImpl.java
│ │ │ │ │ ├── GroupFileTransferImpl.java
│ │ │ │ │ ├── HistoryServiceImpl.java
│ │ │ │ │ ├── ImageSharingImpl.java
│ │ │ │ │ ├── ImageSharingServiceConfigurationImpl.java
│ │ │ │ │ ├── ImageSharingServiceImpl.java
│ │ │ │ │ ├── MultimediaMessagingSessionImpl.java
│ │ │ │ │ ├── MultimediaSessionServiceConfigurationImpl.java
│ │ │ │ │ ├── MultimediaSessionServiceImpl.java
│ │ │ │ │ ├── MultimediaSessionStorageAccessor.java
│ │ │ │ │ ├── MultimediaStreamingSessionImpl.java
│ │ │ │ │ ├── OneToOneChatImpl.java
│ │ │ │ │ ├── OneToOneDeliveryExpirationService.java
│ │ │ │ │ ├── OneToOneFileTransferImpl.java
│ │ │ │ │ ├── ServerApiBaseException.java
│ │ │ │ │ ├── ServerApiGenericException.java
│ │ │ │ │ ├── ServerApiIllegalArgumentException.java
│ │ │ │ │ ├── ServerApiMaxAllowedSessionLimitReachedException.java
│ │ │ │ │ ├── ServerApiPermissionDeniedException.java
│ │ │ │ │ ├── ServerApiPersistentStorageException.java
│ │ │ │ │ ├── ServerApiServiceNotRegisteredException.java
│ │ │ │ │ ├── ServerApiUnsupportedOperationException.java
│ │ │ │ │ ├── ServerApiUtils.java
│ │ │ │ │ ├── VideoSharingImpl.java
│ │ │ │ │ ├── VideoSharingServiceConfigurationImpl.java
│ │ │ │ │ └── VideoSharingServiceImpl.java
│ │ │ │ ├── broadcaster/
│ │ │ │ │ ├── CapabilitiesBroadcaster.java
│ │ │ │ │ ├── FileUploadEventBroadcaster.java
│ │ │ │ │ ├── GeolocSharingEventBroadcaster.java
│ │ │ │ │ ├── GroupChatEventBroadcaster.java
│ │ │ │ │ ├── GroupFileTransferBroadcaster.java
│ │ │ │ │ ├── IFileUploadEventBroadcaster.java
│ │ │ │ │ ├── IGeolocSharingEventBroadcaster.java
│ │ │ │ │ ├── IGroupChatEventBroadcaster.java
│ │ │ │ │ ├── IGroupFileTransferBroadcaster.java
│ │ │ │ │ ├── IImageSharingEventBroadcaster.java
│ │ │ │ │ ├── IMultimediaMessagingSessionEventBroadcaster.java
│ │ │ │ │ ├── IMultimediaStreamingSessionEventBroadcaster.java
│ │ │ │ │ ├── IOneToOneChatEventBroadcaster.java
│ │ │ │ │ ├── IOneToOneFileTransferBroadcaster.java
│ │ │ │ │ ├── IRcsServiceRegistrationEventBroadcaster.java
│ │ │ │ │ ├── IVideoSharingEventBroadcaster.java
│ │ │ │ │ ├── ImageSharingEventBroadcaster.java
│ │ │ │ │ ├── MultimediaMessagingSessionEventBroadcaster.java
│ │ │ │ │ ├── MultimediaStreamingSessionEventBroadcaster.java
│ │ │ │ │ ├── OneToOneChatEventBroadcaster.java
│ │ │ │ │ ├── OneToOneFileTransferBroadcaster.java
│ │ │ │ │ ├── RcsServiceRegistrationEventBroadcaster.java
│ │ │ │ │ └── VideoSharingEventBroadcaster.java
│ │ │ │ └── permissions/
│ │ │ │ ├── PermissionsAlertDialog.java
│ │ │ │ └── PermissionsManager.java
│ │ │ └── utils/
│ │ │ ├── Base64.java
│ │ │ ├── CloseableUtils.java
│ │ │ ├── ContactUtil.java
│ │ │ ├── DatabaseUtils.java
│ │ │ ├── DateUtils.java
│ │ │ ├── DeviceUtils.java
│ │ │ ├── FifoBuffer.java
│ │ │ ├── FileUtils.java
│ │ │ ├── HexadecimalUtils.java
│ │ │ ├── IdGenerator.java
│ │ │ ├── InetAddressUtils.java
│ │ │ ├── IntentUtils.java
│ │ │ ├── IpAddressUtils.java
│ │ │ ├── MimeManager.java
│ │ │ ├── NetworkRessourceManager.java
│ │ │ ├── NetworkUtils.java
│ │ │ ├── PeriodicRefresher.java
│ │ │ ├── PhoneUtils.java
│ │ │ ├── StorageUtils.java
│ │ │ ├── StringUtils.java
│ │ │ ├── TimerUtils.java
│ │ │ └── logger/
│ │ │ ├── Appender.java
│ │ │ └── Logger.java
│ │ └── telekom/
│ │ └── bouncycastle/
│ │ └── wrapper/
│ │ └── SimpleContentSignerBuilder.java
│ └── tests/
│ ├── .gitignore
│ ├── AndroidManifest.xml
│ ├── ant.properties
│ ├── default.properties
│ ├── project.properties
│ ├── res/
│ │ └── .gitignore
│ └── src/
│ └── com/
│ └── gsma/
│ ├── rcs/
│ │ ├── InitTest.java
│ │ ├── RcsSettingsMock.java
│ │ ├── chat/
│ │ │ ├── ChatSdpUtilsTest.java
│ │ │ ├── ConferenceInfoParserTest.java
│ │ │ ├── ImdnParserTest.java
│ │ │ ├── IsComposingParserTest.java
│ │ │ └── ResourceListParserTest.java
│ │ ├── contact/
│ │ │ └── ContactManagerTest.java
│ │ ├── cpim/
│ │ │ ├── CpimIdentityTest.java
│ │ │ └── CpimParserTest.java
│ │ ├── dns/
│ │ │ └── DnsTest.java
│ │ ├── im/
│ │ │ ├── chat/
│ │ │ │ ├── ChatLogTest.java
│ │ │ │ ├── ChatMessageTest.java
│ │ │ │ ├── GeolocInfoXmlParserTest.java
│ │ │ │ └── MessageLogTest.java
│ │ │ └── filetransfer/
│ │ │ ├── FileTransferLogTest.java
│ │ │ ├── FileTransferSdpUtilsTest.java
│ │ │ └── FileTransferXmlParserTest.java
│ │ ├── provider/
│ │ │ └── UserProfilePersistedStorageUtilTest.java
│ │ ├── richcall/
│ │ │ └── VideoSdpTest.java
│ │ └── utils/
│ │ ├── Base64Test.java
│ │ ├── ContactUtilMockContext.java
│ │ ├── DateUtilsTest.java
│ │ ├── IpAddressUtilsTest.java
│ │ └── NetworkRessourceMangerTest.java
│ └── service/
│ └── rcs/
│ ├── GeolocTest.java
│ ├── capabilities/
│ │ ├── CapabilitiesTest.java
│ │ └── ServiceExtensionManagerTest.java
│ ├── contacts/
│ │ ├── ContactUtilTest.java
│ │ ├── ContactUtilsTest.java
│ │ └── RcsContactTest.java
│ ├── history/
│ │ └── HistoryLogTest.java
│ └── upload/
│ └── FileUploadInfoTest.java
├── docs/
│ ├── RCSJTA_open_source.ppt
│ ├── SUPPORTED-STANDARDS.txt
│ └── tapi/
│ ├── CR_1.6/
│ │ ├── RCSJTA_T-API1.6_CR001_AudioMessage-R1.doc
│ │ ├── RCSJTA_T-API1.6_CR001_AudioMessage-R2.doc
│ │ ├── RCSJTA_T-API1.6_CR001_AudioMessage_old.doc
│ │ ├── RCSJTA_T-API1.6_CR002_ReferenceToCranePriorityRelease-R1.doc
│ │ ├── RCSJTA_T-API1.6_CR002_ReferenceToCranePriorityRelease-R2.doc
│ │ ├── RCSJTA_T-API1.6_CR003_Capability_Discovery-R1.doc
│ │ ├── RCSJTA_TT_BB_CR001_EnrichCalling_R5.doc
│ │ └── RCSJTA_TT_BB_CR001_EnrichCalling_R6.doc
│ ├── RCC.53_CR1005_1.6.1.docx
│ ├── RCC.53_v3.0_1.5.1-r1.docx
│ ├── RCC.53_v3.0_1.5.1.docx
│ └── wiki/
│ ├── TAPI_architecture.ppt
│ ├── TAPI_capabilities_contact.ppt
│ └── TAPI_chat.ppt
├── eclipse/
│ ├── README.txt
│ ├── android-eclipse-formatting.xml
│ └── android.importorder
├── gradle/
│ └── wrapper/
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libs/
│ ├── api/
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ ├── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── aidl/
│ │ │ │ └── com/
│ │ │ │ └── gsma/
│ │ │ │ └── services/
│ │ │ │ └── rcs/
│ │ │ │ ├── Geoloc.aidl
│ │ │ │ ├── ICommonServiceConfiguration.aidl
│ │ │ │ ├── IRcsServiceRegistrationListener.aidl
│ │ │ │ ├── RcsServiceRegistration.aidl
│ │ │ │ ├── capability/
│ │ │ │ │ ├── Capabilities.aidl
│ │ │ │ │ ├── ICapabilitiesListener.aidl
│ │ │ │ │ └── ICapabilityService.aidl
│ │ │ │ ├── chat/
│ │ │ │ │ ├── IChatMessage.aidl
│ │ │ │ │ ├── IChatService.aidl
│ │ │ │ │ ├── IChatServiceConfiguration.aidl
│ │ │ │ │ ├── IGroupChat.aidl
│ │ │ │ │ ├── IGroupChatListener.aidl
│ │ │ │ │ ├── IOneToOneChat.aidl
│ │ │ │ │ └── IOneToOneChatListener.aidl
│ │ │ │ ├── contact/
│ │ │ │ │ ├── ContactId.aidl
│ │ │ │ │ ├── IContactService.aidl
│ │ │ │ │ └── RcsContact.aidl
│ │ │ │ ├── extension/
│ │ │ │ │ ├── IMultimediaMessagingSession.aidl
│ │ │ │ │ ├── IMultimediaMessagingSessionListener.aidl
│ │ │ │ │ ├── IMultimediaSessionService.aidl
│ │ │ │ │ ├── IMultimediaSessionServiceConfiguration.aidl
│ │ │ │ │ ├── IMultimediaStreamingSession.aidl
│ │ │ │ │ └── IMultimediaStreamingSessionListener.aidl
│ │ │ │ ├── filetransfer/
│ │ │ │ │ ├── IFileTransfer.aidl
│ │ │ │ │ ├── IFileTransferService.aidl
│ │ │ │ │ ├── IFileTransferServiceConfiguration.aidl
│ │ │ │ │ ├── IGroupFileTransferListener.aidl
│ │ │ │ │ └── IOneToOneFileTransferListener.aidl
│ │ │ │ ├── history/
│ │ │ │ │ └── IHistoryService.aidl
│ │ │ │ ├── sharing/
│ │ │ │ │ ├── geoloc/
│ │ │ │ │ │ ├── IGeolocSharing.aidl
│ │ │ │ │ │ ├── IGeolocSharingListener.aidl
│ │ │ │ │ │ └── IGeolocSharingService.aidl
│ │ │ │ │ ├── image/
│ │ │ │ │ │ ├── IImageSharing.aidl
│ │ │ │ │ │ ├── IImageSharingListener.aidl
│ │ │ │ │ │ ├── IImageSharingService.aidl
│ │ │ │ │ │ └── IImageSharingServiceConfiguration.aidl
│ │ │ │ │ └── video/
│ │ │ │ │ ├── IVideoPlayer.aidl
│ │ │ │ │ ├── IVideoSharing.aidl
│ │ │ │ │ ├── IVideoSharingListener.aidl
│ │ │ │ │ ├── IVideoSharingService.aidl
│ │ │ │ │ ├── IVideoSharingServiceConfiguration.aidl
│ │ │ │ │ ├── VideoCodec.aidl
│ │ │ │ │ └── VideoDescriptor.aidl
│ │ │ │ └── upload/
│ │ │ │ ├── FileUploadInfo.aidl
│ │ │ │ ├── IFileUpload.aidl
│ │ │ │ ├── IFileUploadListener.aidl
│ │ │ │ ├── IFileUploadService.aidl
│ │ │ │ └── IFileUploadServiceConfiguration.aidl
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── gsma/
│ │ │ └── services/
│ │ │ └── rcs/
│ │ │ ├── CommonServiceConfiguration.java
│ │ │ ├── Geoloc.java
│ │ │ ├── Intents.java
│ │ │ ├── RcsGenericException.java
│ │ │ ├── RcsIllegalArgumentException.java
│ │ │ ├── RcsMaxAllowedSessionLimitReachedException.java
│ │ │ ├── RcsPermissionDeniedException.java
│ │ │ ├── RcsPersistentStorageException.java
│ │ │ ├── RcsService.java
│ │ │ ├── RcsServiceControl.java
│ │ │ ├── RcsServiceException.java
│ │ │ ├── RcsServiceListener.java
│ │ │ ├── RcsServiceNotAvailableException.java
│ │ │ ├── RcsServiceNotRegisteredException.java
│ │ │ ├── RcsServiceRegistration.java
│ │ │ ├── RcsServiceRegistrationListener.java
│ │ │ ├── RcsServiceRegistrationListenerImpl.java
│ │ │ ├── RcsUnsupportedOperationException.java
│ │ │ ├── capability/
│ │ │ │ ├── Capabilities.java
│ │ │ │ ├── CapabilitiesListener.java
│ │ │ │ ├── CapabilitiesListenerImpl.java
│ │ │ │ ├── CapabilitiesLog.java
│ │ │ │ ├── CapabilityService.java
│ │ │ │ └── package-info.java
│ │ │ ├── chat/
│ │ │ │ ├── ChatLog.java
│ │ │ │ ├── ChatMessage.java
│ │ │ │ ├── ChatService.java
│ │ │ │ ├── ChatServiceConfiguration.java
│ │ │ │ ├── GroupChat.java
│ │ │ │ ├── GroupChatIntent.java
│ │ │ │ ├── GroupChatListener.java
│ │ │ │ ├── GroupChatListenerImpl.java
│ │ │ │ ├── OneToOneChat.java
│ │ │ │ ├── OneToOneChatIntent.java
│ │ │ │ ├── OneToOneChatListener.java
│ │ │ │ ├── OneToOneChatListenerImpl.java
│ │ │ │ └── package-info.java
│ │ │ ├── contact/
│ │ │ │ ├── ContactId.java
│ │ │ │ ├── ContactProvider.java
│ │ │ │ ├── ContactService.java
│ │ │ │ ├── ContactUtil.java
│ │ │ │ ├── RcsContact.java
│ │ │ │ └── package-info.java
│ │ │ ├── extension/
│ │ │ │ ├── InstantMultimediaMessageIntent.java
│ │ │ │ ├── MultimediaMessagingSession.java
│ │ │ │ ├── MultimediaMessagingSessionIntent.java
│ │ │ │ ├── MultimediaMessagingSessionListener.java
│ │ │ │ ├── MultimediaMessagingSessionListenerImpl.java
│ │ │ │ ├── MultimediaSession.java
│ │ │ │ ├── MultimediaSessionService.java
│ │ │ │ ├── MultimediaSessionServiceConfiguration.java
│ │ │ │ ├── MultimediaStreamingSession.java
│ │ │ │ ├── MultimediaStreamingSessionIntent.java
│ │ │ │ ├── MultimediaStreamingSessionListener.java
│ │ │ │ ├── MultimediaStreamingSessionListenerImpl.java
│ │ │ │ └── package-info.java
│ │ │ ├── filetransfer/
│ │ │ │ ├── FileTransfer.java
│ │ │ │ ├── FileTransferIntent.java
│ │ │ │ ├── FileTransferLog.java
│ │ │ │ ├── FileTransferService.java
│ │ │ │ ├── FileTransferServiceConfiguration.java
│ │ │ │ ├── GroupFileTransferListener.java
│ │ │ │ ├── GroupFileTransferListenerImpl.java
│ │ │ │ ├── OneToOneFileTransferListener.java
│ │ │ │ ├── OneToOneFileTransferListenerImpl.java
│ │ │ │ └── package-info.java
│ │ │ ├── groupdelivery/
│ │ │ │ ├── GroupDeliveryInfo.java
│ │ │ │ └── GroupDeliveryInfoLog.java
│ │ │ ├── history/
│ │ │ │ ├── HistoryLog.java
│ │ │ │ ├── HistoryService.java
│ │ │ │ └── HistoryUriBuilder.java
│ │ │ ├── package-info.java
│ │ │ ├── sharing/
│ │ │ │ ├── geoloc/
│ │ │ │ │ ├── GeolocSharing.java
│ │ │ │ │ ├── GeolocSharingIntent.java
│ │ │ │ │ ├── GeolocSharingListener.java
│ │ │ │ │ ├── GeolocSharingListenerImpl.java
│ │ │ │ │ ├── GeolocSharingLog.java
│ │ │ │ │ ├── GeolocSharingService.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── image/
│ │ │ │ │ ├── ImageSharing.java
│ │ │ │ │ ├── ImageSharingIntent.java
│ │ │ │ │ ├── ImageSharingListener.java
│ │ │ │ │ ├── ImageSharingListenerImpl.java
│ │ │ │ │ ├── ImageSharingLog.java
│ │ │ │ │ ├── ImageSharingService.java
│ │ │ │ │ ├── ImageSharingServiceConfiguration.java
│ │ │ │ │ └── package-info.java
│ │ │ │ └── video/
│ │ │ │ ├── VideoCodec.java
│ │ │ │ ├── VideoDescriptor.java
│ │ │ │ ├── VideoPlayer.java
│ │ │ │ ├── VideoPlayerImpl.java
│ │ │ │ ├── VideoSharing.java
│ │ │ │ ├── VideoSharingIntent.java
│ │ │ │ ├── VideoSharingListener.java
│ │ │ │ ├── VideoSharingListenerImpl.java
│ │ │ │ ├── VideoSharingLog.java
│ │ │ │ ├── VideoSharingService.java
│ │ │ │ ├── VideoSharingServiceConfiguration.java
│ │ │ │ └── package-info.java
│ │ │ └── upload/
│ │ │ ├── FileUpload.java
│ │ │ ├── FileUploadInfo.java
│ │ │ ├── FileUploadListener.java
│ │ │ ├── FileUploadListenerImpl.java
│ │ │ ├── FileUploadService.java
│ │ │ ├── FileUploadServiceConfiguration.java
│ │ │ └── package-info.java
│ │ └── version/
│ │ ├── blackbird_1_5_1.xml
│ │ └── crane_1_6_1.xml
│ ├── api_cnx/
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── gsma/
│ │ │ └── rcs/
│ │ │ └── api/
│ │ │ └── connection/
│ │ │ ├── ConnectionManager.java
│ │ │ ├── IRcsActivityFinishable.java
│ │ │ └── utils/
│ │ │ ├── DialogUtil.java
│ │ │ ├── ExceptionUtil.java
│ │ │ ├── IConnectionManager.java
│ │ │ ├── IRcsDialog.java
│ │ │ ├── LockAccess.java
│ │ │ ├── LogUtils.java
│ │ │ ├── RcsActivity.java
│ │ │ ├── RcsFragmentActivity.java
│ │ │ ├── RcsListActivity.java
│ │ │ ├── RcsPreferenceActivity.java
│ │ │ └── TimerUtils.java
│ │ └── res/
│ │ └── values/
│ │ └── strings.xml
│ ├── bouncycastle/
│ │ ├── AndroidManifestLibrary.xml
│ │ ├── build.gradle
│ │ └── src/
│ │ └── local/
│ │ └── org/
│ │ └── bouncycastle/
│ │ ├── asn1/
│ │ │ ├── ASN1ApplicationSpecificParser.java
│ │ │ ├── ASN1Boolean.java
│ │ │ ├── ASN1Choice.java
│ │ │ ├── ASN1Encodable.java
│ │ │ ├── ASN1EncodableVector.java
│ │ │ ├── ASN1Encoding.java
│ │ │ ├── ASN1Enumerated.java
│ │ │ ├── ASN1Exception.java
│ │ │ ├── ASN1GeneralizedTime.java
│ │ │ ├── ASN1Generator.java
│ │ │ ├── ASN1InputStream.java
│ │ │ ├── ASN1Integer.java
│ │ │ ├── ASN1Null.java
│ │ │ ├── ASN1Object.java
│ │ │ ├── ASN1ObjectIdentifier.java
│ │ │ ├── ASN1OctetString.java
│ │ │ ├── ASN1OctetStringParser.java
│ │ │ ├── ASN1OutputStream.java
│ │ │ ├── ASN1ParsingException.java
│ │ │ ├── ASN1Primitive.java
│ │ │ ├── ASN1Sequence.java
│ │ │ ├── ASN1SequenceParser.java
│ │ │ ├── ASN1Set.java
│ │ │ ├── ASN1SetParser.java
│ │ │ ├── ASN1StreamParser.java
│ │ │ ├── ASN1String.java
│ │ │ ├── ASN1TaggedObject.java
│ │ │ ├── ASN1TaggedObjectParser.java
│ │ │ ├── ASN1UTCTime.java
│ │ │ ├── BERApplicationSpecific.java
│ │ │ ├── BERApplicationSpecificParser.java
│ │ │ ├── BERConstructedOctetString.java
│ │ │ ├── BERFactory.java
│ │ │ ├── BERGenerator.java
│ │ │ ├── BEROctetString.java
│ │ │ ├── BEROctetStringGenerator.java
│ │ │ ├── BEROctetStringParser.java
│ │ │ ├── BEROutputStream.java
│ │ │ ├── BERSequence.java
│ │ │ ├── BERSequenceGenerator.java
│ │ │ ├── BERSequenceParser.java
│ │ │ ├── BERSet.java
│ │ │ ├── BERSetParser.java
│ │ │ ├── BERTaggedObject.java
│ │ │ ├── BERTaggedObjectParser.java
│ │ │ ├── BERTags.java
│ │ │ ├── ConstructedOctetStream.java
│ │ │ ├── DERApplicationSpecific.java
│ │ │ ├── DERBMPString.java
│ │ │ ├── DERBitString.java
│ │ │ ├── DERBoolean.java
│ │ │ ├── DEREncodableVector.java
│ │ │ ├── DEREnumerated.java
│ │ │ ├── DERExternal.java
│ │ │ ├── DERExternalParser.java
│ │ │ ├── DERFactory.java
│ │ │ ├── DERGeneralString.java
│ │ │ ├── DERGeneralizedTime.java
│ │ │ ├── DERGenerator.java
│ │ │ ├── DERIA5String.java
│ │ │ ├── DERInteger.java
│ │ │ ├── DERNull.java
│ │ │ ├── DERNumericString.java
│ │ │ ├── DERObjectIdentifier.java
│ │ │ ├── DEROctetString.java
│ │ │ ├── DEROctetStringParser.java
│ │ │ ├── DEROutputStream.java
│ │ │ ├── DERPrintableString.java
│ │ │ ├── DERSequence.java
│ │ │ ├── DERSequenceGenerator.java
│ │ │ ├── DERSequenceParser.java
│ │ │ ├── DERSet.java
│ │ │ ├── DERSetParser.java
│ │ │ ├── DERT61String.java
│ │ │ ├── DERT61UTF8String.java
│ │ │ ├── DERTaggedObject.java
│ │ │ ├── DERTags.java
│ │ │ ├── DERUTCTime.java
│ │ │ ├── DERUTF8String.java
│ │ │ ├── DERUniversalString.java
│ │ │ ├── DERVisibleString.java
│ │ │ ├── DLOutputStream.java
│ │ │ ├── DLSequence.java
│ │ │ ├── DLSet.java
│ │ │ ├── DLTaggedObject.java
│ │ │ ├── DefiniteLengthInputStream.java
│ │ │ ├── InMemoryRepresentable.java
│ │ │ ├── IndefiniteLengthInputStream.java
│ │ │ ├── LazyConstructionEnumeration.java
│ │ │ ├── LazyEncodedSequence.java
│ │ │ ├── LimitedInputStream.java
│ │ │ ├── OIDTokenizer.java
│ │ │ ├── StreamUtil.java
│ │ │ ├── cryptopro/
│ │ │ │ ├── CryptoProObjectIdentifiers.java
│ │ │ │ └── ECGOST3410NamedCurves.java
│ │ │ ├── kisa/
│ │ │ │ └── KISAObjectIdentifiers.java
│ │ │ ├── nist/
│ │ │ │ ├── NISTNamedCurves.java
│ │ │ │ └── NISTObjectIdentifiers.java
│ │ │ ├── ntt/
│ │ │ │ └── NTTObjectIdentifiers.java
│ │ │ ├── oiw/
│ │ │ │ └── OIWObjectIdentifiers.java
│ │ │ ├── pkcs/
│ │ │ │ ├── PKCSObjectIdentifiers.java
│ │ │ │ ├── PrivateKeyInfo.java
│ │ │ │ └── RSASSAPSSparams.java
│ │ │ ├── sec/
│ │ │ │ ├── SECNamedCurves.java
│ │ │ │ └── SECObjectIdentifiers.java
│ │ │ ├── teletrust/
│ │ │ │ ├── TeleTrusTNamedCurves.java
│ │ │ │ └── TeleTrusTObjectIdentifiers.java
│ │ │ ├── x500/
│ │ │ │ ├── AttributeTypeAndValue.java
│ │ │ │ ├── RDN.java
│ │ │ │ ├── X500Name.java
│ │ │ │ ├── X500NameBuilder.java
│ │ │ │ ├── X500NameStyle.java
│ │ │ │ └── style/
│ │ │ │ ├── BCStyle.java
│ │ │ │ ├── IETFUtils.java
│ │ │ │ └── X500NameTokenizer.java
│ │ │ ├── x509/
│ │ │ │ ├── AlgorithmIdentifier.java
│ │ │ │ ├── AttCertIssuer.java
│ │ │ │ ├── AttCertValidityPeriod.java
│ │ │ │ ├── Attribute.java
│ │ │ │ ├── AttributeCertificate.java
│ │ │ │ ├── AttributeCertificateInfo.java
│ │ │ │ ├── AuthorityKeyIdentifier.java
│ │ │ │ ├── BasicConstraints.java
│ │ │ │ ├── Certificate.java
│ │ │ │ ├── CertificateList.java
│ │ │ │ ├── DistributionPointName.java
│ │ │ │ ├── ExtendedKeyUsage.java
│ │ │ │ ├── Extension.java
│ │ │ │ ├── Extensions.java
│ │ │ │ ├── ExtensionsGenerator.java
│ │ │ │ ├── GeneralName.java
│ │ │ │ ├── GeneralNames.java
│ │ │ │ ├── Holder.java
│ │ │ │ ├── IssuerSerial.java
│ │ │ │ ├── IssuingDistributionPoint.java
│ │ │ │ ├── KeyPurposeId.java
│ │ │ │ ├── KeyUsage.java
│ │ │ │ ├── ObjectDigestInfo.java
│ │ │ │ ├── ReasonFlags.java
│ │ │ │ ├── SubjectKeyIdentifier.java
│ │ │ │ ├── SubjectPublicKeyInfo.java
│ │ │ │ ├── TBSCertList.java
│ │ │ │ ├── TBSCertificate.java
│ │ │ │ ├── Time.java
│ │ │ │ ├── V2Form.java
│ │ │ │ ├── V3TBSCertificateGenerator.java
│ │ │ │ ├── X509DefaultEntryConverter.java
│ │ │ │ ├── X509Extension.java
│ │ │ │ ├── X509Extensions.java
│ │ │ │ ├── X509Name.java
│ │ │ │ ├── X509NameEntryConverter.java
│ │ │ │ ├── X509NameTokenizer.java
│ │ │ │ └── X509ObjectIdentifiers.java
│ │ │ └── x9/
│ │ │ ├── X962NamedCurves.java
│ │ │ ├── X962Parameters.java
│ │ │ ├── X9Curve.java
│ │ │ ├── X9ECParameters.java
│ │ │ ├── X9ECParametersHolder.java
│ │ │ ├── X9ECPoint.java
│ │ │ ├── X9FieldElement.java
│ │ │ ├── X9FieldID.java
│ │ │ ├── X9IntegerConverter.java
│ │ │ └── X9ObjectIdentifiers.java
│ │ ├── cert/
│ │ │ ├── AttributeCertificateHolder.java
│ │ │ ├── AttributeCertificateIssuer.java
│ │ │ ├── CertException.java
│ │ │ ├── CertIOException.java
│ │ │ ├── CertRuntimeException.java
│ │ │ ├── CertUtils.java
│ │ │ ├── X509AttributeCertificateHolder.java
│ │ │ ├── X509CRLEntryHolder.java
│ │ │ ├── X509CRLHolder.java
│ │ │ ├── X509CertificateHolder.java
│ │ │ ├── X509ExtensionUtils.java
│ │ │ ├── X509v3CertificateBuilder.java
│ │ │ └── jcajce/
│ │ │ ├── CertHelper.java
│ │ │ ├── DefaultCertHelper.java
│ │ │ ├── JcaX509CertificateConverter.java
│ │ │ ├── JcaX509CertificateHolder.java
│ │ │ ├── JcaX509ExtensionUtils.java
│ │ │ ├── JcaX509v3CertificateBuilder.java
│ │ │ ├── NamedCertHelper.java
│ │ │ └── ProviderCertHelper.java
│ │ ├── crypto/
│ │ │ ├── CipherParameters.java
│ │ │ ├── Digest.java
│ │ │ ├── ExtendedDigest.java
│ │ │ ├── digests/
│ │ │ │ ├── GeneralDigest.java
│ │ │ │ └── SHA1Digest.java
│ │ │ ├── params/
│ │ │ │ ├── AsymmetricKeyParameter.java
│ │ │ │ ├── ECDomainParameters.java
│ │ │ │ ├── ECKeyParameters.java
│ │ │ │ ├── ECPrivateKeyParameters.java
│ │ │ │ └── ECPublicKeyParameters.java
│ │ │ └── util/
│ │ │ └── Pack.java
│ │ ├── math/
│ │ │ └── ec/
│ │ │ ├── ECConstants.java
│ │ │ ├── ECCurve.java
│ │ │ ├── ECFieldElement.java
│ │ │ ├── ECMultiplier.java
│ │ │ ├── ECPoint.java
│ │ │ ├── FpNafMultiplier.java
│ │ │ ├── IntArray.java
│ │ │ ├── PreCompInfo.java
│ │ │ ├── SimpleBigDecimal.java
│ │ │ ├── Tnaf.java
│ │ │ ├── WNafMultiplier.java
│ │ │ ├── WNafPreCompInfo.java
│ │ │ ├── WTauNafMultiplier.java
│ │ │ ├── WTauNafPreCompInfo.java
│ │ │ └── ZTauElement.java
│ │ ├── operator/
│ │ │ ├── ContentSigner.java
│ │ │ ├── ContentVerifier.java
│ │ │ ├── ContentVerifierProvider.java
│ │ │ ├── DefaultSignatureAlgorithmIdentifierFinder.java
│ │ │ ├── DigestCalculator.java
│ │ │ ├── DigestCalculatorProvider.java
│ │ │ ├── OperatorCreationException.java
│ │ │ ├── OperatorException.java
│ │ │ ├── OperatorStreamException.java
│ │ │ ├── RuntimeOperatorException.java
│ │ │ └── SignatureAlgorithmIdentifierFinder.java
│ │ └── util/
│ │ ├── Arrays.java
│ │ ├── IPAddress.java
│ │ ├── Memoable.java
│ │ ├── Selector.java
│ │ ├── Strings.java
│ │ ├── encoders/
│ │ │ ├── DecoderException.java
│ │ │ ├── Encoder.java
│ │ │ ├── EncoderException.java
│ │ │ ├── Hex.java
│ │ │ └── HexEncoder.java
│ │ └── io/
│ │ ├── StreamOverflowException.java
│ │ └── Streams.java
│ └── nist_sip/
│ ├── AndroidManifestLibrary.xml
│ ├── build.gradle
│ └── src/
│ ├── gov2/
│ │ └── nist/
│ │ ├── core/
│ │ │ ├── Debug.java
│ │ │ ├── DuplicateNameValueList.java
│ │ │ ├── GenericObject.java
│ │ │ ├── GenericObjectList.java
│ │ │ ├── Host.java
│ │ │ ├── HostNameParser.java
│ │ │ ├── HostPort.java
│ │ │ ├── InternalErrorHandler.java
│ │ │ ├── LexerCore.java
│ │ │ ├── LogLevels.java
│ │ │ ├── LogWriter.java
│ │ │ ├── Match.java
│ │ │ ├── MultiValueMap.java
│ │ │ ├── MultiValueMapImpl.java
│ │ │ ├── NameValue.java
│ │ │ ├── NameValueList.java
│ │ │ ├── PackageNames.java
│ │ │ ├── ParserCore.java
│ │ │ ├── Separators.java
│ │ │ ├── ServerLogger.java
│ │ │ ├── StackLogger.java
│ │ │ ├── StringTokenizer.java
│ │ │ ├── ThreadAuditor.java
│ │ │ ├── Token.java
│ │ │ └── net/
│ │ │ ├── AddressResolver.java
│ │ │ ├── DefaultNetworkLayer.java
│ │ │ ├── NetworkLayer.java
│ │ │ └── SslNetworkLayer.java
│ │ └── javax2/
│ │ └── sip/
│ │ ├── ClientTransactionExt.java
│ │ ├── DefaultAddressResolver.java
│ │ ├── DialogExt.java
│ │ ├── DialogFilter.java
│ │ ├── DialogTimeoutEvent.java
│ │ ├── EventScanner.java
│ │ ├── EventWrapper.java
│ │ ├── ListeningPointExt.java
│ │ ├── ListeningPointImpl.java
│ │ ├── LogRecord.java
│ │ ├── LogRecordFactory.java
│ │ ├── NistSipMessageFactoryImpl.java
│ │ ├── ResponseEventExt.java
│ │ ├── SIPConstants.java
│ │ ├── ServerTransactionExt.java
│ │ ├── SipListenerExt.java
│ │ ├── SipProviderExt.java
│ │ ├── SipProviderImpl.java
│ │ ├── SipStackExt.java
│ │ ├── SipStackImpl.java
│ │ ├── TransactionExt.java
│ │ ├── Utils.java
│ │ ├── UtilsExt.java
│ │ ├── address/
│ │ │ ├── AddressFactoryImpl.java
│ │ │ ├── AddressImpl.java
│ │ │ ├── Authority.java
│ │ │ ├── GenericURI.java
│ │ │ ├── NetObject.java
│ │ │ ├── NetObjectList.java
│ │ │ ├── ParameterNames.java
│ │ │ ├── RFC2396UrlDecoder.java
│ │ │ ├── RouterExt.java
│ │ │ ├── SipURIExt.java
│ │ │ ├── SipUri.java
│ │ │ ├── TelURLImpl.java
│ │ │ ├── TelephoneNumber.java
│ │ │ └── UserInfo.java
│ │ ├── clientauthutils/
│ │ │ ├── AccountManager.java
│ │ │ ├── AuthenticationHelper.java
│ │ │ ├── AuthenticationHelperImpl.java
│ │ │ ├── CredentialsCache.java
│ │ │ ├── MessageDigestAlgorithm.java
│ │ │ ├── SecureAccountManager.java
│ │ │ ├── UserCredentialHash.java
│ │ │ └── UserCredentials.java
│ │ ├── header/
│ │ │ ├── Accept.java
│ │ │ ├── AcceptEncoding.java
│ │ │ ├── AcceptEncodingList.java
│ │ │ ├── AcceptLanguage.java
│ │ │ ├── AcceptLanguageList.java
│ │ │ ├── AcceptList.java
│ │ │ ├── AddressParameters.java
│ │ │ ├── AddressParametersHeader.java
│ │ │ ├── AlertInfo.java
│ │ │ ├── AlertInfoList.java
│ │ │ ├── Allow.java
│ │ │ ├── AllowEvents.java
│ │ │ ├── AllowEventsList.java
│ │ │ ├── AllowList.java
│ │ │ ├── AuthenticationHeader.java
│ │ │ ├── AuthenticationInfo.java
│ │ │ ├── AuthenticationInfoList.java
│ │ │ ├── Authorization.java
│ │ │ ├── AuthorizationList.java
│ │ │ ├── CSeq.java
│ │ │ ├── CallID.java
│ │ │ ├── CallIdentifier.java
│ │ │ ├── CallInfo.java
│ │ │ ├── CallInfoList.java
│ │ │ ├── Challenge.java
│ │ │ ├── Contact.java
│ │ │ ├── ContactList.java
│ │ │ ├── ContentDisposition.java
│ │ │ ├── ContentEncoding.java
│ │ │ ├── ContentEncodingList.java
│ │ │ ├── ContentLanguage.java
│ │ │ ├── ContentLanguageList.java
│ │ │ ├── ContentLength.java
│ │ │ ├── ContentType.java
│ │ │ ├── Credentials.java
│ │ │ ├── ErrorInfo.java
│ │ │ ├── ErrorInfoList.java
│ │ │ ├── Event.java
│ │ │ ├── Expires.java
│ │ │ ├── ExtensionHeaderImpl.java
│ │ │ ├── ExtensionHeaderList.java
│ │ │ ├── From.java
│ │ │ ├── HeaderExt.java
│ │ │ ├── HeaderFactoryExt.java
│ │ │ ├── HeaderFactoryImpl.java
│ │ │ ├── InReplyTo.java
│ │ │ ├── InReplyToList.java
│ │ │ ├── Indentation.java
│ │ │ ├── MaxForwards.java
│ │ │ ├── MediaRange.java
│ │ │ ├── MimeVersion.java
│ │ │ ├── MinExpires.java
│ │ │ ├── NameMap.java
│ │ │ ├── Organization.java
│ │ │ ├── ParameterNames.java
│ │ │ ├── ParametersHeader.java
│ │ │ ├── Priority.java
│ │ │ ├── Protocol.java
│ │ │ ├── ProxyAuthenticate.java
│ │ │ ├── ProxyAuthenticateList.java
│ │ │ ├── ProxyAuthorization.java
│ │ │ ├── ProxyAuthorizationList.java
│ │ │ ├── ProxyRequire.java
│ │ │ ├── ProxyRequireList.java
│ │ │ ├── RAck.java
│ │ │ ├── RSeq.java
│ │ │ ├── Reason.java
│ │ │ ├── ReasonList.java
│ │ │ ├── RecordRoute.java
│ │ │ ├── RecordRouteList.java
│ │ │ ├── ReferTo.java
│ │ │ ├── ReplyTo.java
│ │ │ ├── RequestLine.java
│ │ │ ├── Require.java
│ │ │ ├── RequireList.java
│ │ │ ├── RetryAfter.java
│ │ │ ├── Route.java
│ │ │ ├── RouteList.java
│ │ │ ├── SIPDate.java
│ │ │ ├── SIPDateHeader.java
│ │ │ ├── SIPETag.java
│ │ │ ├── SIPHeader.java
│ │ │ ├── SIPHeaderList.java
│ │ │ ├── SIPHeaderNames.java
│ │ │ ├── SIPHeaderNamesCache.java
│ │ │ ├── SIPIfMatch.java
│ │ │ ├── SIPObject.java
│ │ │ ├── SIPObjectList.java
│ │ │ ├── Server.java
│ │ │ ├── SipRequestLine.java
│ │ │ ├── SipStatusLine.java
│ │ │ ├── StatusLine.java
│ │ │ ├── Subject.java
│ │ │ ├── SubscriptionState.java
│ │ │ ├── Supported.java
│ │ │ ├── SupportedList.java
│ │ │ ├── TimeStamp.java
│ │ │ ├── To.java
│ │ │ ├── Unsupported.java
│ │ │ ├── UnsupportedList.java
│ │ │ ├── UserAgent.java
│ │ │ ├── Via.java
│ │ │ ├── ViaHeaderExt.java
│ │ │ ├── ViaList.java
│ │ │ ├── WWWAuthenticate.java
│ │ │ ├── WWWAuthenticateList.java
│ │ │ ├── Warning.java
│ │ │ ├── WarningList.java
│ │ │ ├── extensions/
│ │ │ │ ├── Join.java
│ │ │ │ ├── JoinHeader.java
│ │ │ │ ├── MinSE.java
│ │ │ │ ├── MinSEHeader.java
│ │ │ │ ├── References.java
│ │ │ │ ├── ReferencesHeader.java
│ │ │ │ ├── ReferredBy.java
│ │ │ │ ├── ReferredByHeader.java
│ │ │ │ ├── Replaces.java
│ │ │ │ ├── ReplacesHeader.java
│ │ │ │ ├── SessionExpires.java
│ │ │ │ └── SessionExpiresHeader.java
│ │ │ └── ims/
│ │ │ ├── AddressHeaderIms.java
│ │ │ ├── AuthorizationHeaderIms.java
│ │ │ ├── PAccessNetworkInfo.java
│ │ │ ├── PAccessNetworkInfoHeader.java
│ │ │ ├── PAssertedIdentity.java
│ │ │ ├── PAssertedIdentityHeader.java
│ │ │ ├── PAssertedIdentityList.java
│ │ │ ├── PAssertedService.java
│ │ │ ├── PAssertedServiceHeader.java
│ │ │ ├── PAssociatedURI.java
│ │ │ ├── PAssociatedURIHeader.java
│ │ │ ├── PAssociatedURIList.java
│ │ │ ├── PCalledPartyID.java
│ │ │ ├── PCalledPartyIDHeader.java
│ │ │ ├── PChargingFunctionAddresses.java
│ │ │ ├── PChargingFunctionAddressesHeader.java
│ │ │ ├── PChargingVector.java
│ │ │ ├── PChargingVectorHeader.java
│ │ │ ├── PMediaAuthorization.java
│ │ │ ├── PMediaAuthorizationHeader.java
│ │ │ ├── PMediaAuthorizationList.java
│ │ │ ├── PPreferredIdentity.java
│ │ │ ├── PPreferredIdentityHeader.java
│ │ │ ├── PPreferredService.java
│ │ │ ├── PPreferredServiceHeader.java
│ │ │ ├── PProfileKey.java
│ │ │ ├── PProfileKeyHeader.java
│ │ │ ├── PServedUser.java
│ │ │ ├── PServedUserHeader.java
│ │ │ ├── PUserDatabase.java
│ │ │ ├── PUserDatabaseHeader.java
│ │ │ ├── PVisitedNetworkID.java
│ │ │ ├── PVisitedNetworkIDHeader.java
│ │ │ ├── PVisitedNetworkIDList.java
│ │ │ ├── ParameterNamesIms.java
│ │ │ ├── Path.java
│ │ │ ├── PathHeader.java
│ │ │ ├── PathList.java
│ │ │ ├── Privacy.java
│ │ │ ├── PrivacyHeader.java
│ │ │ ├── PrivacyList.java
│ │ │ ├── SIPHeaderNamesIms.java
│ │ │ ├── SecurityAgree.java
│ │ │ ├── SecurityAgreeHeader.java
│ │ │ ├── SecurityClient.java
│ │ │ ├── SecurityClientHeader.java
│ │ │ ├── SecurityClientList.java
│ │ │ ├── SecurityServer.java
│ │ │ ├── SecurityServerHeader.java
│ │ │ ├── SecurityServerList.java
│ │ │ ├── SecurityVerify.java
│ │ │ ├── SecurityVerifyHeader.java
│ │ │ ├── SecurityVerifyList.java
│ │ │ ├── ServiceRoute.java
│ │ │ ├── ServiceRouteHeader.java
│ │ │ ├── ServiceRouteList.java
│ │ │ └── WWWAuthenticateHeaderIms.java
│ │ ├── message/
│ │ │ ├── Content.java
│ │ │ ├── ContentImpl.java
│ │ │ ├── HeaderIterator.java
│ │ │ ├── ListMap.java
│ │ │ ├── MessageExt.java
│ │ │ ├── MessageFactoryExt.java
│ │ │ ├── MessageFactoryImpl.java
│ │ │ ├── MessageObject.java
│ │ │ ├── MultipartMimeContent.java
│ │ │ ├── MultipartMimeContentImpl.java
│ │ │ ├── RequestExt.java
│ │ │ ├── ResponseExt.java
│ │ │ ├── SIPDuplicateHeaderException.java
│ │ │ ├── SIPMessage.java
│ │ │ ├── SIPRequest.java
│ │ │ └── SIPResponse.java
│ │ ├── parser/
│ │ │ ├── AcceptEncodingParser.java
│ │ │ ├── AcceptLanguageParser.java
│ │ │ ├── AcceptParser.java
│ │ │ ├── AddressParametersParser.java
│ │ │ ├── AddressParser.java
│ │ │ ├── AlertInfoParser.java
│ │ │ ├── AllowEventsParser.java
│ │ │ ├── AllowParser.java
│ │ │ ├── AuthenticationInfoParser.java
│ │ │ ├── AuthorizationParser.java
│ │ │ ├── CSeqParser.java
│ │ │ ├── CallIDParser.java
│ │ │ ├── CallInfoParser.java
│ │ │ ├── ChallengeParser.java
│ │ │ ├── ContactParser.java
│ │ │ ├── ContentDispositionParser.java
│ │ │ ├── ContentEncodingParser.java
│ │ │ ├── ContentLanguageParser.java
│ │ │ ├── ContentLengthParser.java
│ │ │ ├── ContentTypeParser.java
│ │ │ ├── DateParser.java
│ │ │ ├── ErrorInfoParser.java
│ │ │ ├── EventParser.java
│ │ │ ├── ExpiresParser.java
│ │ │ ├── FromParser.java
│ │ │ ├── HeaderParser.java
│ │ │ ├── InReplyToParser.java
│ │ │ ├── Lexer.java
│ │ │ ├── MaxForwardsParser.java
│ │ │ ├── MimeVersionParser.java
│ │ │ ├── MinExpiresParser.java
│ │ │ ├── OrganizationParser.java
│ │ │ ├── ParametersParser.java
│ │ │ ├── ParseExceptionListener.java
│ │ │ ├── Parser.java
│ │ │ ├── ParserFactory.java
│ │ │ ├── Pipeline.java
│ │ │ ├── PipelinedMsgParser.java
│ │ │ ├── PriorityParser.java
│ │ │ ├── ProxyAuthenticateParser.java
│ │ │ ├── ProxyAuthorizationParser.java
│ │ │ ├── ProxyRequireParser.java
│ │ │ ├── RAckParser.java
│ │ │ ├── RSeqParser.java
│ │ │ ├── ReasonParser.java
│ │ │ ├── RecordRouteParser.java
│ │ │ ├── ReferToParser.java
│ │ │ ├── ReplyToParser.java
│ │ │ ├── RequestLineParser.java
│ │ │ ├── RequireParser.java
│ │ │ ├── RetryAfterParser.java
│ │ │ ├── RouteParser.java
│ │ │ ├── SIPETagParser.java
│ │ │ ├── SIPIfMatchParser.java
│ │ │ ├── SIPMessageListener.java
│ │ │ ├── ServerParser.java
│ │ │ ├── StatusLineParser.java
│ │ │ ├── StringMsgParser.java
│ │ │ ├── SubjectParser.java
│ │ │ ├── SubscriptionStateParser.java
│ │ │ ├── SupportedParser.java
│ │ │ ├── TimeStampParser.java
│ │ │ ├── ToParser.java
│ │ │ ├── TokenNames.java
│ │ │ ├── TokenTypes.java
│ │ │ ├── URLParser.java
│ │ │ ├── UnsupportedParser.java
│ │ │ ├── UserAgentParser.java
│ │ │ ├── ViaParser.java
│ │ │ ├── WWWAuthenticateParser.java
│ │ │ ├── WarningParser.java
│ │ │ ├── extensions/
│ │ │ │ ├── JoinParser.java
│ │ │ │ ├── MinSEParser.java
│ │ │ │ ├── ReferencesParser.java
│ │ │ │ ├── ReferredByParser.java
│ │ │ │ ├── ReplacesParser.java
│ │ │ │ └── SessionExpiresParser.java
│ │ │ └── ims/
│ │ │ ├── AddressHeaderParser.java
│ │ │ ├── PAccessNetworkInfoParser.java
│ │ │ ├── PAssertedIdentityParser.java
│ │ │ ├── PAssertedServiceParser.java
│ │ │ ├── PAssociatedURIParser.java
│ │ │ ├── PCalledPartyIDParser.java
│ │ │ ├── PChargingFunctionAddressesParser.java
│ │ │ ├── PChargingVectorParser.java
│ │ │ ├── PMediaAuthorizationParser.java
│ │ │ ├── PPreferredIdentityParser.java
│ │ │ ├── PPreferredServiceParser.java
│ │ │ ├── PProfileKeyParser.java
│ │ │ ├── PServedUserParser.java
│ │ │ ├── PUserDatabaseParser.java
│ │ │ ├── PVisitedNetworkIDParser.java
│ │ │ ├── PathParser.java
│ │ │ ├── PrivacyParser.java
│ │ │ ├── SecurityAgreeParser.java
│ │ │ ├── SecurityClientParser.java
│ │ │ ├── SecurityServerParser.java
│ │ │ ├── SecurityVerifyParser.java
│ │ │ ├── ServiceRouteParser.java
│ │ │ └── TokenNamesIms.java
│ │ └── stack/
│ │ ├── DefaultMessageLogFactory.java
│ │ ├── DefaultRouter.java
│ │ ├── HandshakeCompletedListenerImpl.java
│ │ ├── HopImpl.java
│ │ ├── IOHandler.java
│ │ ├── MessageChannel.java
│ │ ├── MessageLog.java
│ │ ├── MessageProcessor.java
│ │ ├── RawMessageChannel.java
│ │ ├── SIPClientTransaction.java
│ │ ├── SIPDialog.java
│ │ ├── SIPDialogErrorEvent.java
│ │ ├── SIPDialogEventListener.java
│ │ ├── SIPServerTransaction.java
│ │ ├── SIPStackTimerTask.java
│ │ ├── SIPTransaction.java
│ │ ├── SIPTransactionErrorEvent.java
│ │ ├── SIPTransactionEventListener.java
│ │ ├── SIPTransactionStack.java
│ │ ├── ServerLog.java
│ │ ├── ServerRequestInterface.java
│ │ ├── ServerResponseInterface.java
│ │ ├── StackMessageFactory.java
│ │ ├── TCPMessageChannel.java
│ │ ├── TCPMessageProcessor.java
│ │ ├── TLSMessageChannel.java
│ │ ├── TLSMessageProcessor.java
│ │ ├── UDPMessageChannel.java
│ │ └── UDPMessageProcessor.java
│ └── javax2/
│ └── sip/
│ ├── ClientTransaction.java
│ ├── Dialog.java
│ ├── DialogDoesNotExistException.java
│ ├── DialogState.java
│ ├── DialogTerminatedEvent.java
│ ├── IOExceptionEvent.java
│ ├── InvalidArgumentException.java
│ ├── ListeningPoint.java
│ ├── ObjectInUseException.java
│ ├── PeerUnavailableException.java
│ ├── ProviderDoesNotExistException.java
│ ├── RequestEvent.java
│ ├── ResponseEvent.java
│ ├── ServerTransaction.java
│ ├── SipException.java
│ ├── SipFactory.java
│ ├── SipListener.java
│ ├── SipProvider.java
│ ├── SipStack.java
│ ├── Timeout.java
│ ├── TimeoutEvent.java
│ ├── Transaction.java
│ ├── TransactionAlreadyExistsException.java
│ ├── TransactionDoesNotExistException.java
│ ├── TransactionState.java
│ ├── TransactionTerminatedEvent.java
│ ├── TransactionUnavailableException.java
│ ├── TransportNotSupportedException.java
│ ├── address/
│ │ ├── Address.java
│ │ ├── AddressFactory.java
│ │ ├── Hop.java
│ │ ├── Router.java
│ │ ├── SipURI.java
│ │ ├── TelURL.java
│ │ └── URI.java
│ ├── header/
│ │ ├── AcceptEncodingHeader.java
│ │ ├── AcceptHeader.java
│ │ ├── AcceptLanguageHeader.java
│ │ ├── AlertInfoHeader.java
│ │ ├── AllowEventsHeader.java
│ │ ├── AllowHeader.java
│ │ ├── AuthenticationInfoHeader.java
│ │ ├── AuthorizationHeader.java
│ │ ├── CSeqHeader.java
│ │ ├── CallIdHeader.java
│ │ ├── CallInfoHeader.java
│ │ ├── ContactHeader.java
│ │ ├── ContentDispositionHeader.java
│ │ ├── ContentEncodingHeader.java
│ │ ├── ContentLanguageHeader.java
│ │ ├── ContentLengthHeader.java
│ │ ├── ContentTypeHeader.java
│ │ ├── DateHeader.java
│ │ ├── Encoding.java
│ │ ├── ErrorInfoHeader.java
│ │ ├── EventHeader.java
│ │ ├── ExpiresHeader.java
│ │ ├── ExtensionHeader.java
│ │ ├── FromHeader.java
│ │ ├── Header.java
│ │ ├── HeaderAddress.java
│ │ ├── HeaderFactory.java
│ │ ├── InReplyToHeader.java
│ │ ├── MaxForwardsHeader.java
│ │ ├── MediaType.java
│ │ ├── MimeVersionHeader.java
│ │ ├── MinExpiresHeader.java
│ │ ├── OptionTag.java
│ │ ├── OrganizationHeader.java
│ │ ├── Parameters.java
│ │ ├── PriorityHeader.java
│ │ ├── ProxyAuthenticateHeader.java
│ │ ├── ProxyAuthorizationHeader.java
│ │ ├── ProxyRequireHeader.java
│ │ ├── RAckHeader.java
│ │ ├── RSeqHeader.java
│ │ ├── ReasonHeader.java
│ │ ├── RecordRouteHeader.java
│ │ ├── ReferToHeader.java
│ │ ├── ReplyToHeader.java
│ │ ├── RequireHeader.java
│ │ ├── RetryAfterHeader.java
│ │ ├── RouteHeader.java
│ │ ├── SIPETagHeader.java
│ │ ├── SIPIfMatchHeader.java
│ │ ├── ServerHeader.java
│ │ ├── SubjectHeader.java
│ │ ├── SubscriptionStateHeader.java
│ │ ├── SupportedHeader.java
│ │ ├── TimeStampHeader.java
│ │ ├── ToHeader.java
│ │ ├── TooManyHopsException.java
│ │ ├── UnsupportedHeader.java
│ │ ├── UserAgentHeader.java
│ │ ├── ViaHeader.java
│ │ ├── WWWAuthenticateHeader.java
│ │ └── WarningHeader.java
│ └── message/
│ ├── Message.java
│ ├── MessageFactory.java
│ ├── Request.java
│ └── Response.java
├── mediaplayer/
│ ├── AndroidManifest.xml
│ ├── build.gradle
│ ├── proguard-project.txt
│ ├── project.properties
│ └── src/
│ └── com/
│ └── orangelabs/
│ └── rcs/
│ └── core/
│ └── ims/
│ └── protocol/
│ └── rtp/
│ ├── CodecChain.java
│ ├── DummyPacketGenerator.java
│ ├── MediaRegistry.java
│ ├── MediaRtpReceiver.java
│ ├── MediaRtpSender.java
│ ├── Processor.java
│ ├── RtpException.java
│ ├── RtpUtils.java
│ ├── VideoRtpReceiver.java
│ ├── VideoRtpSender.java
│ ├── codec/
│ │ ├── Codec.java
│ │ └── video/
│ │ ├── VideoCodec.java
│ │ └── h264/
│ │ ├── H264Config.java
│ │ ├── H264RtpHeaders.java
│ │ ├── JavaDepacketizer.java
│ │ ├── JavaPacketizer.java
│ │ ├── NalUnitHeader.java
│ │ ├── NalUnitType.java
│ │ ├── decoder/
│ │ │ └── NativeH264Decoder.java
│ │ ├── encoder/
│ │ │ ├── NativeH264Encoder.java
│ │ │ └── NativeH264EncoderParams.java
│ │ └── profiles/
│ │ ├── H264Profile.java
│ │ ├── H264Profile1.java
│ │ ├── H264Profile1_1.java
│ │ ├── H264Profile1_2.java
│ │ ├── H264Profile1_3.java
│ │ ├── H264Profile1b.java
│ │ ├── H264TypeLevel.java
│ │ └── H264TypeProfile.java
│ ├── core/
│ │ ├── ReceptionReport.java
│ │ ├── RtcpAppPacket.java
│ │ ├── RtcpByePacket.java
│ │ ├── RtcpCompoundPacket.java
│ │ ├── RtcpPacket.java
│ │ ├── RtcpPacketReceiver.java
│ │ ├── RtcpPacketTransmitter.java
│ │ ├── RtcpPacketUtils.java
│ │ ├── RtcpReceiverReportPacket.java
│ │ ├── RtcpReport.java
│ │ ├── RtcpSdesBlock.java
│ │ ├── RtcpSdesItem.java
│ │ ├── RtcpSdesPacket.java
│ │ ├── RtcpSenderReportPacket.java
│ │ ├── RtcpSession.java
│ │ ├── RtcpStatisticsReceiver.java
│ │ ├── RtcpStatisticsTransmitter.java
│ │ ├── RtpExtensionHeader.java
│ │ ├── RtpPacket.java
│ │ ├── RtpPacketReceiver.java
│ │ ├── RtpPacketTransmitter.java
│ │ ├── RtpSource.java
│ │ ├── RtpStatisticsReceiver.java
│ │ └── RtpStatisticsTransmitter.java
│ ├── event/
│ │ ├── RtcpApplicationEvent.java
│ │ ├── RtcpByeEvent.java
│ │ ├── RtcpEvent.java
│ │ ├── RtcpEventListener.java
│ │ ├── RtcpReceiverReportEvent.java
│ │ ├── RtcpSdesEvent.java
│ │ └── RtcpSenderReportEvent.java
│ ├── format/
│ │ ├── DummyFormat.java
│ │ ├── Format.java
│ │ └── video/
│ │ ├── CameraOptions.java
│ │ ├── H264VideoFormat.java
│ │ ├── Orientation.java
│ │ ├── VideoFormat.java
│ │ └── VideoOrientation.java
│ ├── media/
│ │ ├── MediaException.java
│ │ ├── MediaInput.java
│ │ ├── MediaListener.java
│ │ ├── MediaOutput.java
│ │ ├── MediaSample.java
│ │ └── VideoSample.java
│ ├── stream/
│ │ ├── DummyPacketSourceStream.java
│ │ ├── MediaCaptureStream.java
│ │ ├── MediaRendererStream.java
│ │ ├── ProcessorInputStream.java
│ │ ├── ProcessorOutputStream.java
│ │ ├── RtpInputStream.java
│ │ ├── RtpOutputStream.java
│ │ ├── RtpStreamListener.java
│ │ ├── VideoCaptureStream.java
│ │ └── VideoRendererStream.java
│ └── util/
│ ├── AndroidDatagramConnection.java
│ ├── Buffer.java
│ ├── DatagramConnection.java
│ ├── FifoBuffer.java
│ ├── HexadecimalUtils.java
│ ├── NetworkRessourceManager.java
│ ├── Packet.java
│ └── SystemTimeBase.java
├── samples/
│ └── api/
│ ├── extension/
│ │ ├── AndroidManifest.xml
│ │ ├── LICENSE-2.0.txt
│ │ ├── README.md
│ │ ├── build.gradle
│ │ ├── default.properties
│ │ ├── proguard-project.txt
│ │ ├── project.properties
│ │ ├── res/
│ │ │ ├── layout/
│ │ │ │ └── main.xml
│ │ │ └── values/
│ │ │ └── strings.xml
│ │ └── src/
│ │ └── com/
│ │ └── gsma/
│ │ └── services/
│ │ └── rcs/
│ │ └── samples/
│ │ └── extension/
│ │ └── Main.java
│ └── tts/
│ ├── AndroidManifest.xml
│ ├── LICENSE-2.0.txt
│ ├── README.md
│ ├── build.gradle
│ ├── build.xml
│ ├── proguard-project.txt
│ ├── project.properties
│ ├── res/
│ │ ├── values/
│ │ │ └── strings.xml
│ │ └── xml/
│ │ └── tts_preferences.xml
│ └── src/
│ └── com/
│ └── orangelabs/
│ └── rcs/
│ └── tts/
│ ├── ChatEvent.java
│ ├── Main.java
│ ├── PlayTextToSpeech.java
│ └── Registry.java
├── settings.gradle
├── studio/
│ └── README.md
├── templates-sdk/
│ ├── assets/
│ │ ├── android-developer-core.css
│ │ ├── android-developer-docs-devguide.css
│ │ ├── android-developer-docs.css
│ │ ├── android-developer-docs.js
│ │ ├── android-developer-reference.js
│ │ ├── carousel.js
│ │ ├── joyn-sdk.css
│ │ ├── joyn-sdk.js
│ │ ├── jquery-history.js
│ │ ├── prettify.js
│ │ ├── search_autocomplete.js
│ │ ├── skin-carousel-home.css
│ │ ├── skin-carousel-ri.css
│ │ ├── style.css
│ │ └── widget.js
│ ├── components/
│ │ ├── left_nav.cs
│ │ └── masthead.cs
│ ├── footer.cs
│ ├── head_tag.cs
│ └── macros.cs
├── tests/
│ ├── cts/
│ │ ├── provider/
│ │ │ ├── build.gradle
│ │ │ └── src/
│ │ │ ├── androidTest/
│ │ │ │ └── java/
│ │ │ │ └── android/
│ │ │ │ └── tests/
│ │ │ │ └── provider/
│ │ │ │ ├── CapabilitiesLogTest.java
│ │ │ │ ├── ChatLogGroupChatTest.java
│ │ │ │ ├── ChatLogMessageTest.java
│ │ │ │ ├── FileTransferLogTest.java
│ │ │ │ ├── GeolocSharingLogTest.java
│ │ │ │ ├── GroupDeliveryInfoLogTest.java
│ │ │ │ ├── HistoryLogTest.java
│ │ │ │ ├── ImageSharingLogTest.java
│ │ │ │ ├── Utils.java
│ │ │ │ └── VideoSharingLogTest.java
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ └── res/
│ │ │ └── values/
│ │ │ └── strings.xml
│ │ ├── run-test
│ │ └── signature/
│ │ ├── README.md
│ │ ├── build.gradle
│ │ └── src/
│ │ ├── androidTest/
│ │ │ └── java/
│ │ │ └── android/
│ │ │ └── tests/
│ │ │ └── sigtest/
│ │ │ └── RcsApiSignatureTest.java
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── android/
│ │ │ └── tests/
│ │ │ └── sigtest/
│ │ │ ├── JDiffClassDescription.java
│ │ │ ├── RcsApiSignatureTestResult.java
│ │ │ ├── ResultObserver.java
│ │ │ ├── SignatureTest.java
│ │ │ ├── SignatureTestActivity.java
│ │ │ ├── SignatureTestLog.java
│ │ │ └── ToTest.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── rcs_api_signature.xml
│ │ │ └── rcs_api_signature_item.xml
│ │ ├── raw/
│ │ │ └── excludepackages.txt
│ │ ├── values/
│ │ │ └── strings.xml
│ │ └── xml/
│ │ ├── albatros.xml
│ │ ├── blackbird_1_0.xml
│ │ ├── blackbird_1_5_1.xml
│ │ └── crane_1_6_1.xml
│ ├── multistack/
│ │ ├── AndroidManifest.xml
│ │ ├── res/
│ │ │ ├── layout/
│ │ │ │ └── activity_main.xml
│ │ │ └── values/
│ │ │ └── strings.xml
│ │ └── src/
│ │ └── com/
│ │ └── orangelabs/
│ │ └── test/
│ │ └── stack2/
│ │ ├── GetStatusReceiver.java
│ │ └── MainActivity.java
│ ├── samples/
│ │ ├── AndroidManifest.xml
│ │ ├── res/
│ │ │ └── values/
│ │ │ └── strings.xml
│ │ └── src/
│ │ └── com/
│ │ └── gsma/
│ │ └── rcs/
│ │ └── api/
│ │ ├── CapabilitySampleTest.java
│ │ ├── ChatSampleTest.java
│ │ ├── CodeSamplesInstrumentationTestRunner.java
│ │ ├── ContactSampleTest.java
│ │ ├── MultimediaSessionSampleTest.java
│ │ └── Synchronizer.java
│ └── security-extension/
│ ├── AndroidManifest.xml
│ ├── install.bat
│ ├── res/
│ │ ├── layout/
│ │ │ └── activity_main.xml
│ │ └── values/
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── src/
│ │ └── com/
│ │ └── gsma/
│ │ └── rcs/
│ │ └── MainActivity.java
│ └── test_security_extension.apk
└── tools/
├── notification/
│ ├── AndroidManifest.xml
│ ├── README.md
│ ├── build.gradle
│ ├── build.xml
│ ├── proguard-project.txt
│ ├── project.properties
│ ├── res/
│ │ ├── values/
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-de/
│ │ │ └── strings.xml
│ │ ├── values-fr/
│ │ │ └── strings.xml
│ │ ├── values-v11/
│ │ │ └── styles.xml
│ │ └── values-v14/
│ │ └── styles.xml
│ └── src/
│ └── com/
│ └── gsma/
│ └── rcs/
│ └── notif/
│ ├── DeviceBoot.java
│ ├── LaunchServiceActivity.java
│ └── RcsServiceNotifManager.java
├── provisioning/
│ ├── AndroidManifest.xml
│ ├── README.md
│ ├── build.gradle
│ ├── proguard-project.txt
│ ├── project.properties
│ ├── res/
│ │ ├── layout/
│ │ │ └── activity_main.xml
│ │ ├── values/
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-v11/
│ │ │ └── styles.xml
│ │ ├── values-v14/
│ │ │ └── styles.xml
│ │ └── values-w820dp/
│ │ └── dimens.xml
│ └── src/
│ └── com/
│ └── gsma/
│ └── rcs/
│ └── tools/
│ └── http/
│ └── provisioning/
│ └── ProvisioningTemplateActivity.java
└── settings/
├── AndroidManifest.xml
├── README.md
├── build.gradle
├── build.xml
├── project.properties
├── res/
│ ├── layout/
│ │ └── app_about.xml
│ ├── values/
│ │ └── strings.xml
│ └── xml/
│ ├── rcs_settings_messaging_preferences.xml
│ ├── rcs_settings_preferences.xml
│ └── rcs_settings_userprofile_preferences.xml
└── src/
└── com/
└── gsma/
└── rcs/
└── core/
└── control/
├── CoreControlApplication.java
└── settings/
├── AboutSettings.java
├── ControlCoreReceiver.java
├── MessagingSettingsDisplay.java
├── SettingsDisplay.java
└── UserprofileSettingsDisplay.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.bak
gen
bin
.settings
.idea
.gradle
*.iml
*.jar
*.classpath
*.project
build
doclava
local.properties
/gradle.properties
================================================
FILE: README.md
================================================
# rcsjta
RCS-e stack for Android with GSMA API **RCS-e stack for Android with GSMA API**
Follow @androidrcsstack
The RCS-e stack is an open source implementation of the Rich Communication Suite standards for Google Android platform. This implementation is compliant to GSMA RCS-e Blackbird standards. Thanks to its client/server API, the stack may be easily integrated with existing native Android applications (e.g. address book, dialer) and permits to create new RCS applications (e.g. chat, widgets).
##About RCS, Rich Communication Suite:
The Rich Communication Suite Initiative is a GSM Association programme dedicated to deliver convergent rich communication services. RCS should be the first set of services using IMS architecture in the mobile field. "joyn" is the commercial name of RCS.
See also the RCS website at GSM Association, [http://www.gsma.com/rcs/](http://www.gsma.com/rcs/).
The RCS specifications (product, technical, API, Guidelines) are available at [http://www.gsma.com/network2020/rcs/specs-and-product-docs/](http://www.gsma.com/network2020/rcs/specs-and-product-docs/).
Note: the [supported standards](https://rawgit.com/android-rcs/rcsjta/master/docs/SUPPORTED-STANDARDS.txt).
##Licensing:
The RCS core stack is under [Apache 2 license](https://rawgit.com/android-rcs/rcsjta/master/core/LICENSE-2.0.txt) and uses the following open source libraries:
- the SIP stack comes from [NIST-SIP project] (http://jsip.java.net/'>http://jsip.java.net/), see [License](https://rawgit.com/android-rcs/rcsjta/master/core/LICENSE-NIST.txt).
- the DNS stack comes from [DNSJava project](http://www.dnsjava.org/), see [License](https://rawgit.com/android-rcs/rcsjta/master/core/LICENSE-DNS.txt).
- the cryptography API comes from Legion of the [Bouncy Castle Inc] (https://www.bouncycastle.org/), see [License](https://rawgit.com/android-rcs/rcsjta/master/core/LICENSE-BOUNCYCASTLE.txt).
##Project:
- see [Project management](https://rawgit.com/android-rcs/rcsjta/master/docs/RCSJTA_open_source.ppt).
- see [GIT branches](https://github.com/android-rcs/rcsjta/blob/wiki/Branches.md).
##RCS API definition:
- TAPI 1.5
* see [TAPI 1.5.1 specification](https://rawgit.com/android-rcs/rcsjta/master/docs/tapi/RCC.53_v3.0_1.5.1-r1.docx).
* see [TAPI 1.5.1 Javadoc] (https://rawgit.com/android-rcs/rcsjta.javadoc/javadoc1.5/index.html).
- TAPI 1.6
* see [TAPI 1.6.1 specification](https://rawgit.com/android-rcs/rcsjta/master/docs/tapi/RCC.53_CR1005_1.6.1.docx).
* see [TAPI 1.6.1 Javadoc] (https://rawgit.com/android-rcs/rcsjta.javadoc/javadoc1.6/index.html).
##SDK nightly builds:
- see latest SDK of [tapi_1.5](https://github.com/android-rcs/rcsjta.build/tree/tapi_1.5) branch.
- see latest SDK of [tapi_1.6](https://github.com/android-rcs/rcsjta.build/tree/tapi_1.6) branch.
##Stack overview:

##More docs:
- see [Wiki](https://github.com/android-rcs/rcsjta/wiki).
================================================
FILE: RI/AndroidManifest.xml
================================================
================================================
FILE: RI/LICENSE-2.0.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: RI/README.md
================================================
# The Reference Implementation
This application shows how to use the RCSJTA api.
It gives examples of use cases for a RCSJTA client application.
Build instruction:
../gradlew :RI:build
**Additional steps for Eclipse compatibility:**
ant libs
This will create the following jar files under the "libs" folder:
- api.jar
- rcs_media.jar
- api_cnx.jar
Download the [android-support-v4.jar](https://developer.android.com/tools/support-library/setup.html) library and copy under the "libs" folder.
Set up the [google play service library](https://developers.google.com/android/guides/setup).
Set up the api connection library by importing the Android project '../libs/api_cnx/build/intermediates/bundles/debug/'
================================================
FILE: RI/build.gradle
================================================
apply plugin: 'com.android.application'
android {
//Required to support the old folder structure
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
androidTest.setRoot('tests')
}
//Required to support builds although lint errors exist
lintOptions {
abortOnError false
disable 'IconLocation'
disable 'IconDuplicates'
disable 'IconDuplicatesConfig'
disable 'IconColors'
disable 'IconMissingDensityFolder'
disable 'IconDensities'
}
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
applicationId "com.gsma.rcs.ri"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode 2
versionName "2.0"
archivesBaseName = "RI"
}
}
dependencies {
compile project(':api')
compile project(':api_cnx')
compile project(':mediaplayer')
compile 'com.android.support:support-v4:25.0.1'
compile 'com.google.android.gms:play-services:8.4.0'
}
//Below install dependecy was added to always install RCS service before
//a RCS client to secure that Android handles RCS permissions correctly.
task installServiceFirst(dependsOn: ':core:installDebug') << {
println 'RCS core service was installed first!'
}
tasks.whenTaskAdded { task ->
if (task.name == 'installDebug') {
task.dependsOn installServiceFirst
}
}
================================================
FILE: RI/build.xml
================================================
Copy ${terminal.target1} file
Copy ${terminal.target2} file
Copy ${terminal.target3} file
Delete library files
Library Ant Build. Available targets:
help: Displays this help.
clean: Removes output files created by other targets.
libs: Generate input libraries.
all: Deletes then copy input libraries.
================================================
FILE: RI/default.properties
================================================
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=Google Inc.:Google APIs:10
================================================
FILE: RI/proguard-project.txt
================================================
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: RI/project.properties
================================================
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=Google Inc.:Google APIs:22
android.library.reference.1=../../../../../adt-bundle-windows-x86_64/sdk/extras/google/google_play_services/libproject/google-play-services_lib
================================================
FILE: RI/res/drawable/counter_circle.xml
================================================
================================================
FILE: RI/res/drawable/talk_item_rcs_in.xml
================================================
================================================
FILE: RI/res/drawable/talk_item_rcs_out.xml
================================================
================================================
FILE: RI/res/layout/app_about.xml
================================================
================================================
FILE: RI/res/layout/ask_permissions.xml
================================================
================================================
FILE: RI/res/layout/audio_msg_record.xml
================================================
================================================
FILE: RI/res/layout/capabilities_list.xml
================================================
================================================
FILE: RI/res/layout/capabilities_list_item.xml
================================================
================================================
FILE: RI/res/layout/capabilities_mine.xml
================================================
================================================
FILE: RI/res/layout/capabilities_refresh.xml
================================================
================================================
FILE: RI/res/layout/capabilities_request.xml
================================================
================================================
FILE: RI/res/layout/chat_initiate_group.xml
================================================
================================================
FILE: RI/res/layout/chat_initiate_single.xml
================================================
================================================
FILE: RI/res/layout/chat_list.xml
================================================
================================================
FILE: RI/res/layout/chat_message_log_item.xml
================================================
================================================
FILE: RI/res/layout/chat_send_file.xml
================================================
================================================
FILE: RI/res/layout/chat_service_config.xml
================================================
================================================
FILE: RI/res/layout/chat_view.xml
================================================
================================================
FILE: RI/res/layout/contact_list_item.xml
================================================
================================================
FILE: RI/res/layout/contacts_blocking.xml
================================================
================================================
FILE: RI/res/layout/contacts_rcs_list.xml
================================================
================================================
FILE: RI/res/layout/contacts_vcard.xml
================================================
================================================
FILE: RI/res/layout/delivery_info_item.xml
================================================
================================================
FILE: RI/res/layout/delivery_info_list.xml
================================================
================================================
FILE: RI/res/layout/extension_initiate_session.xml
================================================
================================================
FILE: RI/res/layout/extension_messaging_session_view.xml
================================================
================================================
FILE: RI/res/layout/extension_send_instant_message.xml
================================================
================================================
FILE: RI/res/layout/extension_session_list.xml
================================================
================================================
FILE: RI/res/layout/extension_streaming_session_view.xml
================================================
================================================
FILE: RI/res/layout/filetransfer_custom_title.xml
================================================
================================================
FILE: RI/res/layout/filetransfer_initiate.xml
================================================
================================================
FILE: RI/res/layout/filetransfer_log_item.xml
================================================
================================================
FILE: RI/res/layout/filetransfer_receive.xml
================================================
================================================
FILE: RI/res/layout/filetransfer_send_multi_file.xml
================================================
================================================
FILE: RI/res/layout/filetransfer_send_multi_file_item.xml
================================================
================================================
FILE: RI/res/layout/filetransfer_service_config.xml
================================================
================================================
FILE: RI/res/layout/fileupload_initiate.xml
================================================
================================================
FILE: RI/res/layout/gchat_item_rcs_chat_in.xml
================================================
================================================
FILE: RI/res/layout/gchat_item_rcs_chat_out.xml
================================================
================================================
FILE: RI/res/layout/gchat_item_rcs_file_transfer_in.xml
================================================
================================================
FILE: RI/res/layout/gchat_item_rcs_file_transfer_out.xml
================================================
================================================
FILE: RI/res/layout/geoloc_display.xml
================================================
================================================
FILE: RI/res/layout/geoloc_edit.xml
================================================
================================================
FILE: RI/res/layout/geoloc_sharing_initiate.xml
================================================
================================================
FILE: RI/res/layout/geoloc_sharing_receive.xml
================================================
================================================
FILE: RI/res/layout/geoloc_show.xml
================================================
================================================
FILE: RI/res/layout/groupchat_event_view_item.xml
================================================
================================================
FILE: RI/res/layout/history_log_sharing.xml
================================================
================================================
FILE: RI/res/layout/image_sharing_initiate.xml
================================================
================================================
FILE: RI/res/layout/image_sharing_receive.xml
================================================
================================================
FILE: RI/res/layout/intents_apps.xml
================================================
================================================
FILE: RI/res/layout/ri_list.xml
================================================
================================================
FILE: RI/res/layout/service_configuration.xml
================================================
================================================
FILE: RI/res/layout/service_registration.xml
================================================
================================================
FILE: RI/res/layout/service_status.xml
================================================
================================================
FILE: RI/res/layout/sharing_log_geoloc_item.xml
================================================
================================================
FILE: RI/res/layout/sharing_log_image_item.xml
================================================
================================================
FILE: RI/res/layout/sharing_log_list.xml
================================================
================================================
FILE: RI/res/layout/sharing_log_video_item.xml
================================================
================================================
FILE: RI/res/layout/talk_item_rcs_chat_in.xml
================================================
================================================
FILE: RI/res/layout/talk_item_rcs_chat_out.xml
================================================
================================================
FILE: RI/res/layout/talk_item_rcs_file_transfer_in.xml
================================================
================================================
FILE: RI/res/layout/talk_item_rcs_file_transfer_out.xml
================================================
================================================
FILE: RI/res/layout/talk_log_group_item.xml
================================================
================================================
FILE: RI/res/layout/talk_log_one_to_one_item.xml
================================================
================================================
FILE: RI/res/layout/utils_smiley_menu_item.xml
================================================
================================================
FILE: RI/res/layout/utils_spinner_item.xml
================================================
================================================
FILE: RI/res/layout/video_sharing_incoming.xml
================================================
================================================
FILE: RI/res/layout/video_sharing_outgoing.xml
================================================
================================================
FILE: RI/res/menu/menu_1to1_talk.xml
================================================
================================================
FILE: RI/res/menu/menu_1to1_talk_item.xml
================================================
================================================
FILE: RI/res/menu/menu_ft.xml
================================================
================================================
FILE: RI/res/menu/menu_gchat_item.xml
================================================
================================================
FILE: RI/res/menu/menu_geoloc_sharing.xml
================================================
================================================
FILE: RI/res/menu/menu_group_chat.xml
================================================
================================================
FILE: RI/res/menu/menu_historylog.xml
================================================
================================================
FILE: RI/res/menu/menu_image_sharing.xml
================================================
================================================
FILE: RI/res/menu/menu_initiate_ft.xml
================================================
================================================
FILE: RI/res/menu/menu_log.xml
================================================
================================================
FILE: RI/res/menu/menu_log_item.xml
================================================
================================================
FILE: RI/res/menu/menu_log_sharing_item.xml
================================================
================================================
FILE: RI/res/menu/menu_mm_session.xml
================================================
================================================
FILE: RI/res/menu/menu_video_sharing.xml
================================================
================================================
FILE: RI/res/values/filetotransfer.xml
================================================
- Image from gallery
- Text file from explorer
- Audio from explorer
================================================
FILE: RI/res/values/quicktexts.xml
================================================
- Hello
- Hello everybody
- How are you
- I\'m fine
- Bye Bye
- See you soon
- Where are you?
- I\'ll get back to you
- Urgent! Please reply…
- Have a nice weekend
- Thanks a lot
================================================
FILE: RI/res/values/smileys.xml
================================================
- :-)
- :-(
- ;-)
- :-P
- =-O
- :-*
- :O
- B-)
- :-$
- :-!
- :-[
- O:-)
- :-\\
- :\'(
- :-X
- :-D
- o_O
- Happy
- Sad
- Winking
- Tongue sticking out
- Surprised
- Kissing
- Yelling
- Cool
- Money mouth
- Foot in mouth
- Embarrassed
- Angel
- Undecided
- Crying
- Lips are sealed
- Laughing
- Confused
================================================
FILE: RI/res/values/strings.xml
================================================
RCS RI
About
The address book is not found, launch it manually from your device home screen.
Initializing…
The service is not available. Please retry later.
Do you confirm ?\nThis will close the session.
Quit
Clear log
Size: %1$s
Command in progress…
Session not found
API is not compatible
OK
Cancel
Accept
Decline
Refresh
From: %s
Yes
No
Database access has failed
No entry
incoming
outgoing
unknown direction
unknown
Delivered at %s
Displayed at %s
Status: %s
Reason: %s
Reason:
File: %s
Status:
Position:
Progress:
Selected file:
Size:
Contact:
Duration (sec):
Remote contact:
Select file
Invalid list of participants
RCS API permission denied
Feature not yet implemented
Not registered to IMS
dummy text
2016/01/02 03:00:00
+33123456789
ext.foo
direction
0
No record found!
Reference Implementation
App release %s
Contacts
Native address book
All RCS contacts
Contact visit card
Visit card:
Show card
Block contact
Block
Unblock
%s has been blocked
%s has been unblocked
Is blocked:
On line:
Blocking time:
Capabilities
My capabilities
Request capabilities
Refresh all capabilities
Capabilities log
Contacts will been refreshed in background
Capabilities:
This command permits to force an update of capabilities for each contact in the address book. This may take a long time if there is a lof of contacts.
Video sharing
Image sharing
File transfer
IM session
Extensions:
Request has been sent in background for %s
No capabilities
Contact %s
Geolocation push
Automata:
Last refresh:
No capabilities for %s
Messaging
File transfer
Chat
Messaging log
View details
One to one conversation
Transfer a file
File transfer invitation
File transfer
Transfer failed (%1$s)
Transfer aborted (%1$s)
File transfer session has been cancelled
Transfer rejected (%1$s)
Pause
Resume
Pause of File Transfer has failed
Resume of File Transfer has failed
Transfer is aborted because file is too big
Not enough free space on device storage for File Transfer
Session has expired (timeout or any other error)
%1$s (%2$s)
From: %1$s\nSize: %2$s
File transfer configuration
File size warning:
Max file size:
AA enabled:
AA enabled in roaming:
Max file transfers:
Max audio file duration (msec):
Group supported:
File transfer service configuration
Image resize option:
Expiration:
Expiration: %s
Start transfer
Size:
Not allowed to transfer file(s)
Multiple file transfer
Cannot pause HTTP file transfer
Cannot resume HTTP file transfer
Initiate single chat
Initiate group chat
Chat service configuration
Invite
Chat from %s
Group chat from %s
Group chat
Chat with %s
Are you sure to quit the session?
Chat session aborted (%1$s)
Chat session rejected (%1$s)
Smiley
Participants
Add participant
Chat session failed (%1$s)
Contacts:
%1$s is typing…
Select contacts
Quick text
Quick text
Subject:
Subject: %s
Can\'t add participants: max number of participants is reached (max=%d)
Do you confirm sending a file of %1$s ?
Me
No chat
No participant found or available
no subject
…
Thumbnail
Audio message
Send file
Delete all messages
Geoloc
From: %1$s\nSubject: %2$s
Send
Contact has %1$s
Chat service configuration
Warning SF:
Composing timeout:
Min GC participants:
MaX GC participants:
Max GC msg length:
Max GC subject length:
Max 1to1 msg length:
SMS Fallback:
Reply to display reports:
Max Geoloc label length:
Geoloc Expire Timeout:
GC supported:
Not allowed to initiate Group Chat
Undelivered message(s)!
%1$s did not receive your message(s)
Undelivered file(s)!
%1$s did not receive your file(s)
View delivery information
Sharing
Video sharing
Dial
Sharing aborted (%1$s)
Sharing failed (%1$s)
Sharing rejected (%1$s)
Delete
Display
Initiate image sharing
Select picture
Filename:
Size:
Image sharing invitation
Image sharing
ISH state %1$s (%2$s)
Initiate video sharing
Invite
Video sharing invitation
Video sharing
VSH state %1$s (%2$s)
Switch camera
Format:
Geoloc sharing invitation
Select location
Initiate geoloc sharing
Geoloc sharing
GSH state %1$s (%2$s)
0.0
0.0
0
Multimedia session
Initiate messaging session
Current messaging sessions
Initiate streaming session
Current streaming sessions
Send instant message
Initiate session
No session
Service ID:
Session initiation has been canceled
Session aborted (%1$s)
Session rejected (%1$s)
Session failed (%1$s)
Session has expired
Multimedia messaging invitation
Multimedia streaming invitation
Messaging session
Streaming session
Instant session
Session %s
Send data
Start RTP streaming
Stop RTP streaming
To enable data streaming, both sides must send data. NAT binding is maintained by sending data periodically (1 packet per second).
Receive data:
MMS state %1$s (%2$s)
From: %1$s\nService ID: %2$s
RX :
TX :
Send message
Instant multimedia message from %s:
Message has been sent!
Select from google map\n(Internet connectivity required)
Show us in a map
Send geoloc
Show a geoloc
Geolocation message
Label:
Contact:
Latitude:
Longitude:
Accuracy:
No geoloc info to display!
Display via google map\n(Internet connectivity required)
Intents
Applications
Load a chat
Start a chat
Load a group chat
Start a group chat
Load a file transfer
Start a file transfer
Intent not found
Service
Service status
RCS service
Registration status
Service bound:
Service activated:
Service started:
Failed to read stack activation mode
Failed to read stack started status
Service registered:
Service configuration
Configuration valid:
Messaging method:
Messaging mode:
My display name:
My contact identifier:
Min battery level:
RCS stack activation:
Unchangeable
Changeable
Connected to RCS platform
Disconnected from RCS platform
Disconnected from RCS platform (Battery low)
File upload
File upload
Upload
Thumbnail
File upload failed
File upload aborted
Show uploaded file
Show uploaded thumbnail
File is too big. The max size authorised is %s
STARTED
TRANSFERRED
Group delivery information
Delete
Resend
Display
Listen to
Id:
Chat Id:
Contact:
Content:
Date:
Sent:
Delivered:
Displayed:
Expiration:
Icon expiration:
Read:
Expired delivery:
Direction:
Duration:
Encoding:
Size:
Filename:
Height :
Mime type:
Icon mime type:
Reason:
State:
Transferred:
File uri:
Icon uri:
Width :
History Log detail
Sharing log
Geoloc Share
Image Share
Video Share
No contact filter
Duration: %1$s
Filter
Disposition:
Messaging Log
Sharing Log
Change filter
Log is empty
No contact filter
Below permissions are not granted!
Record audio
Play record
Start audio record
Stop audio record
Recorded file:
Maximum duration is reached!
Delete conversation failed
Cannot read RCS history log
- INVITED
- ACCEPTING
- REJECTED
- QUEUED
- INITIATING
- STARTED
- PAUSED
- ABORTED
- TRANSFERRED
- FAILED
- DELIVERED
- DISPLAYED
- ABORTED_BY_USER
- ABORTED_BY_REMOTE
- ABORTED_BY_SYSTEM
- ABORTED_BY_SECONDARY_DEVICE
- REJECTED_BY_TIMEOUT
- REJECTED_SPAM
- REJECTED_LOW_SPACE
- REJECTED_MAX_SIZE
- REJECTED_MAX_FILE_TRANSFERS
- REJECTED_BY_USER
- REJECTED_BY_REMOTE
- REJECTED_MEDIA_FAILED
- REJECTED_BY_SYSTEM
- PAUSED_BY_SYSTEM
- PAUSED_BY_USER
- FAILED_INITIATION
- FAILED_DATA_TRANSFER
- FAILED_SAVING
- FAILED_DELIVERY
- FAILED_DISPLAY
- FAILED_NOT_ALLOWED_TO_SEND
- INVITE_QUEUED
- INVITING
- DISCONNECTED
- INVITED
- CONNECTED
- DISCONNECTED
- DEPARTED
- FAILED
- DECLINED
- TIMEOUT
- UNSUPPORTED
- NOT_DELIVERED
- DELIVERED
- DISPLAYED
- FAILED
- FAILED_DELIVERY
- FAILED_DISPLAY
- INVITED
- INITIATING
- STARTED
- ABORTED
- FAILED
- ACCEPTING
- REJECTED
- ABORTED_BY_USER
- ABORTED_BY_REMOTE
- ABORTED_BY_INACTIVITY
- ABORTED_BY_SECONDARY_DEVICE
- REJECTED_SPAM
- REJECTED_MAX_CHATS
- REJECTED_BY_REMOTE
- REJECTED_BY_TIMEOUT
- REJECTED_BY_SYSTEM
- FAILED_INITIATION
- REJECTED
- QUEUED
- SENDING
- SENT
- FAILED
- DELIVERED
- DISPLAY_REPORT_REQUESTED
- RECEIVED
- DISPLAYED
- FAILED_SEND
- FAILED_DELIVERY
- FAILED_DISPLAY
- REJECTED_SPAM
- INVITED
- INITIATING
- STARTED
- ABORTED
- FAILED
- TRANSFERRED
- REJECTED
- RINGING
- ACCEPTING
- ABORTED_BY_USER
- ABORTED_BY_REMOTE
- ABORTED_BY_SYSTEM
- ABORTED_BY_SECONDARY_DEVICE
- REJECTED_SPAM
- REJECTED_BY_TIMEOUT
- REJECTED_LOW_SPACE
- REJECTED_ MAX_SIZE
- REJECTED_MAX_SESSIONS
- REJECTED_BY_USER
- REJECTED_BY_REMOTE
- REJECTED_BY_SYSTEM
- FAILED_INITIATION
- FAILED_SHARING
- FAILED_SAVING
- INVITED
- INITIATING
- STARTED
- ABORTED
- FAILED
- REJECTED
- RINGING
- ACCEPTING
- ABORTED_BY_USER
- ABORTED_BY_REMOTE
- ABORTED_BY_SYSTEM
- ABORTED_BY_SECONDARY_DEVICE
- REJECTED_SPAM
- REJECTED_MAX_SESSIONS
- REJECTED_BY_USER
- REJECTED_BY_REMOTE
- REJECTED_BY_TIMEOUT
- REJECTED_BY_SYSTEM
- FAILED_INITIATION
- FAILED_SHARING
- INVITED
- INITIATING
- STARTED
- ABORTED
- FAILED
- TRANSFERRED
- REJECTED
- RINGING
- ACCEPTING
- ABORTED_BY_USER
- ABORTED_BY_REMOTE
- ABORTED_BY_SYSTEM
- ABORTED_BY_SECONDARY_DEVICE
- REJECTED_SPAM
- REJECTED_MAX_SESSIONS
- REJECTED_BY_USER
- REJECTED_BY_REMOTE
- REJECTED_BY_TIMEOUT
- REJECTED_BY_SYSTEM
- FAILED_INITIATION
- FAILED_SHARING
- INVITED
- INITIATING
- STARTED
- ABORTED
- FAILED
- REJECTED
- RINGING
- ACCEPTING
- ABORTED_BY_USER
- ABORTED_BY_REMOTE
- ABORTED_BY_SYSTEM
- REJECTED_BY_INACTIVITY
- REJECTED_BY_USER
- REJECTED_BY_REMOTE
- REJECTED_BY_TIMEOUT
- REJECTED_BY_SYSTEM
- FAILED_INITIATION
- FAILED_SESSION
- FAILED_MEDIA
- JOINED
- DEPARTED
- Automatic
- RCS
- Non RCS
- Never stop
- 5 %
- 10 %
- 20 %
================================================
FILE: RI/src/com/gsma/rcs/ri/AboutRI.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri;
import com.gsma.services.rcs.RcsService.Build;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
/**
* About the RI
*
* @author Jean-Marc AUFFRET
*/
public class AboutRI extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.app_about);
// Display application release
TextView releaseView = (TextView) findViewById(R.id.app_version);
releaseView.setText(getString(R.string.label_about_app_version, getAppVersion()));
// Display GSMA version
TextView gsmaView = (TextView) findViewById(R.id.gsma_version);
gsmaView.setText(getGsmaVersion());
}
/**
* Returns the application version from manifest file
*
* @return Application version or null if not found
*/
private String getAppVersion() {
String version = null;
try {
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
version = info.versionName + "." + info.versionCode;
} catch (PackageManager.NameNotFoundException ignored) {
}
return version;
}
/**
* Returns the GSMA version
*
* @return String
*/
private String getGsmaVersion() {
StringBuilder version = new StringBuilder(Build.API_CODENAME);
version.append(" ");
switch (Build.API_VERSION) {
case Build.VERSION_CODES.BASE:
version.append("Albatros 2.0.");
break;
case Build.VERSION_CODES.BLACKBIRD:
version.append("Blackbird 1.5.");
break;
case Build.VERSION_CODES.CPR:
version.append("Crane Priority Release 1.6.");
break;
default:
version.append("Unknown 0.0");
}
version.append(Build.API_INCREMENTAL);
return version.toString();
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/DeviceBoot.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* On device boot starts the RCS service notification manager automatically
*
* @author Jean-Marc AUFFRET
*/
public class DeviceBoot extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
context.startService(new Intent(context, RcsServiceNotifManager.class));
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/RI.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsListActivity;
import com.gsma.rcs.ri.capabilities.TestCapabilitiesApi;
import com.gsma.rcs.ri.contacts.TestContactsApi;
import com.gsma.rcs.ri.extension.TestMultimediaSessionApi;
import com.gsma.rcs.ri.intents.TestIntentApps;
import com.gsma.rcs.ri.messaging.TestMessagingApi;
import com.gsma.rcs.ri.service.TestServiceApi;
import com.gsma.rcs.ri.sharing.TestSharingApi;
import com.gsma.rcs.ri.upload.InitiateFileUpload;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsPermissionDeniedException;
import com.gsma.services.rcs.contact.ContactUtil;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
/**
* RI application
*
* @author Jean-Marc AUFFRET
*/
public class RI extends RcsListActivity {
private static final int PROGRESS_INIT_INCREMENT = 100;
private static final String LOGTAG = LogUtils.getTag(RI.class.getSimpleName());
public static String sChatIdOnForeground;
private ListView mListView;
private ProgressBar mProgressBar;
private LinearLayout mInitLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Set layout */
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.ri_list);
mListView = (ListView) findViewById(android.R.id.list);
mProgressBar = (ProgressBar) findViewById(android.R.id.progress);
mInitLayout = (LinearLayout) findViewById(R.id.wait_cnx_start);
/* Set items */
// @formatter:off
String[] items = {
getString(R.string.menu_contacts),
getString(R.string.menu_capabilities),
getString(R.string.menu_messaging),
getString(R.string.menu_sharing),
getString(R.string.menu_mm_session),
getString(R.string.menu_intents),
getString(R.string.menu_service),
getString(R.string.menu_upload),
getString(R.string.menu_about)
};
// @formatter:on
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
ContactUtil contactUtil = ContactUtil.getInstance(this);
try {
/* The country code must be read to check that ContactUtil is ready to be used */
String cc = contactUtil.getMyCountryCode();
if (LogUtils.isActive) {
Log.w(LOGTAG, "Country code is '" + cc + "'");
}
} catch (RcsPermissionDeniedException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
/* We should not be allowed to continue if this exception occurs */
showMessageThenExit(R.string.error_api_permission_denied);
}
/*
* The initialization of the connection manager is delayed to avoid non response during
* application initialization after installation. The application waits until end of
* connection manager initialization.
*/
if (!RiApplication.sCnxManagerStarted) {
new WaitForConnectionManagerStart()
.execute(RiApplication.DELAY_FOR_STARTING_CNX_MANAGER);
} else {
mInitLayout.setVisibility(View.GONE);
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, TestContactsApi.class));
break;
case 1:
startActivity(new Intent(this, TestCapabilitiesApi.class));
break;
case 2:
startActivity(new Intent(this, TestMessagingApi.class));
break;
case 3:
startActivity(new Intent(this, TestSharingApi.class));
break;
case 4:
startActivity(new Intent(this, TestMultimediaSessionApi.class));
break;
case 5:
startActivity(new Intent(this, TestIntentApps.class));
break;
case 6:
startActivity(new Intent(this, TestServiceApi.class));
break;
case 7:
startActivity(new Intent(this, InitiateFileUpload.class));
break;
case 8:
startActivity(new Intent(this, AboutRI.class));
break;
}
}
private class WaitForConnectionManagerStart extends AsyncTask {
@Override
protected void onPreExecute() {
mInitLayout.setVisibility(View.VISIBLE);
mListView.setVisibility(View.GONE);
}
@Override
protected void onProgressUpdate(Integer... progress) {
mProgressBar.setProgress(progress[0]);
}
@Override
protected Void doInBackground(Long... duration) {
long delay = (duration[0] / PROGRESS_INIT_INCREMENT);
for (int i = 0; i < PROGRESS_INIT_INCREMENT; i++) {
try {
Thread.sleep(delay);
publishProgress((int) (delay * (i + 1) * 100 / duration[0]));
if (RiApplication.sCnxManagerStarted) {
break;
}
} catch (InterruptedException ignore) {
}
}
return null;
}
@Override
protected void onPostExecute(Void res) {
mInitLayout.setVisibility(View.GONE);
mListView.setVisibility(View.VISIBLE);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/RcsServiceNotifManager.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri;
import com.gsma.rcs.api.connection.utils.TimerUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.RcsServiceControl;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceListener;
import com.gsma.services.rcs.RcsServiceRegistration;
import com.gsma.services.rcs.RcsServiceRegistrationListener;
import com.gsma.services.rcs.capability.CapabilityService;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* Service manager that monitors the service availability and that displays the RCS status in the
* notification bar
*
* @author Jean-Marc AUFFRET
*/
public class RcsServiceNotifManager extends Service {
private static final String LOGTAG = LogUtils.getTag(RcsServiceNotifManager.class
.getSimpleName());
private final static int NOTIF_ID = 1000;
private RcsServiceStartupListener mStartupEventReceiver;
private RcsService mServiceApi;
private Context mCtx;
private PendingIntent mCnxIntent;
private int mRetryCount;
private AlarmManager mAlarmManager;
private static final String ACTION_VIEW_SETTINGS = "com.gsma.services.rcs.action.VIEW_SETTINGS";
private static final String ACTION_API_CONNECT = "com.gsma.rcs.ri.ACTION_API_CONNECT";
private static final long API_DELAY_TO_CONNECT = 5000;
private static final int MAX_RETRY_API_CNX = 4;
@Override
public void onCreate() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Service started");
}
mCtx = this;
mCnxIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_API_CONNECT), 0);
mAlarmManager = (AlarmManager) mCtx.getSystemService(Context.ALARM_SERVICE);
notifyImsUnregistered(RcsServiceRegistration.ReasonCode.UNSPECIFIED);
mStartupEventReceiver = new RcsServiceStartupListener();
registerReceiver(mStartupEventReceiver, new IntentFilter(RcsService.ACTION_SERVICE_UP));
/* Register the broadcast receiver to pool periodically the API connections */
registerReceiver(new ReceiveTimerToReConnectApi(), new IntentFilter(ACTION_API_CONNECT));
mRetryCount = 0;
connectToService(this);
}
@Override
public void onDestroy() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Service stopped");
}
unregisterReceiver(mStartupEventReceiver);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void connectToService(Context ctx) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Try to connect to service API");
}
try {
if (!RcsServiceControl.getInstance(ctx).isServiceStarted()) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "RCS service not yet started");
}
return;
}
mServiceApi = new CapabilityService(ctx, newRcsServiceListener());
mServiceApi.connect();
} catch (RcsServiceException e) {
Log.w(LOGTAG, "Cannot connect service API: ".concat(e.getMessage()));
mRetryCount++;
if (mRetryCount < MAX_RETRY_API_CNX) {
TimerUtils.setExactTimer(mAlarmManager, System.currentTimeMillis()
+ API_DELAY_TO_CONNECT, mCnxIntent);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Set timer to retry API connection");
}
} else {
Log.e(LOGTAG, "Maximum attempts to connect API is reached");
}
}
}
private class RcsServiceStartupListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!RcsService.ACTION_SERVICE_UP.equals(intent.getAction())) {
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Service UP");
}
mRetryCount = 0;
TimerUtils.setExactTimer(mAlarmManager, System.currentTimeMillis()
+ API_DELAY_TO_CONNECT, mCnxIntent);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Set timer to connect API");
}
}
}
private RcsServiceListener newRcsServiceListener() {
return new RcsServiceListener() {
@Override
public void onServiceDisconnected(ReasonCode error) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Service API disconnected");
}
notifyImsUnregistered(RcsServiceRegistration.ReasonCode.CONNECTION_LOST);
}
@Override
public void onServiceConnected() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Service API connected");
}
try {
mRetryCount = 0;
mServiceApi.addEventListener(mRcsRegistrationListener);
if (mServiceApi.isServiceRegistered()) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "IMS is registered");
}
notifyImsRegistered();
} else {
if (LogUtils.isActive) {
Log.d(LOGTAG, "IMS is unregistered");
}
RcsServiceRegistration.ReasonCode reason = mServiceApi
.getServiceRegistrationReasonCode();
notifyImsUnregistered(reason);
}
} catch (RcsServiceException e) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "Cannot add RCS Service Registration Listener", e);
}
}
}
};
}
private RcsServiceRegistrationListener mRcsRegistrationListener = new RcsServiceRegistrationListener() {
@Override
public void onServiceUnregistered(RcsServiceRegistration.ReasonCode reason) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "IMS has been unregistered");
}
notifyImsUnregistered(reason);
}
@Override
public void onServiceRegistered() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "IMS has been registered");
}
notifyImsRegistered();
}
};
private void notifyImsRegistered() {
addImsConnectionNotification(true, getString(R.string.ims_connected));
}
private void notifyImsUnregistered(RcsServiceRegistration.ReasonCode reason) {
String label;
if (RcsServiceRegistration.ReasonCode.BATTERY_LOW == reason) {
label = getString(R.string.ims_battery_disconnected);
} else {
label = getString(R.string.ims_disconnected);
}
addImsConnectionNotification(false, label);
}
private void addImsConnectionNotification(boolean connected, String label) {
Intent intent = new Intent(ACTION_VIEW_SETTINGS);
PendingIntent contentIntent = PendingIntent.getBroadcast(getApplicationContext(), 0,
intent, 0);
String title = this.getString(R.string.notification_title_rcs_service);
Notification notif = buildImsConnectionNotification(contentIntent, title, label, connected);
notif.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_FOREGROUND_SERVICE;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIF_ID, notif);
}
private Notification buildImsConnectionNotification(PendingIntent intent, String title,
String message, boolean connected) {
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(intent);
// With Android 5.0 Lollipop it is no longer possible to use colored icons in the
// notification area.
// Only large icon supports colors but only small icon can be shown in notification bar.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (connected) {
notif.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.ri_notif_on_icon_color));
notif.setSmallIcon(R.drawable.ri_notif_on_icon_white);
} else {
notif.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.ri_notif_off_icon_color));
notif.setSmallIcon(R.drawable.ri_notif_off_icon_white);
}
} else {
if (connected) {
notif.setSmallIcon(R.drawable.ri_notif_on_icon_color);
} else {
notif.setSmallIcon(R.drawable.ri_notif_off_icon_color);
}
}
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(false);
notif.setOnlyAlertOnce(true);
notif.setContentTitle(title);
notif.setContentText(message);
return notif.build();
}
private class ReceiveTimerToReConnectApi extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
new Thread() {
public void run() {
try {
connectToService(mCtx);
} catch (RuntimeException e) {
/*
* Intentionally catch runtime exceptions as else it will abruptly end the
* thread and eventually bring the whole system down, which is not intended.
*/
Log.e(LOGTAG, "Failed to pool connection to RCS service!", e);
}
}
}.start();
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/RiApplication.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri;
import com.gsma.rcs.api.connection.ConnectionManager;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.RcsServiceControl;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* This subclass of Application allows to get a resource content from a static context
*
* @author Philippe LEMORDANT
*/
public class RiApplication extends Application {
/**
* Delay (ms) before starting connection manager.
*/
/* package private */static final long DELAY_FOR_STARTING_CNX_MANAGER = 1000;
private static Context mContext;
/**
* Array of participant statuses
*/
public static String[] sParticipantStatuses;
/**
* Array of delivery statuses
*/
public static String[] sDeliveryStatuses;
/**
* Array of delivery reason codes
*/
public static String[] sDeliveryReasonCode;
/**
* Array of Group CHAT states
*/
public static String[] sGroupChatStates;
/**
* Array of Group CHAT reason codes
*/
public static String[] sGroupChatReasonCodes;
/**
* Array of message reason codes
*/
public static String[] sMessageReasonCodes;
/**
* Array of message statuses
*/
public static String[] sMessagesStatuses;
/**
* Array of file transfer states
*/
public static String[] sFileTransferStates;
/**
* Array of file transfer reason codes
*/
public static String[] sFileTransferReasonCodes;
/**
* Array of Image sharing states
*/
public static String[] sImageSharingStates;
/**
* Array of Image sharing reason codes
*/
public static String[] sImageSharingReasonCodes;
/**
* Array of Video sharing states
*/
public static String[] sVideoSharingStates;
/**
* Array of Video sharing reason codes
*/
public static String[] sVideoReasonCodes;
/**
* Array of Geolocation sharing states
*/
public static String[] sGeolocSharingStates;
/**
* Array of Geolocation sharing reason codes
*/
public static String[] sGeolocReasonCodes;
/**
* Array of MULTIMEDIA Messaging Session states
*/
public static String[] sMultimediaStates;
/**
* Array of MULTIMEDIA Messaging Session codes
*/
public static String[] sMultimediaReasonCodes;
/**
* Array of group chat events
*/
public static String[] sGroupChatEvents;
private static Map sDirectionToString;
private static RcsServiceControl mRcsServiceControl;
private static final String LOGTAG = LogUtils.getTag(RiApplication.class.getSimpleName());
/**
* Gets direction
*
* @param direction Direction
* @return String
*/
public static String getDirection(Direction direction) {
return sDirectionToString.get(direction);
}
/* package private */static boolean sCnxManagerStarted = false;
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
Resources resources = getResources();
sParticipantStatuses = convertForUI(resources.getStringArray(R.array.participant_statuses));
sDeliveryStatuses = convertForUI(resources.getStringArray(R.array.delivery_statuses));
sDeliveryReasonCode = convertForUI(resources.getStringArray(R.array.delivery_reason_codes));
sGroupChatStates = convertForUI(resources.getStringArray(R.array.group_chat_states));
sGroupChatReasonCodes = convertForUI(resources
.getStringArray(R.array.group_chat_reason_codes));
sMessageReasonCodes = convertForUI(resources.getStringArray(R.array.message_reason_codes));
sMessagesStatuses = convertForUI(resources.getStringArray(R.array.message_statuses));
sFileTransferStates = convertForUI(resources.getStringArray(R.array.file_transfer_states));
sFileTransferReasonCodes = convertForUI(resources
.getStringArray(R.array.file_transfer_reason_codes));
sImageSharingStates = convertForUI(resources.getStringArray(R.array.ish_states));
sImageSharingReasonCodes = convertForUI(resources.getStringArray(R.array.ish_reason_codes));
sVideoSharingStates = convertForUI(resources.getStringArray(R.array.vsh_states));
sVideoReasonCodes = convertForUI(resources.getStringArray(R.array.vsh_reason_codes));
sGeolocSharingStates = convertForUI(resources.getStringArray(R.array.gsh_states));
sGeolocReasonCodes = convertForUI(resources.getStringArray(R.array.gsh_reason_codes));
sMultimediaStates = convertForUI(resources.getStringArray(R.array.mms_states));
sMultimediaReasonCodes = convertForUI(resources.getStringArray(R.array.mms_reason_codes));
sGroupChatEvents = convertForUI(resources.getStringArray(R.array.group_chat_event));
sDirectionToString = new HashMap<>();
sDirectionToString.put(Direction.INCOMING, resources.getString(R.string.label_incoming));
sDirectionToString.put(Direction.OUTGOING, resources.getString(R.string.label_outgoing));
sDirectionToString.put(Direction.IRRELEVANT,
resources.getString(R.string.label_direction_unknown));
mRcsServiceControl = RcsServiceControl.getInstance(mContext);
/* Starts the RCS service notification manager */
startService(new Intent(this, RcsServiceNotifManager.class));
/* Do not execute the ConnectionManager on the main thread */
Handler mainThreadHandler = new Handler(Looper.getMainLooper());
final ConnectionManager cnxManager = ConnectionManager.createInstance(mContext,
mRcsServiceControl, EnumSet.allOf(RcsServiceName.class));
mainThreadHandler.postDelayed(new Runnable() {
@Override
public void run() {
try {
cnxManager.start();
sCnxManagerStarted = true;
} catch (RuntimeException e) {
Log.e(LOGTAG, "Failed to start connection manager!", e);
}
}
}, DELAY_FOR_STARTING_CNX_MANAGER);
}
/**
* Convert to lower case and readable strings
*
* @param strings array of strings to be converted
* @return converted strings
*/
private String[] convertForUI(String[] strings) {
List stringList = Arrays.asList(strings);
for (int i = 0, l = stringList.size(); i < l; ++i) {
stringList.set(i, stringList.get(i).toLowerCase(Locale.getDefault()).replace('_', ' '));
}
return (String[]) stringList.toArray();
}
/**
* Gets the application context
*
* @return the application context
*/
public static Context getAppContext() {
return mContext;
}
/**
* Gets the RCS service control singleton
*
* @return the RCS service control singleton
*/
public static RcsServiceControl getRcsServiceControl() {
return mRcsServiceControl;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/capabilities/CapabilitiesList.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.capabilities;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.services.rcs.capability.CapabilitiesLog;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.TextView;
/**
* List capabilities from the content provider
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class CapabilitiesList extends RcsActivity {
// @formatter:off
private static final String[] PROJECTION = new String[] {
CapabilitiesLog.BASECOLUMN_ID,
CapabilitiesLog.CONTACT,
CapabilitiesLog.CAPABILITY_IM_SESSION,
CapabilitiesLog.CAPABILITY_FILE_TRANSFER,
CapabilitiesLog.CAPABILITY_IMAGE_SHARE,
CapabilitiesLog.CAPABILITY_VIDEO_SHARE,
CapabilitiesLog.CAPABILITY_GEOLOC_PUSH,
CapabilitiesLog.CAPABILITY_EXTENSIONS,
CapabilitiesLog.AUTOMATA,
CapabilitiesLog.TIMESTAMP
};
// @formatter:on
private static final String SORT_ORDER = CapabilitiesLog.CONTACT + " DESC";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.capabilities_list);
// Set list adapter
ListView view = (ListView) findViewById(android.R.id.list);
TextView emptyView = (TextView) findViewById(android.R.id.empty);
view.setEmptyView(emptyView);
CapabilitiesListAdapter adapter = createListAdapter();
view.setAdapter(adapter);
}
private CapabilitiesListAdapter createListAdapter() {
Cursor cursor = getContentResolver().query(CapabilitiesLog.CONTENT_URI, PROJECTION, null,
null, SORT_ORDER);
if (cursor == null) {
showMessageThenExit(R.string.label_db_failed);
return null;
}
return new CapabilitiesListAdapter(this, cursor);
}
/**
* List adapter
*/
private class CapabilitiesListAdapter extends CursorAdapter {
private RcsContactUtil rcsDisplayName;
/**
* Constructor
*
* @param context Context
* @param c Cursor
*/
public CapabilitiesListAdapter(Context context, Cursor c) {
// TODO use CursorLoader
super(context, c);
rcsDisplayName = RcsContactUtil.getInstance(context);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.capabilities_list_item, parent, false);
CapabilitiesItemViewHolder holder = new CapabilitiesItemViewHolder(view, cursor);
view.setTag(holder);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
CapabilitiesItemViewHolder holder = (CapabilitiesItemViewHolder) view.getTag();
// Set display name from number
String number = cursor.getString(holder.columnContact);
String displayName = rcsDisplayName.getDisplayName(number);
holder.numberText.setText(getString(R.string.label_contact_arg, displayName));
holder.imBox
.setChecked(cursor.getInt(holder.columnCapabilityIm) == CapabilitiesLog.SUPPORTED);
holder.ftBox
.setChecked(cursor.getInt(holder.columnCapabilityFileTransfer) == CapabilitiesLog.SUPPORTED);
holder.ishBox
.setChecked(cursor.getInt(holder.columnCapabilityImageSharing) == CapabilitiesLog.SUPPORTED);
holder.vshBox
.setChecked(cursor.getInt(holder.columnCapabilityVideoSharing) == CapabilitiesLog.SUPPORTED);
holder.geolocBox
.setChecked(cursor.getInt(holder.columnCapabilityGeolocPush) == CapabilitiesLog.SUPPORTED);
String exts = cursor.getString(holder.columnCapabilityExtensions);
if (exts != null) {
exts = exts.replace(';', '\n');
}
holder.extsText.setText(exts);
holder.automataBox
.setChecked(cursor.getInt(holder.columnAutomata) == CapabilitiesLog.SUPPORTED);
long lastRefresh = cursor.getLong(holder.columnTimestamp);
if (lastRefresh == -1) {
holder.lastRefreshText.setVisibility(View.GONE);
} else {
holder.lastRefreshText.setVisibility(View.VISIBLE);
holder.lastRefreshText.setText(DateUtils.getRelativeTimeSpanString(lastRefresh,
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
}
}
}
/**
* A ViewHolder class keeps references to children views to avoid unnecessary calls to
* findViewById() or getColumnIndex() on each row.
*/
private class CapabilitiesItemViewHolder {
public TextView numberText;
public final CheckBox imBox;
public final CheckBox ftBox;
public final CheckBox ishBox;
public final CheckBox vshBox;
public final CheckBox geolocBox;
public final TextView extsText;
public final CheckBox automataBox;
public final TextView lastRefreshText;
public final int columnContact;
public final int columnCapabilityIm;
public final int columnCapabilityImageSharing;
public final int columnCapabilityFileTransfer;
public final int columnCapabilityVideoSharing;
public final int columnCapabilityGeolocPush;
public final int columnCapabilityExtensions;
public final int columnAutomata;
public final int columnTimestamp;
CapabilitiesItemViewHolder(View base, Cursor cursor) {
columnContact = cursor.getColumnIndexOrThrow(CapabilitiesLog.CONTACT);
columnCapabilityIm = cursor
.getColumnIndexOrThrow(CapabilitiesLog.CAPABILITY_IM_SESSION);
columnCapabilityFileTransfer = cursor
.getColumnIndexOrThrow(CapabilitiesLog.CAPABILITY_FILE_TRANSFER);
columnCapabilityImageSharing = cursor
.getColumnIndexOrThrow(CapabilitiesLog.CAPABILITY_IMAGE_SHARE);
columnCapabilityVideoSharing = cursor
.getColumnIndexOrThrow(CapabilitiesLog.CAPABILITY_VIDEO_SHARE);
columnCapabilityGeolocPush = cursor
.getColumnIndexOrThrow(CapabilitiesLog.CAPABILITY_GEOLOC_PUSH);
columnCapabilityExtensions = cursor
.getColumnIndexOrThrow(CapabilitiesLog.CAPABILITY_EXTENSIONS);
columnAutomata = cursor.getColumnIndexOrThrow(CapabilitiesLog.AUTOMATA);
columnTimestamp = cursor.getColumnIndexOrThrow(CapabilitiesLog.TIMESTAMP);
numberText = (TextView) base.findViewById(R.id.number);
imBox = (CheckBox) base.findViewById(R.id.im);
ftBox = (CheckBox) base.findViewById(R.id.file_transfer);
ishBox = (CheckBox) base.findViewById(R.id.image_sharing);
vshBox = (CheckBox) base.findViewById(R.id.video_sharing);
geolocBox = (CheckBox) base.findViewById(R.id.geoloc_push);
extsText = (TextView) base.findViewById(R.id.extensions);
automataBox = (CheckBox) base.findViewById(R.id.automata);
lastRefreshText = (TextView) base.findViewById(R.id.last_refresh);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/capabilities/MyCapabilities.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.capabilities;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.capability.Capabilities;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.widget.CheckBox;
import android.widget.TextView;
/**
* My capabilities
*/
public class MyCapabilities extends RcsActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.capabilities_mine);
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.CAPABILITY)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.CAPABILITY);
}
@Override
protected void onResume() {
super.onResume();
if (isExiting()) {
return;
}
try {
// Get the current capabilities from the RCS contacts API
Capabilities capabilities = getCapabilityApi().getMyCapabilities();
// Set capabilities
CheckBox imageCSh = (CheckBox) findViewById(R.id.image_sharing);
imageCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IMAGE_SHARING));
CheckBox videoCSh = (CheckBox) findViewById(R.id.video_sharing);
videoCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_VIDEO_SHARING));
CheckBox ft = (CheckBox) findViewById(R.id.file_transfer);
ft.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_FILE_TRANSFER));
CheckBox im = (CheckBox) findViewById(R.id.im);
im.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IM));
CheckBox geolocationPush = (CheckBox) findViewById(R.id.geoloc_push);
geolocationPush.setChecked(capabilities
.hasCapabilities(Capabilities.CAPABILITY_GEOLOC_PUSH));
TextView extensions = (TextView) findViewById(R.id.extensions);
extensions.setText(RequestCapabilities.getExtensions(capabilities));
CheckBox automata = (CheckBox) findViewById(R.id.automata);
automata.setChecked(capabilities.isAutomata());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/capabilities/RequestAllCapabilities.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.capabilities;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* Request capabilities of all contacts
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class RequestAllCapabilities extends RcsActivity {
private OnClickListener mBtnSyncListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
intialize();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.capabilities_refresh);
/* Set buttons callback */
Button refreshBtn = (Button) findViewById(R.id.refresh_btn);
refreshBtn.setOnClickListener(mBtnSyncListener);
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.CAPABILITY)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.CAPABILITY);
}
private void intialize() {
mBtnSyncListener = new OnClickListener() {
public void onClick(View v) {
try {
/* Check if the service is available */
try {
if (!getCapabilityApi().isServiceRegistered()) {
showMessage(R.string.error_not_registered);
return;
}
} catch (RcsServiceNotAvailableException e) {
showMessage(R.string.label_service_not_available);
return;
}
/* Refresh all contacts */
getCapabilityApi().requestAllContactsCapabilities();
/* Display message */
Utils.displayLongToast(RequestAllCapabilities.this,
getString(R.string.label_refresh_success));
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/capabilities/RequestCapabilities.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.capabilities;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.capability.Capabilities;
import com.gsma.services.rcs.capability.CapabilitiesListener;
import com.gsma.services.rcs.capability.CapabilityService;
import com.gsma.services.rcs.contact.ContactId;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.HashSet;
import java.util.Set;
/**
* Refresh capabilities of a given contact
*
* @author Jean-Marc AUFFRET
*/
public class RequestCapabilities extends RcsActivity {
private final Handler mHandler = new Handler();
private MyCapabilitiesListener mCapabilitiesListener = new MyCapabilitiesListener();
private static final String EXTENSION_SEPARATOR = "\n";
private static final String LOGTAG = LogUtils.getTag(RequestCapabilities.class.getSimpleName());
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
private OnClickListener mBtnRefreshListener;
private OnItemSelectedListener mListenerContact;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialize();
/* Set layout */
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.capabilities_request);
/* Set the contact selector */
mSpinner = (Spinner) findViewById(R.id.contact);
mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this));
mSpinner.setOnItemSelectedListener(mListenerContact);
/* Set button callback */
Button refreshBtn = (Button) findViewById(R.id.refresh_btn);
refreshBtn.setOnClickListener(mBtnRefreshListener);
/* Update refresh button */
if (mSpinner.getAdapter().getCount() == 0) {
// Disable button if no contact available
refreshBtn.setEnabled(false);
} else {
refreshBtn.setEnabled(true);
}
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.CAPABILITY)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.CAPABILITY);
try {
getCapabilityApi().addCapabilitiesListener(mCapabilitiesListener);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (isServiceConnected(RcsServiceName.CAPABILITY)) {
// Remove image sharing listener
try {
getCapabilityApi().removeCapabilitiesListener(mCapabilitiesListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
/**
* Capabilities event listener
*/
private class MyCapabilitiesListener extends CapabilitiesListener {
/**
* Callback called when new capabilities are received for a given contact
*
* @param contact Contact
* @param capabilities Capabilities
*/
public void onCapabilitiesReceived(final ContactId contact, final Capabilities capabilities) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCapabilitiesReceived " + contact);
}
final ContactId selectedContact = getSelectedContact();
if (!contact.equals(selectedContact)) {
// Discard capabilities if not for selected contact
return;
}
mHandler.post(new Runnable() {
public void run() {
// Check if this intent concerns the current selected
// contact
if (contact.equals(selectedContact)) {
// Update UI
displayCapabilities(capabilities);
}
}
});
}
}
/**
* Returns the selected contact
*
* @return Contact
*/
private ContactId getSelectedContact() {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
return ContactUtil.formatContact(adapter.getSelectedNumber(mSpinner.getSelectedView()));
}
private void updateCapabilities(ContactId contact) {
// Display info
Utils.displayLongToast(RequestCapabilities.this,
getString(R.string.label_request_in_background, contact));
try {
Set contactSet = new HashSet<>();
contactSet.add(contact);
getCapabilityApi().requestContactCapabilities(contactSet);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void displayCapabilities(Capabilities capabilities) {
CheckBox imageCSh = (CheckBox) findViewById(R.id.image_sharing);
CheckBox videoCSh = (CheckBox) findViewById(R.id.video_sharing);
CheckBox ft = (CheckBox) findViewById(R.id.file_transfer);
CheckBox im = (CheckBox) findViewById(R.id.im);
CheckBox geoloc = (CheckBox) findViewById(R.id.geoloc_push);
TextView extensions = (TextView) findViewById(R.id.extensions);
TextView timestamp = (TextView) findViewById(R.id.last_refresh);
CheckBox automata = (CheckBox) findViewById(R.id.automata);
if (capabilities != null) {
// Set capabilities
imageCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IMAGE_SHARING));
videoCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_VIDEO_SHARING));
ft.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_FILE_TRANSFER));
im.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IM));
geoloc.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_GEOLOC_PUSH));
}
// Set extensions
extensions.setVisibility(View.VISIBLE);
extensions.setText(getExtensions(capabilities));
automata.setChecked((capabilities != null) && capabilities.isAutomata());
timestamp.setText((capabilities != null) ? DateUtils.getRelativeTimeSpanString(
capabilities.getTimestamp(), System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE) : "");
}
/* package private */static String getExtensions(Capabilities capabilities) {
if (capabilities == null || capabilities.getSupportedExtensions().isEmpty()) {
return "";
}
StringBuilder extensions = new StringBuilder();
for (String capability : capabilities.getSupportedExtensions()) {
extensions.append(EXTENSION_SEPARATOR).append(capability);
}
return extensions.substring(EXTENSION_SEPARATOR.length());
}
private void initialize() {
mBtnRefreshListener = new OnClickListener() {
public void onClick(View v) {
// Check if the service is available
try {
if (!getCapabilityApi().isServiceRegistered()) {
showMessage(R.string.error_not_registered);
return;
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return;
}
ContactId contact = getSelectedContact();
if (contact != null) {
updateCapabilities(contact);
}
}
};
mListenerContact = new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
CapabilityService capabilityApi = getCapabilityApi();
try {
// Get selected contact
ContactId contactId = getSelectedContact();
// Get current capabilities
Capabilities currentCapabilities = capabilityApi
.getContactCapabilities(contactId);
// Display default capabilities
displayCapabilities(currentCapabilities);
if (currentCapabilities == null) {
Utils.displayLongToast(RequestCapabilities.this,
getString(R.string.label_no_capabilities, contactId.toString()));
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onNothingSelected(AdapterView> arg0) {
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/capabilities/TestCapabilitiesApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.capabilities;
import com.gsma.rcs.ri.R;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* Capabilities API
*
* @author Jean-Marc AUFFRET
*/
public class TestCapabilitiesApi extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Set items
String[] items = {
getString(R.string.menu_my_capabilities),
getString(R.string.menu_capabilities_request),
getString(R.string.menu_refresh_capabilities),
getString(R.string.menu_capabilities_log)
};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, MyCapabilities.class));
break;
case 1:
startActivity(new Intent(this, RequestCapabilities.class));
break;
case 2:
startActivity(new Intent(this, RequestAllCapabilities.class));
break;
case 3:
startActivity(new Intent(this, CapabilitiesList.class));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/contacts/BlockingContact.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.contacts;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.contact.RcsContact;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import android.widget.ToggleButton;
/**
* Block/unblock a contact
*
* @author Jean-Marc AUFFRET
*/
public class BlockingContact extends RcsActivity {
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
private ToggleButton mToggleBtn;
private OnClickListener mToggleListener;
private OnItemSelectedListener mListenerContact;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialize();
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.contacts_blocking);
// Register to API connection manager
if (!isServiceConnected(RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.CONTACT);
// Set the contact selector
mSpinner = (Spinner) findViewById(R.id.contact);
ContactListAdapter adapter = ContactListAdapter.createRcsContactListAdapter(this);
mSpinner.setAdapter(adapter);
mSpinner.setOnItemSelectedListener(mListenerContact);
// Set button callback
mToggleBtn = (ToggleButton) findViewById(R.id.block_btn);
mToggleBtn.setOnClickListener(mToggleListener);
// Update refresh button
if (mSpinner.getAdapter().getCount() == 0) {
// Disable button if no contact available
mToggleBtn.setEnabled(false);
} else {
mToggleBtn.setEnabled(true);
}
}
private void updateBlockingState(ContactId contactId) {
try {
RcsContact contact = getContactApi().getRcsContact(contactId);
mToggleBtn.setChecked(contact.isBlocked());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private ContactId getSelectedContact() {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
return ContactUtil.formatContact(adapter.getSelectedNumber(mSpinner.getSelectedView()));
}
private void initialize() {
mToggleListener = new OnClickListener() {
public void onClick(View view) {
try {
ContactId contact = getSelectedContact();
if (mToggleBtn.isChecked()) {
// Block the contact
getContactApi().blockContact(contact);
Utils.displayToast(BlockingContact.this,
getString(R.string.label_contact_blocked, contact.toString()));
} else {
// Unblock the contact
getContactApi().unblockContact(contact);
Utils.displayToast(BlockingContact.this,
getString(R.string.label_contact_unblocked, contact.toString()));
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mListenerContact = new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
ContactId contactId = getSelectedContact();
updateBlockingState(contactId);
}
@Override
public void onNothingSelected(AdapterView> arg0) {
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/contacts/ContactVCard.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.contacts;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.contact.ContactUtil;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import java.io.File;
/**
* Display contact VCard
*
* @author Jean-Marc AUFFRET
*/
public class ContactVCard extends RcsActivity {
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.contacts_vcard);
// Set the contact selector
mSpinner = (Spinner) findViewById(R.id.contact);
mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this));
mSpinner.setOnItemSelectedListener(listenerContact);
// Set button callback
Button showBtn = (Button) findViewById(R.id.show_btn);
showBtn.setOnClickListener(btnShowListener);
}
/**
* Spinner contact listener
*/
private OnItemSelectedListener listenerContact = new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
// Get selected contact
String contact = getSelectedContact();
// Update UI
displayVisitCard(contact);
}
@Override
public void onNothingSelected(AdapterView> arg0) {
}
};
/**
* Returns the selected contact
*
* @return Contact
*/
private String getSelectedContact() {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
return adapter.getSelectedNumber(mSpinner.getSelectedView());
}
/**
* Display the visit card
*
* @param contact Contact
*/
private void displayVisitCard(String contact) {
try {
Uri contactUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(contact));
Uri vcard = ContactUtil.getInstance(this).getVCard(contactUri);
TextView vcardView = (TextView) findViewById(R.id.vcard);
vcardView.setText(vcard.getPath());
} catch (RcsGenericException e) {
showExceptionThenExit(e);
}
}
/**
* Show button listener
*/
private OnClickListener btnShowListener = new OnClickListener() {
public void onClick(View v) {
// Get filename
TextView vcardView = (TextView) findViewById(R.id.vcard);
String filename = vcardView.getText().toString();
// Show the transferred vCard
File file = new File(filename);
Uri uri = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "text/plain");
startActivity(intent);
}
};
}
================================================
FILE: RI/src/com/gsma/rcs/ri/contacts/RcsContactsList.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.contacts;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsListActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.capability.Capabilities;
import com.gsma.services.rcs.contact.RcsContact;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* List of RCS contacts
*
* @author Philippe LEMORDANT
* @author Jean-Marc AUFFRET
*/
public class RcsContactsList extends RcsListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.contacts_rcs_list);
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.CONTACT);
}
@Override
protected void onResume() {
super.onResume();
if (isExiting()) {
return;
}
updateList();
}
private void updateList() {
try {
Set allContacts = getContactApi().getRcsContacts();
List contacts = new ArrayList<>(allContacts);
if (contacts.size() > 0) {
ContactArrayAdapter adapter = new ContactArrayAdapter(this,
R.layout.contact_list_item, contacts);
setListAdapter(adapter);
} else {
setListAdapter(null);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private class ContactArrayAdapter extends ArrayAdapter {
private final Context mCtx;
private final int mResourceRowLayout;
private final RcsContactUtil mRcsContactUtil;
private final LayoutInflater mInflater;
public ContactArrayAdapter(Context ctx, int resourceRowLayout, List items) {
super(ctx, 0, items);
mCtx = ctx;
mResourceRowLayout = resourceRowLayout;
mInflater = LayoutInflater.from(mCtx);
mRcsContactUtil = RcsContactUtil.getInstance(mCtx);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final CapabilitiesItemViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(mResourceRowLayout, parent, false);
holder = new CapabilitiesItemViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (CapabilitiesItemViewHolder) convertView.getTag();
}
RcsContact item = getItem(position);
if (item != null) {
String displayName = mRcsContactUtil.getDisplayName(item.getContactId());
holder.numberText.setText(getString(R.string.label_contact_arg, displayName));
Capabilities capa = item.getCapabilities();
holder.imBox.setChecked(capa.hasCapabilities(Capabilities.CAPABILITY_IM));
holder.ftBox
.setChecked(capa.hasCapabilities(Capabilities.CAPABILITY_FILE_TRANSFER));
holder.ishBox.setChecked(capa
.hasCapabilities(Capabilities.CAPABILITY_IMAGE_SHARING));
holder.vshBox.setChecked(capa
.hasCapabilities(Capabilities.CAPABILITY_VIDEO_SHARING));
holder.geolocBox.setChecked(capa
.hasCapabilities(Capabilities.CAPABILITY_GEOLOC_PUSH));
holder.extsText.setText(capa.getSupportedExtensions().toString());
holder.automataBox.setChecked(capa.isAutomata());
long lastRefresh = capa.getTimestamp();
if (lastRefresh == -1) {
holder.lastRefreshText.setVisibility(View.GONE);
} else {
holder.lastRefreshText.setVisibility(View.VISIBLE);
holder.lastRefreshText.setText(DateUtils.getRelativeTimeSpanString(lastRefresh,
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
}
holder.onLineBox.setChecked(item.isOnline());
boolean blocked = item.isBlocked();
holder.blockedBox.setChecked(blocked);
long blockingTime = item.getBlockingTimestamp();
if (!blocked) {
holder.blockingTimeText.setVisibility(View.GONE);
} else {
holder.blockingTimeText.setVisibility(View.VISIBLE);
holder.blockingTimeText.setText(DateUtils.getRelativeTimeSpanString(
blockingTime, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
}
}
return convertView;
}
}
/**
* A ViewHolder class keeps references to children views to avoid unnecessary calls to
* findViewById().
*/
private class CapabilitiesItemViewHolder {
public final CheckBox onLineBox;
public final CheckBox blockedBox;
public final TextView blockingTimeText;
public final TextView numberText;
public final CheckBox imBox;
public final CheckBox ftBox;
public final CheckBox ishBox;
public final CheckBox vshBox;
public final CheckBox geolocBox;
public final TextView extsText;
public final CheckBox automataBox;
public final TextView lastRefreshText;
CapabilitiesItemViewHolder(View base) {
numberText = (TextView) base.findViewById(R.id.number);
imBox = (CheckBox) base.findViewById(R.id.im);
ftBox = (CheckBox) base.findViewById(R.id.file_transfer);
ishBox = (CheckBox) base.findViewById(R.id.image_sharing);
vshBox = (CheckBox) base.findViewById(R.id.video_sharing);
geolocBox = (CheckBox) base.findViewById(R.id.geoloc_push);
extsText = (TextView) base.findViewById(R.id.extensions);
automataBox = (CheckBox) base.findViewById(R.id.automata);
lastRefreshText = (TextView) base.findViewById(R.id.last_refresh);
onLineBox = (CheckBox) base.findViewById(R.id.is_online);
blockedBox = (CheckBox) base.findViewById(R.id.is_blocked);
blockingTimeText = (TextView) base.findViewById(R.id.block_timestamp);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/contacts/TestContactsApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.contacts;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsListActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* CONTACTS API
*
* @author Jean-Marc AUFFRET
*/
public class TestContactsApi extends RcsListActivity {
private static final String LOGTAG = LogUtils.getTag(TestContactsApi.class.getSimpleName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Set items
String[] items = {
getString(R.string.menu_address_book), getString(R.string.menu_list_rcs_contacts),
getString(R.string.menu_contact_vcard), getString(R.string.menu_blocking_contact)
};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
try {
startActivity(new Intent(Intent.ACTION_VIEW)
.setType(ContactsContract.Contacts.CONTENT_TYPE));
} catch (ActivityNotFoundException e1) {
try {
startActivity(new Intent("com.android.contacts.action.LIST_DEFAULT"));
} catch (ActivityNotFoundException e2) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e2));
showMessage(R.string.label_ab_not_found);
}
}
break;
case 1:
startActivity(new Intent(this, RcsContactsList.class));
break;
case 2:
startActivity(new Intent(this, ContactVCard.class));
break;
case 3:
startActivity(new Intent(this, BlockingContact.class));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/InitiateMultimediaSession.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.contact.ContactId;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Spinner;
/**
* Abstract class to initiate a multimedia session
*
* @author Jean-Marc AUFFRET
*/
public abstract class InitiateMultimediaSession extends Activity {
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.extension_initiate_session);
// Set contact selector
mSpinner = (Spinner) findViewById(R.id.contact);
mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this));
// Set buttons callback
Button initiateBtn = (Button) findViewById(R.id.initiate_btn);
initiateBtn.setOnClickListener(btnInitiateListener);
// Disable button if no contact available
if (mSpinner.getAdapter().getCount() == 0) {
initiateBtn.setEnabled(false);
}
}
/**
* Initiate button callback
*/
private OnClickListener btnInitiateListener = new OnClickListener() {
public void onClick(View v) {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
ContactId contact = ContactUtil.formatContact(phoneNumber);
// Initiate session
initiateSession(contact);
// Exit activity
finish();
}
};
/**
* Initiate session
*
* @param contact Remote contact
*/
public abstract void initiateSession(ContactId contact);
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/InstantMessageReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.extension.InstantMultimediaMessageIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
* Messaging session invitation receiver
*
* @author jmauffret
*/
public class InstantMessageReceiver extends BroadcastReceiver {
private static final String LOGTAG = LogUtils.getTag(InstantMessageReceiver.class
.getSimpleName());
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!InstantMultimediaMessageIntent.ACTION_NEW_INSTANT_MESSAGE.equals(action)) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Unknown action=" + action);
}
return;
}
/* Display instant message content */
String content = new String(
intent.getByteArrayExtra(InstantMultimediaMessageIntent.EXTRA_CONTENT));
ContactId contact = intent.getParcelableExtra(InstantMultimediaMessageIntent.EXTRA_CONTACT);
String displayName = RcsContactUtil.getInstance(context).getDisplayName(contact);
Utils.displayLongToast(context,
context.getString(R.string.label_recv_instant_messsage, displayName) + "\n"
+ content);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/MultiMediaSessionIntentService.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension;
import com.gsma.rcs.api.connection.ConnectionManager;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.extension.messaging.MessagingSessionView;
import com.gsma.rcs.ri.extension.streaming.StreamingSessionView;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.extension.MultimediaMessagingSession;
import com.gsma.services.rcs.extension.MultimediaMessagingSessionIntent;
import com.gsma.services.rcs.extension.MultimediaStreamingSession;
import com.gsma.services.rcs.extension.MultimediaStreamingSessionIntent;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* Process the MultiMedia Session invitation
* Purpose is to retrieve the contactId from the service to build the notification.
*
* @author YPLO6403
*/
public class MultiMediaSessionIntentService extends IntentService {
private String mSessionId;
private boolean mMultimediaMessagingSession = false;
private ConnectionManager mCnxManager;
private static final String LOGTAG = LogUtils.getTag(MultiMediaSessionIntentService.class
.getSimpleName());
/**
* Constructor
*/
public MultiMediaSessionIntentService() {
super("MultiMediaSessionIntentService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
/*
* We want this service to stop running if forced stop so return not sticky.
*/
return START_NOT_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
String action;
if ((action = intent.getAction()) == null) {
return;
}
if (MultimediaMessagingSessionIntent.ACTION_NEW_INVITATION.equals(action)) {
mMultimediaMessagingSession = true;
} else {
if (!MultimediaStreamingSessionIntent.ACTION_NEW_INVITATION.equals(action)) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Unknown action=".concat(action));
}
return;
}
}
/* Since there is no provider associated to multimedia sessions, we must connect to the API */
mCnxManager = ConnectionManager.getInstance();
if (!mCnxManager.isServiceConnected(RcsServiceName.MULTIMEDIA)) {
return;
}
// Get invitation info
mSessionId = intent.getStringExtra(MultimediaMessagingSessionIntent.EXTRA_SESSION_ID);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onHandleIntent sessionId=".concat(mSessionId));
}
initiateSession(intent);
}
private void initiateSession(Intent intent) {
try {
if (mMultimediaMessagingSession) {
MultimediaMessagingSession mms = mCnxManager.getMultimediaSessionApi()
.getMessagingSession(mSessionId);
if (mms != null) {
addSessionInvitationNotification(intent, mms.getRemoteContact());
} else {
if (LogUtils.isActive) {
Log.w(LOGTAG, "Cannot get messaging session for ID ".concat(mSessionId));
}
}
} else {
MultimediaStreamingSession mss = mCnxManager.getMultimediaSessionApi()
.getStreamingSession(mSessionId);
if (mss != null) {
addSessionInvitationNotification(intent, mss.getRemoteContact());
} else {
if (LogUtils.isActive) {
Log.w(LOGTAG, "Cannot get streaming session for ID ".concat(mSessionId));
}
}
}
} catch (RcsServiceException e) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot get MM API", e);
}
}
}
private void addSessionInvitationNotification(Intent intent, ContactId contact) {
/* Create pending intent */
Intent invitation = new Intent(intent);
String title;
if (mMultimediaMessagingSession) {
invitation.setClass(this, MessagingSessionView.class);
title = getString(R.string.title_recv_messaging_session);
} else {
invitation.setClass(this, StreamingSessionView.class);
title = getString(R.string.title_recv_streaming_session);
}
invitation.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*
* If the PendingIntent has the same operation, action, data, categories, components, and
* flags it will be replaced. Invitation should be notified individually so we use a random
* generator to provide a unique request code and reuse it for the notification.
*/
int uniqueId = Utils.getUniqueIdForPendingIntent();
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, invitation,
PendingIntent.FLAG_ONE_SHOT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
/* Create notification */
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(contentIntent);
notif.setSmallIcon(R.drawable.ri_notif_mm_session_icon);
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(true);
notif.setOnlyAlertOnce(true);
notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
notif.setDefaults(Notification.DEFAULT_VIBRATE);
notif.setContentTitle(title);
notif.setContentText(getString(R.string.label_from_args, displayName));
/* Send notification */
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(uniqueId, notif.build());
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/MultimediaSessionList.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsListActivity;
import com.gsma.rcs.ri.R;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
/**
* Abstract list of multimedia sessions
*
* @author Jean-Marc AUFFRET
*/
public abstract class MultimediaSessionList extends RcsListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.extension_session_list);
// Register to API connection manager
if (!isServiceConnected(RcsServiceName.MULTIMEDIA)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.MULTIMEDIA);
}
@Override
protected void onResume() {
super.onResume();
if (isExiting()) {
return;
}
updateList();
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
displaySession(position);
}
/**
* Display a session
*
* @param position position
*/
public abstract void displaySession(int position);
/**
* Update the displayed list
*/
public abstract void updateList();
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/SendInstantMessage.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension;
import com.gsma.rcs.api.connection.ConnectionManager;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.extension.messaging.MessagingSessionUtils;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Spinner;
/**
* Send an instant multimedia message
*
* @author Jean-Marc AUFFRET
*/
public class SendInstantMessage extends RcsActivity {
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.extension_send_instant_message);
// Set contact selector
mSpinner = (Spinner) findViewById(R.id.contact);
mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this));
// Set buttons callback
Button sendBtn = (Button) findViewById(R.id.send_btn);
sendBtn.setOnClickListener(btnSendListener);
/* Register to API connection manager */
if (!isServiceConnected(ConnectionManager.RcsServiceName.MULTIMEDIA,
ConnectionManager.RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(ConnectionManager.RcsServiceName.MULTIMEDIA,
ConnectionManager.RcsServiceName.CONTACT);
// Disable button if no contact available
if (mSpinner.getAdapter().getCount() == 0) {
sendBtn.setEnabled(false);
}
}
/**
* Send button callback
*/
private View.OnClickListener btnSendListener = new View.OnClickListener() {
public void onClick(View v) {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
ContactId contact = ContactUtil.formatContact(phoneNumber);
// Initiate session
sendMessage(contact);
}
};
/**
* Send message to a remote contact
*
* @param contact Remote contact
*/
public void sendMessage(ContactId contact) {
try {
String content = "Hello world";
getMultimediaSessionApi().sendInstantMultimediaMessage(
MessagingSessionUtils.SERVICE_ID, contact, content.getBytes(),
MessagingSessionUtils.SERVICE_CONTENT_TYPE);
Utils.displayToast(this, getString(R.string.label_instant_message_sent));
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/SessionInvitationReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Messaging session invitation receiver
*
* @author Jean-Marc AUFFRET
*/
public class SessionInvitationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent invitation) {
invitation.setClass(context, MultiMediaSessionIntentService.class);
context.startService(invitation);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/TestMultimediaSessionApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.extension.messaging.InitiateMessagingSession;
import com.gsma.rcs.ri.extension.messaging.MessagingSessionList;
import com.gsma.rcs.ri.extension.streaming.InitiateStreamingSession;
import com.gsma.rcs.ri.extension.streaming.StreamingSessionList;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* MM session API
*
* @author Jean-Marc AUFFRET
*/
public class TestMultimediaSessionApi extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Set items
String[] items = {
getString(R.string.menu_initiate_messaging_session),
getString(R.string.menu_messaging_sessions_list),
getString(R.string.menu_initiate_streaming_session),
getString(R.string.menu_streaming_sessions_list),
getString(R.string.menu_instant_message)
};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, InitiateMessagingSession.class));
break;
case 1:
startActivity(new Intent(this, MessagingSessionList.class));
break;
case 2:
startActivity(new Intent(this, InitiateStreamingSession.class));
break;
case 3:
startActivity(new Intent(this, StreamingSessionList.class));
break;
case 4:
startActivity(new Intent(this, SendInstantMessage.class));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/messaging/InitiateMessagingSession.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.messaging;
import com.gsma.rcs.ri.extension.InitiateMultimediaSession;
import com.gsma.services.rcs.contact.ContactId;
import android.content.Intent;
import android.os.Parcelable;
/**
* Initiate messaging session
*
* @author Jean-Marc AUFFRET
*/
public class InitiateMessagingSession extends InitiateMultimediaSession {
/**
* Initiate session
*
* @param contact Remote contact
*/
public void initiateSession(ContactId contact) {
// Display session view
Intent intent = new Intent(InitiateMessagingSession.this, MessagingSessionView.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(MessagingSessionView.EXTRA_MODE, MessagingSessionView.MODE_OUTGOING);
intent.putExtra(MessagingSessionView.EXTRA_CONTACT, (Parcelable) contact);
startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/messaging/MessagingSessionList.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.messaging;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.extension.MultimediaSessionList;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.extension.MultimediaMessagingSession;
import android.content.Intent;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* List of messaging sessions in progress
*
* @author Jean-Marc AUFFRET
*/
public class MessagingSessionList extends MultimediaSessionList {
/**
* List of sessions
*/
private List sessions = new ArrayList<>();
/**
* Display a session
*
* @param position position
*/
public void displaySession(int position) {
try {
Intent intent = new Intent(this, MessagingSessionView.class);
String sessionId = sessions.get(position).getSessionId();
intent.putExtra(MessagingSessionView.EXTRA_MODE, MessagingSessionView.MODE_OPEN);
intent.putExtra(MessagingSessionView.EXTRA_SESSION_ID, sessionId);
startActivity(intent);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
/**
* Update the displayed list
*/
public void updateList() {
try {
// Reset the list
sessions.clear();
// Get list of pending sessions
Set currentSessions = getMultimediaSessionApi()
.getMessagingSessions(MessagingSessionUtils.SERVICE_ID);
sessions = new ArrayList<>(currentSessions);
if (sessions.size() > 0) {
String[] items = new String[sessions.size()];
for (int i = 0; i < items.length; i++) {
items[i] = getString(R.string.label_session, sessions.get(i).getSessionId());
}
setListAdapter(new ArrayAdapter<>(MessagingSessionList.this,
android.R.layout.simple_list_item_1, items));
} else {
setListAdapter(null);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/messaging/MessagingSessionUtils.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.messaging;
/**
* Messaging service utils
*
* @author Jean-Marc AUFFRET
*/
public class MessagingSessionUtils {
/**
* Service ID constant
*/
public final static String SERVICE_ID = "ext.messaging";
/**
* Service content type
*/
public final static String SERVICE_CONTENT_TYPE = "plain/text";
/**
* Service accept-type
*/
public final static String[] SERVICE_ACCEPT_TYPE = {
"plain/text"
};
/**
* Service accept-wrapped-type
*/
public final static String[] SERVICE_WRAPPED_ACCEPT_TYPE = {
// no wrapped here
};
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/messaging/MessagingSessionView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.messaging;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.extension.MultimediaMessagingSession;
import com.gsma.services.rcs.extension.MultimediaMessagingSessionIntent;
import com.gsma.services.rcs.extension.MultimediaMessagingSessionListener;
import com.gsma.services.rcs.extension.MultimediaSession;
import com.gsma.services.rcs.extension.MultimediaSessionService;
import com.gsma.services.rcs.extension.MultimediaSessionServiceConfiguration;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
/**
* Messaging session view
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class MessagingSessionView extends RcsActivity {
/**
* View mode: incoming session
*/
public final static int MODE_INCOMING = 0;
/**
* View mode: outgoing session
*/
public final static int MODE_OUTGOING = 1;
/**
* View mode: open session history
*/
public final static int MODE_OPEN = 2;
/**
* Intent parameter: view mode
*/
public final static String EXTRA_MODE = "mode";
/**
* Intent parameter: session ID
*/
public final static String EXTRA_SESSION_ID = "session_id";
/**
* Intent parameter: contact
*/
public final static String EXTRA_CONTACT = "contact";
private final Handler mHandler = new Handler();
private String mSessionId;
private ContactId mContact;
private String mServiceId = MessagingSessionUtils.SERVICE_ID;
private MultimediaMessagingSession mSession;
private Dialog mProgressDialog;
private android.view.View.OnClickListener mBtnSendListener;
private MultimediaMessagingSessionListener mServiceListener;
private OnClickListener mAcceptBtnListener;
private OnClickListener mDeclineBtnListener;
private static final String LOGTAG = LogUtils
.getTag(MessagingSessionView.class.getSimpleName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialize();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.extension_messaging_session_view);
/* Set buttons callback */
Button sendBtn = (Button) findViewById(R.id.send_btn);
sendBtn.setOnClickListener(mBtnSendListener);
sendBtn.setEnabled(false);
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.MULTIMEDIA, RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.MULTIMEDIA, RcsServiceName.CONTACT);
try {
getMultimediaSessionApi().addEventListener(mServiceListener);
initialiseMessagingSession(getIntent());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isServiceConnected(RcsServiceName.MULTIMEDIA)) {
try {
getMultimediaSessionApi().removeEventListener(mServiceListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
private void acceptInvitation() {
try {
mSession.acceptInvitation();
/*
* Wait for the SIP-ACK to allow the user to send message once session is established.
*/
showProgressDialog();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void rejectInvitation() {
try {
mSession.rejectInvitation();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void initialiseMessagingSession(Intent intent) {
MultimediaSessionService sessionApi = getMultimediaSessionApi();
try {
MultimediaSessionServiceConfiguration config = sessionApi.getConfiguration();
if (LogUtils.isActive) {
Log.d(LOGTAG,
"MessageMaxLength: ".concat(Integer.toString(config.getMessageMaxLength())));
}
int mode = intent.getIntExtra(MessagingSessionView.EXTRA_MODE, -1);
if (mode == MessagingSessionView.MODE_OUTGOING) {
/* Outgoing session: Check if the service is available. */
if (!sessionApi.isServiceRegistered()) {
showMessageThenExit(R.string.error_not_registered);
return;
}
mContact = intent.getParcelableExtra(MessagingSessionView.EXTRA_CONTACT);
startSession();
if (mSession == null) {
return;
}
} else if (mode == MessagingSessionView.MODE_OPEN) {
/* Open existing session. */
mSessionId = intent.getStringExtra(MessagingSessionView.EXTRA_SESSION_ID);
mSession = sessionApi.getMessagingSession(mSessionId);
if (mSession == null) {
showMessageThenExit(R.string.label_session_has_expired);
return;
}
mContact = mSession.getRemoteContact();
} else {
/* Incoming session from its Intent */
mSessionId = intent
.getStringExtra(MultimediaMessagingSessionIntent.EXTRA_SESSION_ID);
mSession = sessionApi.getMessagingSession(mSessionId);
if (mSession == null) {
showMessageThenExit(R.string.label_session_has_expired);
return;
}
mContact = mSession.getRemoteContact();
String from = RcsContactUtil.getInstance(this).getDisplayName(mContact);
/* Manual accept */
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_messaging_session);
builder.setMessage(getString(R.string.label_mm_from_id, from, mServiceId));
builder.setCancelable(false);
builder.setIcon(R.drawable.ri_notif_mm_session_icon);
builder.setPositiveButton(R.string.label_accept, mAcceptBtnListener);
builder.setNegativeButton(R.string.label_decline, mDeclineBtnListener);
registerDialog(builder.show());
}
/* Display session info */
TextView featureTagEdit = (TextView) findViewById(R.id.feature_tag);
featureTagEdit.setText(mServiceId);
String from = RcsContactUtil.getInstance(this).getDisplayName(mContact);
TextView contactEdit = (TextView) findViewById(R.id.contact);
contactEdit.setText(from);
Button sendBtn = (Button) findViewById(R.id.send_btn);
if (MultimediaSession.State.STARTED == mSession.getState()) {
sendBtn.setEnabled(true);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void startSession() {
try {
mSession = getMultimediaSessionApi().initiateMessagingSession(mServiceId, mContact,
MessagingSessionUtils.SERVICE_ACCEPT_TYPE,
MessagingSessionUtils.SERVICE_WRAPPED_ACCEPT_TYPE);
mSessionId = mSession.getSessionId();
showProgressDialog();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void showProgressDialog() {
mProgressDialog = showProgressDialog(getString(R.string.label_command_in_progress));
mProgressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
Toast.makeText(MessagingSessionView.this,
getString(R.string.label_session_canceled), Toast.LENGTH_SHORT).show();
quitSession();
}
});
}
private void hideProgressDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
mProgressDialog = null;
}
}
private void quitSession() {
try {
if (mSession != null && RcsSessionUtil.isAllowedToAbortMultimediaSession(mSession)) {
mSession.abortSession();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mSession = null;
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
if (mSession != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Quit the session
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.setCancelable(true);
registerDialog(builder.show());
} else {
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_mm_session, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void initialize() {
mBtnSendListener = new android.view.View.OnClickListener() {
private int i = 0;
public void onClick(View v) {
try {
String data = "data".concat(String.valueOf(i++));
mSession.sendMessage(data.getBytes(),
MessagingSessionUtils.SERVICE_CONTENT_TYPE);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mServiceListener = new MultimediaMessagingSessionListener() {
@Override
public void onStateChanged(ContactId contact, String sessionId,
final MultimediaSession.State state, MultimediaSession.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " sessionId=" + sessionId
+ " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current sessionId */
if (mSessionId == null || !mSessionId.equals(sessionId)) {
return;
}
final String _reasonCode = RiApplication.sMultimediaReasonCodes[reasonCode.toInt()];
mHandler.post(new Runnable() {
public void run() {
switch (state) {
case STARTED:
/* Session is established: hide progress dialog */
hideProgressDialog();
/* Activate the send button */
Button sendBtn = (Button) findViewById(R.id.send_btn);
sendBtn.setEnabled(true);
break;
case ABORTED:
showMessageThenExit(getString(R.string.label_session_aborted,
_reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_session_rejected,
_reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_session_failed,
_reasonCode));
break;
default:
if (LogUtils.isActive) {
Log.d(LOGTAG,
"onStateChanged "
+ getString(R.string.label_mms_state_changed,
RiApplication.sMultimediaStates[state
.toInt()], _reasonCode));
}
}
}
});
}
@Override
public void onMessageReceived(ContactId contact, String sessionId, byte[] content) {
// Deprecated since TAPI 1.6
}
@Override
public void onMessageReceived(ContactId contact, String sessionId, byte[] content,
String contentType) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMessageReceived contact=" + contact + " sessionId="
+ sessionId);
}
/* Discard event if not for current sessionId */
if (mSessionId == null || !mSessionId.equals(sessionId)) {
return;
}
final String data = new String(content);
mHandler.post(new Runnable() {
public void run() {
/* Display received data */
TextView txt = (TextView) MessagingSessionView.this
.findViewById(R.id.recv_data);
txt.setText(data);
}
});
}
@Override
public void onMessagesFlushed(ContactId contact, String sessionId) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMessagesFlushed contact=" + contact + " sessionId="
+ sessionId);
}
}
};
mAcceptBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
acceptInvitation();
}
};
mDeclineBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
rejectInvitation();
/* Exit activity */
finish();
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/streaming/InitiateStreamingSession.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.streaming;
import com.gsma.rcs.ri.extension.InitiateMultimediaSession;
import com.gsma.services.rcs.contact.ContactId;
import android.content.Intent;
import android.os.Parcelable;
/**
* Initiate streaming session
*
* @author Jean-Marc AUFFRET
*/
public class InitiateStreamingSession extends InitiateMultimediaSession {
/**
* Initiate session
*
* @param contact Remote contact
*/
public void initiateSession(ContactId contact) {
// Display session view
Intent intent = new Intent(InitiateStreamingSession.this, StreamingSessionView.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(StreamingSessionView.EXTRA_MODE, StreamingSessionView.MODE_OUTGOING);
intent.putExtra(StreamingSessionView.EXTRA_CONTACT, (Parcelable) contact);
startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/streaming/StreamingSessionList.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.streaming;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.extension.MultimediaSessionList;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.extension.MultimediaStreamingSession;
import android.content.Intent;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* List of streaming sessions in progress
*
* @author Jean-Marc AUFFRET
*/
public class StreamingSessionList extends MultimediaSessionList {
/**
* List of sessions
*/
private List sessions = new ArrayList<>();
/**
* Display a session
*
* @param position position
*/
public void displaySession(int position) {
try {
Intent intent = new Intent(this, StreamingSessionView.class);
String sessionId = sessions.get(position).getSessionId();
intent.putExtra(StreamingSessionView.EXTRA_MODE, StreamingSessionView.MODE_OPEN);
intent.putExtra(StreamingSessionView.EXTRA_SESSION_ID, sessionId);
startActivity(intent);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
/**
* Update the displayed list
*/
public void updateList() {
try {
// Reset the list
sessions.clear();
// Get list of pending sessions
Set currentSessions = getMultimediaSessionApi()
.getStreamingSessions(StreamingSessionUtils.SERVICE_ID);
sessions = new ArrayList<>(currentSessions);
if (sessions.size() > 0) {
String[] items = new String[sessions.size()];
for (int i = 0; i < items.length; i++) {
items[i] = getString(R.string.label_session, sessions.get(i).getSessionId());
}
setListAdapter(new ArrayAdapter<>(StreamingSessionList.this,
android.R.layout.simple_list_item_1, items));
} else {
setListAdapter(null);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/streaming/StreamingSessionUtils.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.streaming;
/**
* Streaming service utils
*
* @author Jean-Marc AUFFRET
*/
public class StreamingSessionUtils {
/**
* Service ID constant
*/
public final static String SERVICE_ID = "ext.streaming";
/**
* Encoding
*/
public final static String ENCODING = "TEXT/1";
}
================================================
FILE: RI/src/com/gsma/rcs/ri/extension/streaming/StreamingSessionView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.extension.streaming;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.extension.MultimediaSession;
import com.gsma.services.rcs.extension.MultimediaSessionService;
import com.gsma.services.rcs.extension.MultimediaStreamingSession;
import com.gsma.services.rcs.extension.MultimediaStreamingSessionIntent;
import com.gsma.services.rcs.extension.MultimediaStreamingSessionListener;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Streaming session view
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class StreamingSessionView extends RcsActivity {
/**
* View mode: incoming session
*/
public final static int MODE_INCOMING = 0;
/**
* View mode: outgoing session
*/
public final static int MODE_OUTGOING = 1;
/**
* View mode: open session history
*/
public final static int MODE_OPEN = 2;
/**
* Intent parameter: view mode
*/
public final static String EXTRA_MODE = "mode";
/**
* Intent parameter: session ID
*/
public final static String EXTRA_SESSION_ID = "session_id";
/**
* Intent parameter: contact
*/
public final static String EXTRA_CONTACT = "contact";
private final Handler handler = new Handler();
private String mSessionId;
private ContactId mContact;
private String mServiceId = StreamingSessionUtils.SERVICE_ID;
private MultimediaStreamingSession mSession;
private Dialog mProgressDialog;
private android.view.View.OnClickListener mBtnStartStopListener;
private OnClickListener mAcceptBtnListener;
private OnClickListener mDeclineBtnListener;
private MultimediaStreamingSessionListener mServiceListener;
private boolean mStarted = false;
private Button mStartStopBtn;
private TextView mTxDataView;
private TextView mRxDataView;
private Integer mCounter = 0;
private ScheduledExecutorService mPeriodicWorker;
private Runnable mPeriodicRtpSendTask;
private MultimediaSessionService mSessionService;
private static final String LOGTAG = LogUtils
.getTag(StreamingSessionView.class.getSimpleName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialize();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.extension_streaming_session_view);
/* Set buttons callback */
mStartStopBtn = (Button) findViewById(R.id.start_stop_btn);
mStartStopBtn.setOnClickListener(mBtnStartStopListener);
mStartStopBtn.setEnabled(false);
mTxDataView = (TextView) findViewById(R.id.tx_data);
mRxDataView = (TextView) findViewById(R.id.rx_data);
mSessionService = getMultimediaSessionApi();
/* Register to API connection manager */
if (mSessionService == null
|| !isServiceConnected(RcsServiceName.MULTIMEDIA, RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.MULTIMEDIA, RcsServiceName.CONTACT);
try {
mSessionService.addEventListener(mServiceListener);
initialiseStreamingSession(getIntent());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mSessionService != null && isServiceConnected(RcsServiceName.MULTIMEDIA)) {
try {
mSessionService.removeEventListener(mServiceListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
if (mPeriodicWorker != null) {
mPeriodicWorker.shutdown();
}
}
private void acceptInvitation() {
try {
mSession.acceptInvitation();
/*
* Wait for the SIP-ACK to allow the user to send message once session is established.
*/
showProgressDialog();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void rejectInvitation() {
try {
mSession.rejectInvitation();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void initialiseStreamingSession(Intent intent) {
try {
int mode = intent.getIntExtra(StreamingSessionView.EXTRA_MODE, -1);
if (mode == StreamingSessionView.MODE_OUTGOING) {
/* Outgoing session: Check if the service is available. */
if (!mSessionService.isServiceRegistered()) {
showMessageThenExit(R.string.error_not_registered);
return;
}
mContact = intent.getParcelableExtra(StreamingSessionView.EXTRA_CONTACT);
startSession();
} else if (mode == StreamingSessionView.MODE_OPEN) {
/* Open an existing session. */
mSessionId = intent.getStringExtra(StreamingSessionView.EXTRA_SESSION_ID);
mSession = mSessionService.getStreamingSession(mSessionId);
if (mSession == null) {
showMessageThenExit(R.string.label_session_has_expired);
return;
}
mContact = mSession.getRemoteContact();
} else {
/* Incoming session from its Intent. */
mSessionId = intent
.getStringExtra(MultimediaStreamingSessionIntent.EXTRA_SESSION_ID);
mSession = mSessionService.getStreamingSession(mSessionId);
if (mSession == null) {
showMessageThenExit(R.string.label_session_has_expired);
return;
}
mContact = mSession.getRemoteContact();
String from = RcsContactUtil.getInstance(this).getDisplayName(mContact);
/* Manual accept */
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_streaming_session);
builder.setMessage(getString(R.string.label_mm_from_id, from, mServiceId));
builder.setCancelable(false);
builder.setIcon(R.drawable.ri_notif_mm_session_icon);
builder.setPositiveButton(R.string.label_accept, mAcceptBtnListener);
builder.setNegativeButton(R.string.label_decline, mDeclineBtnListener);
registerDialog(builder.show());
}
/* Display session info */
TextView featureTagEdit = (TextView) findViewById(R.id.feature_tag);
featureTagEdit.setText(mServiceId);
TextView contactEdit = (TextView) findViewById(R.id.contact);
String from = RcsContactUtil.getInstance(this).getDisplayName(mContact);
contactEdit.setText(from);
Button sendBtn = (Button) findViewById(R.id.send_btn);
if (mSession != null && MultimediaSession.State.STARTED == mSession.getState()) {
sendBtn.setEnabled(true);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void startSession() {
try {
mSession = mSessionService.initiateStreamingSession(mServiceId, mContact,
StreamingSessionUtils.ENCODING);
mSessionId = mSession.getSessionId();
showProgressDialog();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void showProgressDialog() {
mProgressDialog = showProgressDialog(getString(R.string.label_command_in_progress));
mProgressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
Toast.makeText(StreamingSessionView.this,
getString(R.string.label_session_canceled), Toast.LENGTH_SHORT).show();
quitSession();
}
});
}
private void hideProgressDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
mProgressDialog = null;
}
}
private void quitSession() {
try {
if (mSession != null && RcsSessionUtil.isAllowedToAbortMultimediaSession(mSession)) {
mSession.abortSession();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mSession = null;
/* Exit activity */
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
if (mSession != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Exit activity
finish();
}
});
builder.setCancelable(true);
registerDialog(builder.show());
} else {
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_mm_session, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void initialize() {
mPeriodicRtpSendTask = new Runnable() {
public void run() {
if (mSession == null) {
return;
}
mCounter++;
final String data = "data".concat(mCounter.toString());
try {
mSession.sendPayload(data.getBytes());
handler.post(new Runnable() {
public void run() {
/* Display Transmitted data */
mTxDataView.setText(data);
}
});
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mBtnStartStopListener = new android.view.View.OnClickListener() {
public void onClick(View v) {
mStarted = !mStarted;
handler.post(new Runnable() {
public void run() {
mStartStopBtn
.setText(mStarted ? R.string.label_stop : R.string.label_start);
}
});
if (mStarted) {
mPeriodicWorker = Executors.newSingleThreadScheduledExecutor();
mPeriodicWorker.scheduleAtFixedRate(mPeriodicRtpSendTask, 0, 1,
TimeUnit.SECONDS);
} else {
mPeriodicWorker.shutdown();
mPeriodicWorker = null;
}
}
};
mAcceptBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
acceptInvitation();
}
};
mDeclineBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
rejectInvitation();
/* Exit activity */
finish();
}
};
mServiceListener = new MultimediaStreamingSessionListener() {
@Override
public void onStateChanged(ContactId contact, String sessionId,
final MultimediaSession.State state, MultimediaSession.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMultimediaStreamingStateChanged contact=" + contact
+ " sessionId=" + sessionId + " state=" + state + " reason="
+ reasonCode);
}
/* Discard event if not for current sessionId */
if (mSessionId == null || !mSessionId.equals(sessionId)) {
return;
}
final String _reasonCode = RiApplication.sMultimediaReasonCodes[reasonCode.toInt()];
handler.post(new Runnable() {
public void run() {
switch (state) {
case STARTED:
/* Session is established: hide progress dialog. */
hideProgressDialog();
/* Activate sen button. */
mStartStopBtn.setEnabled(true);
break;
case ABORTED:
showMessageThenExit(getString(R.string.label_session_aborted,
_reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_session_rejected,
_reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_session_failed,
_reasonCode));
break;
default:
if (LogUtils.isActive) {
Log.d(LOGTAG,
"onMultimediaStreamingStateChanged "
+ getString(R.string.label_mms_state_changed,
RiApplication.sMultimediaStates[state
.toInt()], _reasonCode));
}
}
}
});
}
@Override
public void onPayloadReceived(ContactId contact, String sessionId, final byte[] content) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onNewMessage contact=" + contact + " sessionId=" + sessionId);
}
if (mSessionId == null || !mSessionId.equals(sessionId)) {
return;
}
handler.post(new Runnable() {
public void run() {
/* Display received data */
mRxDataView.setText(new String(content));
}
});
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/intents/TestIntentApps.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.intents;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* Call each Intents
*
* @author Jean-Marc AUFFRET
*/
public class TestIntentApps extends RcsActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.intents_apps);
// Set button callback
Button btn = (Button) findViewById(R.id.load_chat);
btn.setOnClickListener(btnListener);
btn = (Button) findViewById(R.id.load_ft);
btn.setOnClickListener(btnListener);
btn = (Button) findViewById(R.id.load_group_chat);
btn.setOnClickListener(btnListener);
btn = (Button) findViewById(R.id.initiate_ft);
btn.setOnClickListener(btnListener);
btn = (Button) findViewById(R.id.initiate_group_chat);
btn.setOnClickListener(btnListener);
btn = (Button) findViewById(R.id.initiate_chat);
btn.setOnClickListener(btnListener);
}
/**
* Button callback
*/
private OnClickListener btnListener = new OnClickListener() {
public void onClick(View v) {
try {
if (v.getId() == R.id.load_chat) {
Intent intent = new Intent(
com.gsma.services.rcs.Intents.Chat.ACTION_VIEW_ONE_TO_ONE_CHAT);
startActivity(intent);
} else if (v.getId() == R.id.initiate_chat) {
Intent intent = new Intent(
com.gsma.services.rcs.Intents.Chat.ACTION_SEND_ONE_TO_ONE_CHAT_MESSAGE);
startActivity(intent);
} else if (v.getId() == R.id.load_group_chat) {
Intent intent = new Intent(
com.gsma.services.rcs.Intents.Chat.ACTION_VIEW_GROUP_CHAT);
startActivity(intent);
} else if (v.getId() == R.id.initiate_group_chat) {
Intent intent = new Intent(
com.gsma.services.rcs.Intents.Chat.ACTION_INITIATE_GROUP_CHAT);
startActivity(intent);
} else if (v.getId() == R.id.load_ft) {
Intent intent = new Intent(
com.gsma.services.rcs.Intents.FileTransfer.ACTION_VIEW_FILE_TRANSFER);
startActivity(intent);
} else if (v.getId() == R.id.initiate_ft) {
Intent intent = new Intent(
com.gsma.services.rcs.Intents.FileTransfer.ACTION_INITIATE_ONE_TO_ONE_FILE_TRANSFER);
startActivity(intent);
}
} catch (ActivityNotFoundException e) {
showMessageThenExit(R.string.label_intent_failed);
}
}
};
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/GroupDeliveryInfoList.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.adapter.GroupDeliveryInfoCursorAdapter;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfoLog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.widget.ListView;
/**
* A list view of group chat delivery information where the data comes from a cursor.
*
* @author yplo6403
*/
public class GroupDeliveryInfoList extends FragmentActivity implements
LoaderManager.LoaderCallbacks {
/**
* Intent parameters
*/
private static final String EXTRA_MESSAGE_ID = "message_id";
/**
* the message ID for which we view the delivery information
*/
private String mMessageId;
/**
* The loader's unique ID. Loader IDs are specific to the Activity in which they reside.
*/
private static final int LOADER_ID = 1;
// @formatter:off
private static final String[] PROJECTION = new String[] {
GroupDeliveryInfoLog.BASECOLUMN_ID,
GroupDeliveryInfoLog.CONTACT,
GroupDeliveryInfoLog.TIMESTAMP_DELIVERED,
GroupDeliveryInfoLog.TIMESTAMP_DISPLAYED,
GroupDeliveryInfoLog.STATUS,
GroupDeliveryInfoLog.REASON_CODE
};
// @formatter:on
private static final String WHERE_CLAUSE = GroupDeliveryInfoLog.ID + "=?";
/**
* The log tag for this class
*/
private static final String LOGTAG = LogUtils.getTag(GroupDeliveryInfoList.class
.getSimpleName());
/**
* The adapter that binds data to the ListView
*/
private GroupDeliveryInfoCursorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.delivery_info_list);
mMessageId = getIntent().getStringExtra(EXTRA_MESSAGE_ID);
// Initialize the adapter.
mAdapter = new GroupDeliveryInfoCursorAdapter(this);
// Associate the list adapter with the ListView.
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(mAdapter);
// Initialize the Loader with id and callbacks 'mCallbacks'.
getSupportLoaderManager().initLoader(LOADER_ID, null, this);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate");
}
}
/**
* Start GroupDeliveryInfoList activity
*
* @param context The context
* @param messageId the message ID for which Group Delivery information list is requested
*/
public static void startActivity(Context context, String messageId) {
Intent intent = new Intent(context, GroupDeliveryInfoList.class);
intent.putExtra(EXTRA_MESSAGE_ID, messageId);
context.startActivity(intent);
}
@Override
public Loader onCreateLoader(int id, Bundle arg) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreateLoader " + id);
}
// Create a new CursorLoader with the following query parameters.
return new CursorLoader(this, GroupDeliveryInfoLog.CONTENT_URI, PROJECTION, WHERE_CLAUSE,
new String[] {
mMessageId
}, null);
}
@Override
public void onLoadFinished(Loader loader, Cursor cursor) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onLoadFinished " + loader.getId());
}
// A switch-case is useful when dealing with multiple Loaders/IDs
switch (loader.getId()) {
case LOADER_ID:
// The asynchronous load is complete and the data
// is now available for use. Only now can we associate
// the queried Cursor with the CursorAdapter.
mAdapter.swapCursor(cursor);
break;
}
// The listview now displays the queried data.
}
@Override
public void onLoaderReset(Loader loader) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onLoaderReset " + loader.getId());
}
// For whatever reason, the Loader's data is now unavailable.
// Remove any references to the old data by replacing it with a null
// Cursor.
mAdapter.swapCursor(null);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/GroupTalkView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging;
import com.gsma.rcs.api.connection.ConnectionManager;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsFragmentActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RI;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.messaging.adapter.TalkCursorAdapter;
import com.gsma.rcs.ri.messaging.chat.ChatCursorObserver;
import com.gsma.rcs.ri.messaging.chat.ChatMessageLogView;
import com.gsma.rcs.ri.messaging.chat.ChatPendingIntentManager;
import com.gsma.rcs.ri.messaging.chat.IsComposingManager;
import com.gsma.rcs.ri.messaging.chat.IsComposingManager.INotifyComposing;
import com.gsma.rcs.ri.messaging.chat.group.SendGroupFile;
import com.gsma.rcs.ri.messaging.filetransfer.FileTransferLogView;
import com.gsma.rcs.ri.messaging.geoloc.DisplayGeoloc;
import com.gsma.rcs.ri.messaging.geoloc.EditGeoloc;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Smileys;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsPersistentStorageException;
import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.ChatLog.Message;
import com.gsma.services.rcs.chat.ChatLog.Message.Content;
import com.gsma.services.rcs.chat.ChatMessage;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.chat.ChatServiceConfiguration;
import com.gsma.services.rcs.chat.GroupChat;
import com.gsma.services.rcs.chat.GroupChat.ParticipantStatus;
import com.gsma.services.rcs.chat.GroupChatIntent;
import com.gsma.services.rcs.chat.GroupChatListener;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.contact.ContactUtil;
import com.gsma.services.rcs.contact.RcsContact;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.GroupFileTransferListener;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfo;
import com.gsma.services.rcs.history.HistoryLog;
import com.gsma.services.rcs.history.HistoryUriBuilder;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Group chat view
*/
public class GroupTalkView extends RcsFragmentActivity implements
LoaderManager.LoaderCallbacks {
/**
* The loader's unique ID. Loader IDs are specific to the Activity in which they reside.
*/
private static final int LOADER_ID = 1;
private final static int SELECT_GEOLOCATION = 0;
// @formatter:off
private static final String[] PROJ_CHAT_MSG = new String[]{
HistoryLog.BASECOLUMN_ID,
HistoryLog.ID,
HistoryLog.PROVIDER_ID,
HistoryLog.MIME_TYPE,
HistoryLog.CONTENT,
HistoryLog.TIMESTAMP,
HistoryLog.STATUS,
HistoryLog.DIRECTION,
HistoryLog.CONTACT,
HistoryLog.EXPIRED_DELIVERY,
HistoryLog.FILENAME,
HistoryLog.FILESIZE,
HistoryLog.TRANSFERRED,
HistoryLog.REASON_CODE,
HistoryLog.READ_STATUS};
// @formatter:on
/**
* Query sort order
*/
private final static String ORDER_CHAT_MSG = HistoryLog.TIMESTAMP + " ASC";
/**
* Intent parameters
*/
private final static String EXTRA_PARTICIPANTS = "participants";
private final static String EXTRA_SUBJECT = "subject";
private final static String EXTRA_MODE = "mode";
private enum GroupChatMode {
INCOMING, OUTGOING, OPEN
}
private static final String WHERE_CLAUSE = HistoryLog.CHAT_ID + "=?";
private static final String LOGTAG = LogUtils.getTag(GroupTalkView.class.getSimpleName());
private static final String OPEN_GROUPCHAT = "OPEN_GROUPCHAT";
private static final String INTITIATE_GROUPCHAT = "INTITIATE_GROUPCHAT";
private Handler mHandler;
private EditText mComposeText;
private ChatService mChatService;
private FileTransferService mFileTransferService;
private Uri mUriHistoryProvider;
private IsComposingManager mComposingManager;
private TalkCursorAdapter mAdapter;
private ChatCursorObserver mObserver;
private boolean mChatListnerSet;
private String mSubject;
private String mChatId;
private GroupChat mGroupChat;
private Set mParticipants = new HashSet<>();
private GroupChatListener mChatListener;
private GroupFileTransferListener mFileTransferListener;
private boolean mFileTransferListenerSet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat_view);
if (!isServiceConnected(ConnectionManager.RcsServiceName.CHAT,
ConnectionManager.RcsServiceName.CONTACT,
ConnectionManager.RcsServiceName.CAPABILITY,
ConnectionManager.RcsServiceName.FILE_TRANSFER)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(ConnectionManager.RcsServiceName.CHAT,
ConnectionManager.RcsServiceName.CONTACT,
ConnectionManager.RcsServiceName.CAPABILITY,
ConnectionManager.RcsServiceName.FILE_TRANSFER);
try {
initialize();
processIntent(getIntent());
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate");
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void initialize() throws RcsServiceNotAvailableException, RcsGenericException {
Button sendButton = (Button) findViewById(R.id.send_button);
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendText();
}
});
mHandler = new Handler();
mChatListener = new GroupChatListener() {
@Override
public void onMessageStatusChanged(String chatId, String mimeType, String msgId,
Content.Status status, Content.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.i(LOGTAG, "onMessageStatusChanged chatId=" + chatId + " mime-type="
+ mimeType + " msgId=" + msgId + " status=" + status + " reason="
+ reasonCode);
}
}
// Callback called when an Is-composing event has been received
public void onComposingEvent(String chatId, ContactId contact, boolean status) {
// Discard event if not for current chatId
if (!chatId.equals(mChatId)) {
return;
}
displayComposingEvent(contact, status);
}
@Override
public void onParticipantStatusChanged(String chatId, ContactId contact,
ParticipantStatus status) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onParticipantStatusChanged chatId=" + chatId + " contact="
+ contact + " status=" + status);
}
}
@Override
public void onMessageGroupDeliveryInfoChanged(String chatId, ContactId contact,
String mimeType, String msgId, GroupDeliveryInfo.Status status,
GroupDeliveryInfo.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMessageGroupDeliveryInfoChanged chatId=" + chatId
+ " contact=" + contact + " msgId=" + msgId + " status=" + status
+ " reason=" + reasonCode);
}
}
@Override
public void onStateChanged(String chatId, final GroupChat.State state,
GroupChat.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged chatId=" + chatId + " state=" + state
+ " reason=" + reasonCode);
}
/* Discard event if not for current chatId */
if (mChatId == null || !mChatId.equals(chatId)) {
return;
}
final String _reasonCode = RiApplication.sGroupChatReasonCodes[reasonCode.toInt()];
mHandler.post(new Runnable() {
public void run() {
switch (state) {
case STARTED:
break;
case ABORTED:
showMessageThenExit(getString(R.string.label_chat_aborted,
_reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_chat_rejected,
_reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_chat_failed,
_reasonCode));
break;
default:
}
}
});
}
@Override
public void onDeleted(Set chatIds) {
if (LogUtils.isActive) {
Log.i(LOGTAG, "onDeleted chatIds=".concat(Arrays.toString(chatIds.toArray())));
}
}
@Override
public void onMessagesDeleted(String chatId, Set msgIds) {
if (LogUtils.isActive) {
Log.i(LOGTAG,
"onMessagesDeleted chatId=" + chatId + " msgIds="
+ Arrays.toString(msgIds.toArray()));
}
}
};
mFileTransferListener = new GroupFileTransferListener() {
@Override
public void onStateChanged(String chatId, String transferId, FileTransfer.State state,
FileTransfer.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged chatId=" + chatId + " transferId=" + transferId
+ " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current chatId */
if (mChatId == null || !mChatId.equals(chatId)) {
return;
}
if (FileTransfer.State.TRANSFERRED == state) {
try {
FileTransfer fileTransfer = mFileTransferService
.getFileTransfer(transferId);
if (fileTransfer == null) {
return;
}
if (Utils.isAudioType(fileTransfer.getMimeType())
&& FileTransfer.Disposition.RENDER == fileTransfer.getDisposition()) {
Utils.playAudio(GroupTalkView.this, fileTransfer.getFile());
mFileTransferService.markFileTransferAsRead(transferId);
}
} catch (RcsPersistentStorageException | RcsServiceNotAvailableException
| RcsGenericException e) {
showException(e);
}
}
}
@Override
public void onDeliveryInfoChanged(String chatId, ContactId contact, String transferId,
GroupDeliveryInfo.Status status, GroupDeliveryInfo.ReasonCode reasonCode) {
}
@Override
public void onProgressUpdate(String chatId, String transferId, long currentSize,
long totalSize) {
}
@Override
public void onDeleted(String chatId, Set transferIds) {
}
};
mChatService = getChatApi();
mFileTransferService = getFileTransferApi();
HistoryUriBuilder uriBuilder = new HistoryUriBuilder(HistoryLog.CONTENT_URI);
uriBuilder.appendProvider(ChatLog.Message.HISTORYLOG_MEMBER_ID);
uriBuilder.appendProvider(FileTransferLog.HISTORYLOG_MEMBER_ID);
mUriHistoryProvider = uriBuilder.build();
mComposeText = (EditText) findViewById(R.id.userText);
ChatServiceConfiguration configuration = mChatService.getConfiguration();
// Set max label length
int maxMsgLength = configuration.getGroupChatMessageMaxLength();
if (maxMsgLength > 0) {
InputFilter[] filterArray = new InputFilter[1];
filterArray[0] = new InputFilter.LengthFilter(maxMsgLength);
mComposeText.setFilters(filterArray);
}
mComposingManager = new IsComposingManager(configuration.getIsComposingTimeout(),
getNotifyComposing());
mComposeText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Check if the text is not null.
// we do not wish to consider putting the edit text back to null
// (like when sending message), is having activity
if (!TextUtils.isEmpty(s)) {
// Warn the composing manager that we have some activity
if (mComposingManager != null) {
mComposingManager.hasActivity();
}
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
mAdapter = new TalkCursorAdapter(this, false, mChatService, mFileTransferService);
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(mAdapter);
registerForContextMenu(listView);
}
private boolean processIntent(Intent intent) {
String action = intent.getAction();
if (LogUtils.isActive) {
Log.d(LOGTAG, "processIntent: " + action);
}
String oldChatId = mChatId;
try {
switch ((GroupChatMode) intent.getSerializableExtra(EXTRA_MODE)) {
case OUTGOING:
/* Initiate a Group Chat: Get subject */
mSubject = intent.getStringExtra(GroupTalkView.EXTRA_SUBJECT);
updateGroupChatViewTitle(mSubject);
/* Get the list of participants */
ContactUtil contactUtil = ContactUtil.getInstance(this);
List contacts = intent
.getStringArrayListExtra(GroupTalkView.EXTRA_PARTICIPANTS);
if (contacts == null || contacts.isEmpty()) {
showMessageThenExit(R.string.label_invalid_contacts);
return false;
}
for (String contact : contacts) {
mParticipants.add(contactUtil.formatContact(contact));
}
if (mParticipants.isEmpty()) {
showMessageThenExit(R.string.label_invalid_contacts);
return false;
}
return initiateGroupChat(oldChatId == null);
case OPEN:
/* Open an existing session from the history log */
mChatId = intent.getStringExtra(GroupChatIntent.EXTRA_CHAT_ID);
mGroupChat = mChatService.getGroupChat(mChatId);
if (mGroupChat == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Groupchat not found for Id=".concat(mChatId));
}
showMessageThenExit(R.string.label_session_not_found);
return false;
}
ChatPendingIntentManager.getChatPendingIntentManager(this).clearNotification(
mChatId);
setCursorLoader(oldChatId == null);
RI.sChatIdOnForeground = mChatId;
mSubject = mGroupChat.getSubject();
updateGroupChatViewTitle(mSubject);
/* Set list of participants */
mParticipants = mGroupChat.getParticipants().keySet();
if (LogUtils.isActive) {
Log.i(LOGTAG, "processIntent chatId=" + mChatId + " subject='" + mSubject
+ "'");
}
return true;
case INCOMING:
String rxChatId = intent.getStringExtra(GroupChatIntent.EXTRA_CHAT_ID);
if (GroupChatIntent.ACTION_NEW_GROUP_CHAT_MESSAGE.equals(action)) {
String rxMsgId = intent.getStringExtra(GroupChatIntent.EXTRA_MESSAGE_ID);
mChatService.markMessageAsRead(rxMsgId);
}
mChatId = rxChatId;
mGroupChat = mChatService.getGroupChat(mChatId);
if (mGroupChat == null) {
showMessageThenExit(R.string.label_session_not_found);
return false;
}
setCursorLoader(oldChatId == null);
RI.sChatIdOnForeground = mChatId;
ContactId contact = mGroupChat.getRemoteContact();
mSubject = mGroupChat.getSubject();
updateGroupChatViewTitle(mSubject);
mParticipants = mGroupChat.getParticipants().keySet();
/* Display accept/reject dialog */
if (LogUtils.isActive) {
Log.d(LOGTAG, "New group chat for chatId " + mChatId + " state="
+ mGroupChat.getState());
}
if (GroupChat.State.INVITED == mGroupChat.getState()) {
displayAcceptRejectDialog(contact);
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "New group chat for chatId ".concat(mChatId));
}
return true;
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
return false;
}
private void setCursorLoader(boolean firstLoad) {
if (firstLoad) {
/*
* Initialize the Loader with id '1' and callbacks 'mCallbacks'.
*/
getSupportLoaderManager().initLoader(LOADER_ID, null, this);
} else {
/* We switched from one contact to another: reload history since */
getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}
}
@Override
public void onDestroy() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDestroy");
}
if (mGroupChat != null) {
try {
mGroupChat.setComposingStatus(false);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
RI.sChatIdOnForeground = mChatId;
if (mChatId != null) {
ChatPendingIntentManager.getChatPendingIntentManager(this).clearNotification(mChatId);
}
try {
if (mChatListener != null && mChatService != null && !mChatListnerSet) {
mChatService.addEventListener(mChatListener);
mChatListnerSet = true;
}
if (mFileTransferListener != null && mFileTransferService != null
&& !mFileTransferListenerSet) {
mFileTransferService.addEventListener(mFileTransferListener);
mFileTransferListenerSet = true;
}
} catch (RcsServiceNotAvailableException ignore) {
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
/* Replace the value of intent */
setIntent(intent);
processIntent(intent);
}
@Override
protected void onPause() {
super.onPause();
RI.sChatIdOnForeground = null;
try {
if (mChatListener != null && mChatService != null && mChatListnerSet) {
mChatService.removeEventListener(mChatListener);
mChatListnerSet = false;
}
if (mFileTransferListener != null && mFileTransferService != null
&& mFileTransferListenerSet) {
mFileTransferService.removeEventListener(mFileTransferListener);
mFileTransferListenerSet = false;
}
} catch (RcsServiceNotAvailableException ignore) {
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case SELECT_GEOLOCATION:
Geoloc geoloc = data.getParcelableExtra(EditGeoloc.EXTRA_GEOLOC);
try {
if (mGroupChat != null && geoloc != null) {
mGroupChat.sendMessage(geoloc);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
break;
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_gchat_item, menu);
menu.findItem(R.id.menu_display_content).setVisible(false);
menu.findItem(R.id.menu_listen_content).setVisible(false);
/* Get the list item position. */
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
Cursor cursor = (Cursor) mAdapter.getItem(info.position);
int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID));
Direction direction = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(Message.DIRECTION)));
if (FileTransferLog.HISTORYLOG_MEMBER_ID == providerId) {
String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.MIME_TYPE));
if (Utils.isImageType(mimeType)) {
if (Direction.OUTGOING == direction) {
menu.findItem(R.id.menu_display_content).setVisible(true);
} else {
Long transferred = cursor.getLong(cursor
.getColumnIndexOrThrow(HistoryLog.TRANSFERRED));
Long size = cursor.getLong(cursor.getColumnIndexOrThrow(HistoryLog.FILESIZE));
if (size.equals(transferred)) {
menu.findItem(R.id.menu_display_content).setVisible(true);
}
}
} else if (Utils.isAudioType(mimeType)) {
if (Direction.OUTGOING == direction) {
menu.findItem(R.id.menu_listen_content).setVisible(true);
} else {
Long transferred = cursor.getLong(cursor
.getColumnIndexOrThrow(HistoryLog.TRANSFERRED));
Long size = cursor.getLong(cursor.getColumnIndexOrThrow(HistoryLog.FILESIZE));
if (size.equals(transferred)) {
menu.findItem(R.id.menu_listen_content).setVisible(true);
}
}
}
}
if (Direction.OUTGOING != direction) {
menu.findItem(R.id.menu_view_group_delivery).setVisible(false);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
Cursor cursor = (Cursor) (mAdapter.getItem(info.position));
int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID));
String id = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID));
if (LogUtils.isActive) {
Log.d(LOGTAG, "onContextItemSelected Id=".concat(id));
}
try {
switch (item.getItemId()) {
case R.id.menu_view_group_delivery:
GroupDeliveryInfoList.startActivity(this, id);
return true;
case R.id.menu_delete_message:
if (ChatLog.Message.HISTORYLOG_MEMBER_ID == providerId) {
mChatService.deleteMessage(id);
} else {
mFileTransferService.deleteFileTransfer(id);
}
return true;
case R.id.menu_view_detail:
if (ChatLog.Message.HISTORYLOG_MEMBER_ID == providerId) {
ChatMessageLogView.startActivity(this, id);
} else {
FileTransferLogView.startActivity(this, id);
}
return true;
case R.id.menu_display_content:
if (FileTransferLog.HISTORYLOG_MEMBER_ID == providerId) {
String file = cursor.getString(cursor
.getColumnIndexOrThrow(HistoryLog.CONTENT));
Utils.showPicture(this, Uri.parse(file));
markFileTransferAsRead(cursor, id);
return true;
}
break;
case R.id.menu_listen_content:
if (FileTransferLog.HISTORYLOG_MEMBER_ID == providerId) {
String file = cursor.getString(cursor
.getColumnIndexOrThrow(HistoryLog.CONTENT));
Utils.playAudio(this, Uri.parse(file));
markFileTransferAsRead(cursor, id);
return true;
}
break;
}
return super.onContextItemSelected(item);
} catch (RcsServiceException e) {
showException(e);
}
return true;
}
private void markFileTransferAsRead(Cursor cursor, String ftId) {
try {
RcsService.Direction dir = RcsService.Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.DIRECTION)));
if (RcsService.Direction.INCOMING == dir) {
RcsService.ReadStatus status = RcsService.ReadStatus.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.READ_STATUS)));
if (RcsService.ReadStatus.UNREAD == status) {
mFileTransferService.markFileTransferAsRead(ftId);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Mark file transfer " + ftId + " as read");
}
}
}
} catch (RcsServiceNotAvailableException e) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Cannot mark message as read: service not available");
}
} catch (RcsGenericException | RcsPersistentStorageException e) {
Log.e(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
/**
* Update the view title
*
* @param subject the group chat subject or null
*/
private void updateGroupChatViewTitle(String subject) {
if (!TextUtils.isEmpty(subject)) {
setTitle(getString(R.string.title_group_chat) + " '" + mSubject + "'");
}
}
@Override
public Loader onCreateLoader(int id, Bundle arg) {
/* Create a new CursorLoader with the following query parameters. */
return new CursorLoader(this, mUriHistoryProvider, PROJ_CHAT_MSG, WHERE_CLAUSE,
new String[] {
mChatId
}, ORDER_CHAT_MSG);
}
@Override
public void onLoadFinished(Loader loader, Cursor cursor) {
if (LOADER_ID != loader.getId()) {
return;
}
/*
* The asynchronous load is complete and the data is now available for use. Only now can we
* associate the queried Cursor with the CursorAdapter.
*/
mAdapter.swapCursor(cursor);
/**
* Registering content observer for chat message and file transfer content URIs. When these
* content URIs will change, this will notify the loader to reload its data.
*/
if (mObserver != null && !mObserver.getLoader().equals(loader)) {
ContentResolver resolver = getContentResolver();
resolver.unregisterContentObserver(mObserver);
resolver.unregisterContentObserver(mObserver);
mObserver = null;
}
if (mObserver == null) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onLoadFinished: register content observer");
}
mObserver = new ChatCursorObserver(new Handler(), loader);
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(ChatLog.Message.CONTENT_URI, true, mObserver);
resolver.registerContentObserver(FileTransferLog.CONTENT_URI, true, mObserver);
}
}
@Override
public void onLoaderReset(Loader loader) {
/*
* For whatever reason, the Loader's data is now unavailable. Remove any references to the
* old data by replacing it with a null Cursor.
*/
mAdapter.swapCursor(null);
}
/**
* Display notification to accept or reject invitation
*
* @param remote remote contact
*/
private void displayAcceptRejectDialog(ContactId remote) {
/* Manual accept */
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_group_chat);
String from = RcsContactUtil.getInstance(this).getDisplayName(remote);
String topic = (TextUtils.isEmpty(mSubject)) ? getString(R.string.label_no_subject)
: mSubject;
String msg = getString(R.string.label_gc_from_subject, from, topic);
builder.setMessage(msg);
builder.setCancelable(false);
builder.setIcon(R.drawable.ri_notif_chat_icon);
builder.setPositiveButton(R.string.label_accept,
new android.content.DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try {
/* Accept the invitation */
mGroupChat.openChat();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
});
builder.setNegativeButton(R.string.label_decline,
new android.content.DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
/*
* Let session die by timeout. Exit activity
*/
finish();
}
});
registerDialog(builder.show());
}
/**
* get a set of contact from a set of participant info
*
* @param setOfParticipant a set of participant info
* @return a set of contact
*/
private Set getSetOfParticipants(Map setOfParticipant) {
Set result = new HashSet<>();
if (setOfParticipant.size() != 0) {
for (ContactId contact : setOfParticipant.keySet()) {
// TODO consider status ?
result.add(contact.toString());
}
}
return result;
}
/**
* Initiate the group chat and open a progress dialog waiting for the session to start
*
* @return True if successful
*/
private boolean initiateGroupChat(boolean firstLoad) {
/* Initiate the group chat session in background */
try {
mGroupChat = mChatService.initiateGroupChat(new HashSet<>(mParticipants), mSubject);
mChatId = mGroupChat.getChatId();
setCursorLoader(firstLoad);
RI.sChatIdOnForeground = mChatId;
return true;
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return false;
}
}
/**
* Add participants to be invited in the session
*/
private void addParticipants() {
/* Build list of available contacts not already in the conference */
Set availableParticipants = new HashSet<>();
try {
Set contacts = getContactApi().getRcsContacts();
for (RcsContact rcsContact : contacts) {
ContactId contact = rcsContact.getContactId();
if (mGroupChat.isAllowedToInviteParticipant(contact)) {
availableParticipants.add(contact);
}
}
} catch (RcsServiceException e) {
showException(e);
return;
}
/* Check if some participants are available */
if (availableParticipants.size() == 0) {
showMessage(R.string.label_no_participant_found);
return;
}
/* Display contacts */
final List selectedParticipants = new ArrayList<>();
final CharSequence[] items = new CharSequence[availableParticipants.size()];
int i = 0;
for (ContactId contact : availableParticipants) {
items[i++] = contact.toString();
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_select_contacts);
builder.setCancelable(true);
builder.setMultiChoiceItems(items, null, new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
String c = (String) items[which];
if (isChecked) {
selectedParticipants.add(c);
} else {
selectedParticipants.remove(c);
}
}
});
builder.setNegativeButton(R.string.label_cancel, null);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int position) {
/* Add new participants in the session in background */
try {
int max = mGroupChat.getMaxParticipants() - 1;
int connected = mGroupChat.getParticipants().size();
int limit = max - connected;
if (selectedParticipants.size() > limit) {
showMessage(R.string.label_max_participants);
return;
}
Set contacts = new HashSet<>();
ContactUtil contactUtils = ContactUtil.getInstance(GroupTalkView.this);
for (String participant : selectedParticipants) {
contacts.add(contactUtils.formatContact(participant));
}
/* Add participants */
mGroupChat.inviteParticipants(contacts);
} catch (RcsServiceException e) {
showException(e);
}
}
});
registerDialog(builder.show());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_group_chat, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem menuItemParticipants = menu.findItem(R.id.menu_participants);
MenuItem menuItemSendFile = menu.findItem(R.id.menu_send_file);
MenuItem menuItemLeave = menu.findItem(R.id.menu_close_session);
try {
if (mGroupChat != null) {
menuItemParticipants.setEnabled(mGroupChat.isAllowedToInviteParticipants());
menuItemLeave.setEnabled(mGroupChat.isAllowedToLeave());
FileTransferService fileTransferService = getFileTransferApi();
menuItemSendFile.setEnabled(fileTransferService
.isAllowedToTransferFileToGroupChat(mChatId));
} else {
menuItemParticipants.setEnabled(false);
menuItemSendFile.setEnabled(false);
menuItemLeave.setEnabled(false);
}
} catch (RcsServiceException e) {
showException(e);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
try {
switch (item.getItemId()) {
case R.id.menu_insert_smiley:
AlertDialog alert = Smileys.showSmileyDialog(this, mComposeText,
getResources(), getString(R.string.menu_insert_smiley));
registerDialog(alert);
break;
case R.id.menu_participants:
alert = Utils.showList(this, getString(R.string.menu_participants),
getSetOfParticipants(mGroupChat.getParticipants()));
registerDialog(alert);
break;
case R.id.menu_add_participant:
addParticipants();
break;
case R.id.menu_quicktext:
addQuickText();
break;
case R.id.menu_send_file:
SendGroupFile.startActivity(this, mChatId);
break;
case R.id.menu_send_geoloc:
getGeoLoc();
break;
case R.id.menu_showus_map:
DisplayGeoloc.showContactsOnMap(this, mGroupChat.getParticipants().keySet());
break;
case R.id.menu_close_session:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_chat_exit);
builder.setPositiveButton(R.string.label_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (mGroupChat != null) {
try {
mGroupChat.leave();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
GroupTalkView.this.finish();
}
});
builder.setNegativeButton(R.string.label_cancel, null);
builder.setCancelable(true);
registerDialog(builder.show());
break;
}
} catch (RcsServiceException e) {
showException(e);
}
return true;
}
/**
* Initiate a new Group Chat
*
* @param ctx context
* @param subject subject
* @param participants list of participants
*/
public static void initiateGroupChat(Context ctx, String subject, ArrayList participants) {
Intent intent = new Intent(ctx, GroupTalkView.class);
intent.setAction(INTITIATE_GROUPCHAT);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putStringArrayListExtra(GroupTalkView.EXTRA_PARTICIPANTS, participants);
intent.putExtra(GroupTalkView.EXTRA_MODE, GroupChatMode.OUTGOING);
intent.putExtra(GroupTalkView.EXTRA_SUBJECT, subject);
ctx.startActivity(intent);
}
/**
* Open a Group Chat
*
* @param ctx The context.
* @param chatId The chat ID.
*/
public static void openGroupChat(Context ctx, String chatId) {
Intent intent = new Intent(ctx, GroupTalkView.class);
intent.setAction(OPEN_GROUPCHAT);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(GroupTalkView.EXTRA_MODE, GroupChatMode.OPEN);
intent.putExtra(GroupChatIntent.EXTRA_CHAT_ID, chatId);
ctx.startActivity(intent);
}
/**
* Forge intent to notify Group Chat message
*
* @param ctx The context.
* @param newgroupChatMessage The original intent.
* @param chatId the chat ID
* @return intent
*/
public static Intent forgeIntentNewMessage(Context ctx, Intent newgroupChatMessage,
String chatId) {
newgroupChatMessage.setClass(ctx, GroupTalkView.class);
newgroupChatMessage
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
newgroupChatMessage.putExtra(GroupTalkView.EXTRA_MODE, GroupChatMode.INCOMING);
newgroupChatMessage.putExtra(GroupChatIntent.EXTRA_CHAT_ID, chatId);
return newgroupChatMessage;
}
/**
* Forge intent to notify new Group Chat
*
* @param ctx The context.
* @param invitation The original intent.
* @return intent
*/
public static Intent forgeIntentInvitation(Context ctx, Intent invitation) {
invitation.setClass(ctx, GroupTalkView.class);
invitation.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
invitation.putExtra(GroupTalkView.EXTRA_MODE, GroupChatMode.INCOMING);
return invitation;
}
private ChatMessage sendMessage(String message) throws RcsServiceException {
if (LogUtils.isActive) {
Log.d(LOGTAG, "sendTextMessage: ".concat(message));
}
return mGroupChat.sendMessage(message);
}
private INotifyComposing getNotifyComposing() {
return new INotifyComposing() {
public void setTypingStatus(boolean isTyping) {
try {
if (mGroupChat != null) {
mGroupChat.setComposingStatus(isTyping);
if (LogUtils.isActive) {
Log.d(LOGTAG, "sendIsComposingEvent ".concat(String.valueOf(isTyping)));
}
}
} catch (RcsGenericException e) {
showException(e);
}
}
};
}
private void displayComposingEvent(ContactId contact, final boolean status) {
final String from = RcsContactUtil.getInstance(this).getDisplayName(contact);
mHandler.post(new Runnable() {
public void run() {
TextView view = (TextView) findViewById(R.id.isComposingText);
if (status) {
// Display is-composing notification
view.setText(getString(R.string.label_contact_is_composing, from));
view.setVisibility(View.VISIBLE);
} else {
// Hide is-composing notification
view.setVisibility(View.GONE);
}
}
});
}
private void sendText() {
String text = mComposeText.getText().toString();
if (!TextUtils.isEmpty(text)) {
try {
sendMessage(text);
mComposingManager.messageWasSent();
mComposeText.setText(null);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
private void addQuickText() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_select_quicktext);
builder.setCancelable(true);
builder.setItems(R.array.select_quicktext, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
String[] items = getResources().getStringArray(R.array.select_quicktext);
mComposeText.append(items[which]);
}
});
registerDialog(builder.show());
}
private void getGeoLoc() {
// Start a new activity to send a geolocation
startActivityForResult(new Intent(this, EditGeoloc.class), SELECT_GEOLOCATION);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/OneToOneTalkView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging;
import com.gsma.rcs.api.connection.ConnectionManager;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsFragmentActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RI;
import com.gsma.rcs.ri.messaging.adapter.TalkCursorAdapter;
import com.gsma.rcs.ri.messaging.chat.ChatCursorObserver;
import com.gsma.rcs.ri.messaging.chat.ChatMessageLogView;
import com.gsma.rcs.ri.messaging.chat.ChatPendingIntentManager;
import com.gsma.rcs.ri.messaging.chat.IsComposingManager;
import com.gsma.rcs.ri.messaging.chat.single.SendSingleFile;
import com.gsma.rcs.ri.messaging.chat.single.SingleChatIntentService;
import com.gsma.rcs.ri.messaging.filetransfer.FileTransferIntentService;
import com.gsma.rcs.ri.messaging.filetransfer.FileTransferLogView;
import com.gsma.rcs.ri.messaging.geoloc.EditGeoloc;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsPermissionDeniedException;
import com.gsma.services.rcs.RcsPersistentStorageException;
import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.RcsServiceNotRegisteredException;
import com.gsma.services.rcs.capability.CapabilityService;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.chat.ChatServiceConfiguration;
import com.gsma.services.rcs.chat.OneToOneChat;
import com.gsma.services.rcs.chat.OneToOneChatIntent;
import com.gsma.services.rcs.chat.OneToOneChatListener;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferIntent;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener;
import com.gsma.services.rcs.history.HistoryLog;
import com.gsma.services.rcs.history.HistoryUriBuilder;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* One to one talk view : aggregates the RCS IM messages.
*
* @author Philippe LEMORDANT
*/
public class OneToOneTalkView extends RcsFragmentActivity implements
LoaderManager.LoaderCallbacks {
/**
* The loader's unique ID. Loader IDs are specific to the Activity in which they reside.
*/
private static final int LOADER_ID = 1;
// @formatter:off
private static final String[] PROJECTION = new String[]{
HistoryLog.BASECOLUMN_ID,
HistoryLog.ID,
HistoryLog.PROVIDER_ID,
HistoryLog.MIME_TYPE,
HistoryLog.CONTENT,
HistoryLog.TIMESTAMP,
HistoryLog.STATUS,
HistoryLog.DIRECTION,
HistoryLog.CONTACT,
HistoryLog.EXPIRED_DELIVERY,
HistoryLog.FILENAME,
HistoryLog.FILESIZE,
HistoryLog.TRANSFERRED,
HistoryLog.REASON_CODE,
HistoryLog.READ_STATUS};
// @formatter:on
private final static String EXTRA_CONTACT = "contact";
private static final String LOGTAG = LogUtils.getTag(OneToOneTalkView.class.getSimpleName());
/**
* Chat_id is set to contact id for one to one chat and file transfer messages.
*/
private static final String WHERE_CLAUSE = HistoryLog.CHAT_ID + "=?";
private final static String ORDER_ASC = HistoryLog.TIMESTAMP + " ASC";
private static final String OPEN_TALK = "open_talk";
private final static int SELECT_GEOLOCATION = 0;
/**
* The adapter that binds data to the ListView
*/
private TalkCursorAdapter mAdapter;
private Uri mUriHistoryProvider;
private ContactId mContact;
private ChatCursorObserver mObserver;
private EditText mComposeText;
private ChatService mChatService;
private OneToOneChat mChat;
private FileTransferService mFileTransferService;
private OneToOneChatListener mChatListener;
private OneToOneFileTransferListener mFileTransferListener;
private Handler mHandler;
private AlertDialog mClearUndeliveredAlertDialog;
private DialogInterface.OnCancelListener mUndeliveredCancelListener;
/**
* Utility class to manage the is-composing status
*/
private IsComposingManager mComposingManager;
private CapabilityService mCapabilityService;
private Context mCtx;
// @formatter:off
private static final Set sAllowedIntentActions = new HashSet<>(Arrays.asList(
OneToOneChatIntent.ACTION_MESSAGE_DELIVERY_EXPIRED,
OneToOneChatIntent.ACTION_NEW_ONE_TO_ONE_CHAT_MESSAGE,
FileTransferIntent.ACTION_FILE_TRANSFER_DELIVERY_EXPIRED,
OPEN_TALK));
// @formatter:on
private DialogInterface.OnClickListener mClearUndeliveredChat;
private DialogInterface.OnClickListener mClearUndeliveredFt;
private boolean mChatListenerSet;
private boolean mFileTransferListenerSet;
/**
* Forge intent to start XmsView activity
*
* @param context The context
* @param contact The contact ID
* @return intent
*/
public static Intent forgeIntentToOpenConversation(Context context, ContactId contact) {
Intent intent = new Intent(context, OneToOneTalkView.class);
intent.setAction(OPEN_TALK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_CONTACT, (Parcelable) contact);
return intent;
}
/**
* Forge intent to start OneToOneTalkView activity upon reception of a stack event
*
* @param ctx The context
* @param contact The contact ID
* @param intent intent
* @return intent
*/
public static Intent forgeIntentOnStackEvent(Context ctx, ContactId contact, Intent intent) {
intent.setClass(ctx, OneToOneTalkView.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_CONTACT, (Parcelable) contact);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat_view);
if (!isServiceConnected(ConnectionManager.RcsServiceName.CONTACT,
ConnectionManager.RcsServiceName.CHAT,
ConnectionManager.RcsServiceName.FILE_TRANSFER,
ConnectionManager.RcsServiceName.CAPABILITY)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(ConnectionManager.RcsServiceName.CONTACT,
ConnectionManager.RcsServiceName.CHAT,
ConnectionManager.RcsServiceName.FILE_TRANSFER,
ConnectionManager.RcsServiceName.CAPABILITY);
try {
initialize();
processIntent(getIntent());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void sendText() {
final String text = mComposeText.getText().toString();
if (TextUtils.isEmpty(text)) {
return;
}
try {
if (mChat != null) {
mChat.sendMessage(text);
}
mComposeText.setText(null);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void initialize() throws RcsGenericException, RcsServiceNotAvailableException {
mCtx = this;
/* Set send button listener */
Button sendBtn = (Button) findViewById(R.id.send_button);
sendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendText();
}
});
mHandler = new Handler();
mClearUndeliveredChat = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Set msgIds = SingleChatIntentService.getUndelivered(mCtx, mContact);
if (!msgIds.isEmpty()) {
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Clear delivery expiration for IDs=" + msgIds);
}
mChatService.clearMessageDeliveryExpiration(msgIds);
} catch (RcsServiceException e) {
showException(e);
} finally {
mClearUndeliveredAlertDialog = null;
}
}
}
};
mUndeliveredCancelListener = new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mClearUndeliveredAlertDialog = null;
}
};
mClearUndeliveredFt = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
Set transferIds = FileTransferIntentService.getUndelivered(mCtx,
mContact);
if (!transferIds.isEmpty()) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Clear delivery expiration for IDs=" + transferIds);
}
mFileTransferService.clearFileTransferDeliveryExpiration(transferIds);
mClearUndeliveredAlertDialog = null;
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mClearUndeliveredAlertDialog = null;
}
}
};
mChatListener = new OneToOneChatListener() {
/* Callback called when an Is-composing event has been received */
@Override
public void onComposingEvent(ContactId contact, boolean status) {
/* Discard event if not for current contact */
if (!contact.equals(mContact)) {
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onComposingEvent contact=" + contact + " status=" + status);
}
displayComposingEvent(contact, status);
}
@Override
public void onMessageStatusChanged(ContactId contact, String mimeType, String msgId,
ChatLog.Message.Content.Status status,
ChatLog.Message.Content.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMessageStatusChanged contact=" + contact + " mime-type="
+ mimeType + " msgId=" + msgId + " status=" + status);
}
}
@Override
public void onMessagesDeleted(ContactId contact, Set msgIds) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMessagesDeleted contact=" + contact + " for IDs=" + msgIds);
}
}
};
mFileTransferListener = new OneToOneFileTransferListener() {
@Override
public void onStateChanged(ContactId contact, String transferId,
FileTransfer.State state, FileTransfer.ReasonCode reasonCode) {
if (!contact.equals(mContact)) {
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " transferId=" + transferId
+ " state=" + state + " reason=" + reasonCode);
}
if (FileTransfer.State.TRANSFERRED == state) {
try {
FileTransfer fileTransfer = mFileTransferService
.getFileTransfer(transferId);
if (fileTransfer == null) {
return;
}
if (Utils.isAudioType(fileTransfer.getMimeType())
&& FileTransfer.Disposition.RENDER == fileTransfer.getDisposition()) {
Utils.playAudio(OneToOneTalkView.this, fileTransfer.getFile());
mFileTransferService.markFileTransferAsRead(transferId);
}
} catch (RcsPersistentStorageException | RcsServiceNotAvailableException
| RcsGenericException e) {
showException(e);
}
}
}
@Override
public void onProgressUpdate(ContactId contact, String transferId, long currentSize,
long totalSize) {
}
@Override
public void onDeleted(ContactId contact, Set transferIds) {
}
};
mChatService = getChatApi();
mCapabilityService = getCapabilityApi();
mFileTransferService = getFileTransferApi();
HistoryUriBuilder uriBuilder = new HistoryUriBuilder(HistoryLog.CONTENT_URI);
uriBuilder.appendProvider(ChatLog.Message.HISTORYLOG_MEMBER_ID);
uriBuilder.appendProvider(FileTransferLog.HISTORYLOG_MEMBER_ID);
mUriHistoryProvider = uriBuilder.build();
mComposeText = (EditText) findViewById(R.id.userText);
ChatServiceConfiguration configuration = mChatService.getConfiguration();
// Set max label length
int maxMsgLength = configuration.getOneToOneChatMessageMaxLength();
if (maxMsgLength > 0) {
/* Set the message composer max length */
InputFilter[] filterArray = new InputFilter[1];
filterArray[0] = new InputFilter.LengthFilter(maxMsgLength);
mComposeText.setFilters(filterArray);
}
IsComposingManager.INotifyComposing iNotifyComposing = new IsComposingManager.INotifyComposing() {
public void setTypingStatus(boolean isTyping) {
try {
if (mChat == null) {
return;
}
mChat.setComposingStatus(isTyping);
if (LogUtils.isActive) {
Boolean _isTyping = isTyping;
Log.d(LOGTAG, "sendIsComposingEvent ".concat(_isTyping.toString()));
}
} catch (RcsGenericException e) {
showException(e);
}
}
};
/* Instantiate the composing manager */
mComposingManager = new IsComposingManager(configuration.getIsComposingTimeout(),
iNotifyComposing);
mComposeText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Check if the text is not null.
// we do not wish to consider putting the edit text back to null
// (like when sending message), is having activity
if (!TextUtils.isEmpty(s)) {
// Warn the composing manager that we have some activity
if (mComposingManager != null) {
mComposingManager.hasActivity();
}
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
/* Initialize the adapter. */
mAdapter = new TalkCursorAdapter(this, true, mChatService, mFileTransferService);
/* Associate the list adapter with the ListView. */
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setDivider(null);
listView.setAdapter(mAdapter);
registerForContextMenu(listView);
}
private boolean processIntent(Intent intent) {
String action = intent.getAction();
if (LogUtils.isActive) {
Log.d(LOGTAG, "processIntent " + action);
}
ContactId newContact = intent.getParcelableExtra(EXTRA_CONTACT);
if (newContact == null) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "Cannot process intent: contact is null");
}
return false;
}
if (action == null) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "Cannot process intent: action is null");
}
return false;
}
if (!sAllowedIntentActions.contains(action)) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "Cannot process intent: unauthorized action " + action);
}
return false;
}
try {
if (!newContact.equals(mContact)) {
/* Either it is the first conversation loading or switch to another conversation */
loadConversation(newContact);
}
/* Set activity title with display name */
String displayName = RcsContactUtil.getInstance(this).getDisplayName(mContact);
setTitle(getString(R.string.title_chat, displayName));
switch (action) {
case OneToOneChatIntent.ACTION_NEW_ONE_TO_ONE_CHAT_MESSAGE:
/*
* Open chat to accept session if the parameter IM SESSION START is 0. Client
* application is not aware of the one to one chat session state nor of the IM
* session start mode so we call the method systematically.
*/
mChat.openChat();
break;
case OneToOneChatIntent.ACTION_MESSAGE_DELIVERY_EXPIRED:
processUndeliveredMessages(displayName);
break;
case FileTransferIntent.ACTION_FILE_TRANSFER_DELIVERY_EXPIRED:
processUndeliveredFileTransfers(displayName);
break;
}
return true;
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return false;
}
}
private void clearNotification() {
ChatPendingIntentManager pendingIntentManager = ChatPendingIntentManager
.getChatPendingIntentManager(this);
pendingIntentManager.clearNotification(mContact.toString());
}
private void loadConversation(ContactId newContact) throws RcsServiceNotAvailableException,
RcsGenericException, RcsPersistentStorageException {
boolean firstLoad = (mContact == null);
/* Save contact ID */
mContact = newContact;
clearNotification();
/*
* Open chat so that if the parameter IM SESSION START is 0 then the session is accepted
* now.
*/
mChat = mChatService.getOneToOneChat(mContact);
setCursorLoader(firstLoad);
RI.sChatIdOnForeground = mContact.toString();
/* Request for capabilities ony if they are not available or expired */
requestCapabilities(mContact);
}
private void setCursorLoader(boolean firstLoad) {
if (firstLoad) {
/*
* Initialize the Loader with id '1' and callbacks 'mCallbacks'.
*/
getSupportLoaderManager().initLoader(LOADER_ID, null, this);
} else {
/* We switched from one contact to another: reload history since */
getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}
}
@Override
protected void onPause() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "--> onPause");
}
super.onPause();
RI.sChatIdOnForeground = null;
try {
if (mChatListener != null && mChatService != null && mChatListenerSet) {
mChatService.removeEventListener(mChatListener);
mChatListenerSet = false;
}
if (mFileTransferListener != null && mFileTransferService != null
&& mFileTransferListenerSet) {
mFileTransferService.removeEventListener(mFileTransferListener);
mFileTransferListenerSet = false;
}
} catch (RcsServiceNotAvailableException ignore) {
} catch (RcsGenericException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
/* Replace the value of intent */
setIntent(intent);
processIntent(intent);
}
@Override
protected void onResume() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "--> onResume");
}
super.onResume();
if (mContact != null) {
RI.sChatIdOnForeground = mContact.toString();
clearNotification();
}
try {
if (mChatListener != null && mChatService != null && !mChatListenerSet) {
mChatService.addEventListener(mChatListener);
mChatListenerSet = true;
}
if (mFileTransferListener != null && mFileTransferService != null
&& !mFileTransferListenerSet) {
mFileTransferService.addEventListener(mFileTransferListener);
mFileTransferListenerSet = true;
}
} catch (RcsServiceNotAvailableException ignore) {
} catch (RcsGenericException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_1to1_talk, menu);
return true;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case SELECT_GEOLOCATION:
Geoloc geoloc = data.getParcelableExtra(EditGeoloc.EXTRA_GEOLOC);
try {
if (mChat != null) {
mChat.sendMessage(geoloc);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
break;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
try {
switch (item.getItemId()) {
case R.id.menu_send_geoloc:
/* Start a new activity to select a geolocation */
startActivityForResult(new Intent(this, EditGeoloc.class), SELECT_GEOLOCATION);
break;
case R.id.menu_send_rcs_file:
SendSingleFile.startActivity(this, mContact);
break;
case R.id.menu_delete_talk:
mFileTransferService.deleteOneToOneFileTransfers(mContact);
mChatService.deleteOneToOneChat(mContact);
break;
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_1to1_talk_item, menu);
menu.findItem(R.id.menu_resend_message).setVisible(false);
menu.findItem(R.id.menu_display_content).setVisible(false);
menu.findItem(R.id.menu_listen_content).setVisible(false);
/* Get the list item position */
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
Cursor cursor = (Cursor) mAdapter.getItem(info.position);
/* Adapt the contextual menu according to the selected item */
int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID));
String id = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID));
Direction direction = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.DIRECTION)));
try {
switch (providerId) {
case ChatLog.Message.HISTORYLOG_MEMBER_ID:
if (Direction.OUTGOING == direction) {
ChatLog.Message.Content.Status status = ChatLog.Message.Content.Status
.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.STATUS)));
if (ChatLog.Message.Content.Status.FAILED == status) {
String number = cursor.getString(cursor
.getColumnIndexOrThrow(HistoryLog.CONTACT));
if (number != null) {
ContactId contact = ContactUtil.formatContact(number);
OneToOneChat chat = mChatService.getOneToOneChat(contact);
if (chat != null && chat.isAllowedToSendMessage()) {
menu.findItem(R.id.menu_resend_message).setVisible(true);
}
}
}
}
break;
case FileTransferLog.HISTORYLOG_MEMBER_ID:
String mimeType = cursor.getString(cursor
.getColumnIndexOrThrow(HistoryLog.MIME_TYPE));
FileTransfer.State state = FileTransfer.State.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.STATUS)));
if (FileTransfer.State.FAILED == state) {
FileTransfer transfer = mFileTransferService.getFileTransfer(id);
if (transfer != null && transfer.isAllowedToResendTransfer()) {
menu.findItem(R.id.menu_resend_message).setVisible(true);
}
} else if (Utils.isImageType(mimeType)) {
if (Direction.OUTGOING == direction) {
menu.findItem(R.id.menu_display_content).setVisible(true);
} else if (Direction.INCOMING == direction) {
Long transferred = cursor.getLong(cursor
.getColumnIndexOrThrow(HistoryLog.TRANSFERRED));
Long size = cursor.getLong(cursor
.getColumnIndexOrThrow(HistoryLog.FILESIZE));
if (size.equals(transferred)) {
menu.findItem(R.id.menu_display_content).setVisible(true);
}
}
} else if (Utils.isAudioType(mimeType)) {
if (Direction.OUTGOING == direction) {
menu.findItem(R.id.menu_listen_content).setVisible(true);
} else if (Direction.INCOMING == direction) {
Long transferred = cursor.getLong(cursor
.getColumnIndexOrThrow(HistoryLog.TRANSFERRED));
Long size = cursor.getLong(cursor
.getColumnIndexOrThrow(HistoryLog.FILESIZE));
if (size.equals(transferred)) {
menu.findItem(R.id.menu_listen_content).setVisible(true);
}
}
}
break;
default:
throw new IllegalArgumentException("Invalid provider ID=" + providerId);
}
} catch (RcsServiceNotAvailableException e) {
menu.findItem(R.id.menu_resend_message).setVisible(false);
} catch (RcsGenericException | RcsPersistentStorageException e) {
menu.findItem(R.id.menu_resend_message).setVisible(false);
showException(e);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
Cursor cursor = (Cursor) (mAdapter.getItem(info.position));
int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID));
String id = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID));
if (LogUtils.isActive) {
Log.d(LOGTAG, "onContextItemSelected Id=".concat(id));
}
try {
switch (item.getItemId()) {
case R.id.menu_delete_message:
switch (providerId) {
case ChatLog.Message.HISTORYLOG_MEMBER_ID:
mChatService.deleteMessage(id);
return true;
case FileTransferLog.HISTORYLOG_MEMBER_ID:
mFileTransferService.deleteFileTransfer(id);
return true;
}
break;
case R.id.menu_resend_message:
switch (providerId) {
case ChatLog.Message.HISTORYLOG_MEMBER_ID:
OneToOneChat chat = mChatService.getOneToOneChat(mContact);
if (chat != null) {
chat.resendMessage(id);
}
return true;
case FileTransferLog.HISTORYLOG_MEMBER_ID:
FileTransfer fileTransfer = mFileTransferService.getFileTransfer(id);
if (fileTransfer != null) {
fileTransfer.resendTransfer();
}
return true;
}
break;
case R.id.menu_display_content:
switch (providerId) {
case FileTransferLog.HISTORYLOG_MEMBER_ID:
String file = cursor.getString(cursor
.getColumnIndexOrThrow(HistoryLog.CONTENT));
Utils.showPicture(this, Uri.parse(file));
markFileTransferAsRead(cursor, id);
return true;
}
break;
case R.id.menu_view_detail:
switch (providerId) {
case ChatLog.Message.HISTORYLOG_MEMBER_ID:
ChatMessageLogView.startActivity(this, id);
return true;
case FileTransferLog.HISTORYLOG_MEMBER_ID:
FileTransferLogView.startActivity(this, id);
return true;
}
break;
case R.id.menu_listen_content:
if (FileTransferLog.HISTORYLOG_MEMBER_ID == providerId) {
String file = cursor.getString(cursor
.getColumnIndexOrThrow(HistoryLog.CONTENT));
Utils.playAudio(this, Uri.parse(file));
markFileTransferAsRead(cursor, id);
return true;
}
break;
}
return super.onContextItemSelected(item);
} catch (RcsGenericException | RcsPermissionDeniedException | RcsPersistentStorageException e) {
showException(e);
return true;
} catch (RcsServiceNotAvailableException e) {
Utils.displayLongToast(this, getString(R.string.label_service_not_available));
return true;
}
}
private void markFileTransferAsRead(Cursor cursor, String ftId) {
try {
Direction dir = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.DIRECTION)));
if (Direction.INCOMING == dir) {
RcsService.ReadStatus status = RcsService.ReadStatus.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.READ_STATUS)));
if (RcsService.ReadStatus.UNREAD == status) {
mFileTransferService.markFileTransferAsRead(ftId);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Mark file transfer " + ftId + " as read");
}
}
}
} catch (RcsServiceNotAvailableException e) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Cannot mark message as read: service not available");
}
} catch (RcsGenericException | RcsPersistentStorageException e) {
Log.e(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
/* Create a new CursorLoader with the following query parameters. */
return new CursorLoader(this, mUriHistoryProvider, PROJECTION, WHERE_CLAUSE, new String[] {
mContact.toString()
}, ORDER_ASC);
}
@Override
public void onLoadFinished(Loader loader, Cursor data) {
if (LOADER_ID == loader.getId()) {
/*
* The asynchronous load is complete and the data is now available for use. Only now can
* we associate the queried Cursor with the CursorAdapter.
*/
mAdapter.swapCursor(data);
/**
* Registering content observer for XMS message content URI. When this content URI will
* change, this will notify the loader to reload its data.
*/
if (mObserver != null && !mObserver.getLoader().equals(loader)) {
ContentResolver resolver = getContentResolver();
resolver.unregisterContentObserver(mObserver);
mObserver = null;
}
if (mObserver == null) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onLoadFinished: register content observer");
}
mObserver = new ChatCursorObserver(new Handler(), loader);
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(ChatLog.Message.CONTENT_URI, true, mObserver);
resolver.registerContentObserver(FileTransferLog.CONTENT_URI, true, mObserver);
}
}
}
@Override
public void onLoaderReset(Loader loader) {
/*
* For whatever reason, the Loader's data is now unavailable. Remove any references to the
* old data by replacing it with a null Cursor.
*/
mAdapter.swapCursor(null);
}
private void displayComposingEvent(final ContactId contact, final boolean status) {
final String from = RcsContactUtil.getInstance(this).getDisplayName(contact);
// Execute on UI handler since callback is executed from service
mHandler.post(new Runnable() {
public void run() {
TextView view = (TextView) findViewById(R.id.isComposingText);
if (status) {
// Display is-composing notification
view.setText(getString(R.string.label_contact_is_composing, from));
view.setVisibility(View.VISIBLE);
} else {
// Hide is-composing notification
view.setVisibility(View.GONE);
}
}
});
}
private void processUndeliveredFileTransfers(String displayName) throws RcsGenericException,
RcsServiceNotAvailableException, RcsPersistentStorageException,
RcsPermissionDeniedException {
if (LogUtils.isActive) {
Log.d(LOGTAG, "processUndeliveredFileTransfers: ask");
}
/* Do not propose to clear undelivered if a dialog is already opened */
if (mClearUndeliveredAlertDialog == null) {
mClearUndeliveredAlertDialog = popUpDeliveryExpiration(this,
getString(R.string.title_undelivered_filetransfer),
getString(R.string.label_undelivered_filetransfer, displayName),
mClearUndeliveredFt, null, mUndeliveredCancelListener);
registerDialog(mClearUndeliveredAlertDialog);
}
}
private void processUndeliveredMessages(String displayName) throws RcsGenericException,
RcsPersistentStorageException, RcsServiceNotAvailableException {
if (LogUtils.isActive) {
Log.d(LOGTAG, "processUndeliveredMessages: ask");
}
/* Do not propose to clear undelivered if a dialog is already opened */
if (mClearUndeliveredAlertDialog == null) {
mClearUndeliveredAlertDialog = popUpDeliveryExpiration(this,
getString(R.string.title_undelivered_message),
getString(R.string.label_undelivered_message, displayName),
mClearUndeliveredChat, null, mUndeliveredCancelListener);
registerDialog(mClearUndeliveredAlertDialog);
}
}
private AlertDialog popUpDeliveryExpiration(Context ctx, String title, String msg,
DialogInterface.OnClickListener onPositiveClickListener,
DialogInterface.OnClickListener onNegativeClickListener,
DialogInterface.OnCancelListener onCancelListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setMessage(msg);
builder.setTitle(title);
if (onNegativeClickListener != null) {
builder.setNegativeButton(R.string.label_cancel, onNegativeClickListener);
}
builder.setPositiveButton(R.string.label_ok, onPositiveClickListener);
builder.setOnCancelListener(onCancelListener);
return builder.show();
}
private void requestCapabilities(ContactId contact) throws RcsServiceNotAvailableException,
RcsGenericException {
try {
mCapabilityService.requestContactCapabilities(new HashSet<>(Collections
.singletonList(contact)));
} catch (RcsServiceNotRegisteredException e) {
Log.w(LOGTAG, "Cannot request capabilities: RCS not registered!");
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/TalkList.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.adapter.TalkListArrayAdapter;
import com.gsma.rcs.ri.messaging.adapter.TalkListArrayItem;
import com.gsma.rcs.ri.messaging.chat.ChatPendingIntentManager;
import com.gsma.rcs.ri.messaging.filetransfer.multi.SendMultiFile;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.chat.GroupChat;
import com.gsma.services.rcs.chat.GroupChatIntent;
import com.gsma.services.rcs.chat.GroupChatListener;
import com.gsma.services.rcs.chat.OneToOneChatIntent;
import com.gsma.services.rcs.chat.OneToOneChatListener;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferIntent;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.GroupFileTransferListener;
import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* List of conversations from the content provider: RCS chat + RCS file transfer
*
* @author Philippe LEMORDANT
*/
public class TalkList extends RcsActivity {
private TalkListArrayAdapter mAdapter;
private List mMessageLogs;
private static final String LOGTAG = LogUtils.getTag(TalkList.class.getSimpleName());
private ChatService mChatService;
private FileTransferService mFileTransferService;
private boolean mOneToOneChatListenerSet;
private OneToOneChatListener mOneToOneChatListener;
private Context mCtx;
private boolean mFileTransferListenerSet;
private OneToOneFileTransferListener mOneToOneFileTransferListener;
private TalkListUpdate.TaskCompleted mUpdateTalkListListener;
private static boolean sActivityVisible;
private boolean mGroupChatListenerSet;
private GroupChatListener mGroupChatListener;
private GroupFileTransferListener mGroupFileTransferListener;
private boolean mGroupFileTransferListenerSet;
private boolean mTalkListOpenedToSendFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.chat_list);
initialize();
/*
* If action to launch activity is not null then activity is opened to transfer a file
*/
mTalkListOpenedToSendFile = getIntent().getAction() != null;
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String action = intent.getAction();
if (action == null) {
return;
}
switch (action) {
case GroupChatIntent.ACTION_NEW_INVITATION:
case GroupChatIntent.ACTION_NEW_GROUP_CHAT_MESSAGE:
case OneToOneChatIntent.ACTION_NEW_ONE_TO_ONE_CHAT_MESSAGE:
case FileTransferIntent.ACTION_NEW_INVITATION:
/* Replace the value of intent */
setIntent(intent);
TalkListUpdate updateTalkList = new TalkListUpdate(this, mUpdateTalkListListener);
updateTalkList.execute();
break;
default:
throw new IllegalArgumentException("Invalid action=" + action);
}
}
@Override
protected void onResume() {
super.onResume();
sActivityVisible = true;
addServiceListeners();
updateView();
}
@Override
protected void onPause() {
super.onPause();
sActivityVisible = false;
removeServiceListeners();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_log, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
try {
switch (item.getItemId()) {
case R.id.menu_clear_log:
/* Delete all messages */
if (!isServiceConnected(RcsServiceName.CHAT, RcsServiceName.FILE_TRANSFER)) {
showMessage(R.string.label_service_not_available);
break;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "delete conversations");
}
mChatService.deleteOneToOneChats();
mChatService.deleteGroupChats();
mFileTransferService.deleteOneToOneFileTransfers();
mFileTransferService.deleteGroupFileTransfers();
break;
}
} catch (RcsServiceNotAvailableException e) {
showMessage(R.string.label_service_not_available);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_log_item, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
/* Get selected item */
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
TalkListArrayItem message = mAdapter.getItem(info.position);
String chatId = message.getChatId();
ContactId contact = message.getContact();
if (LogUtils.isActive) {
Log.d(LOGTAG, "onContextItemSelected chatId=".concat(chatId));
}
try {
switch (item.getItemId()) {
case R.id.menu_delete_message:
if (!isServiceConnected(RcsServiceName.CHAT, RcsServiceName.FILE_TRANSFER)) {
showMessage(R.string.error_conversation_delete);
return true;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Delete conversation for chatId=".concat(chatId));
}
if (message.isGroupChat()) {
mChatService.deleteGroupChat(chatId);
mFileTransferService.deleteGroupFileTransfers(chatId);
} else {
mChatService.deleteOneToOneChat(contact);
mFileTransferService.deleteOneToOneFileTransfers(contact);
}
return true;
default:
return super.onContextItemSelected(item);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return true;
}
}
private void updateView() {
if (sActivityVisible) {
TalkListUpdate updateTalkList = new TalkListUpdate(mCtx, mUpdateTalkListListener);
updateTalkList.execute();
}
}
private void initialize() {
mCtx = this;
mMessageLogs = new ArrayList<>();
mChatService = getChatApi();
mFileTransferService = getFileTransferApi();
ListView listView = (ListView) findViewById(android.R.id.list);
TextView emptyView = (TextView) findViewById(android.R.id.empty);
listView.setEmptyView(emptyView);
registerForContextMenu(listView);
mAdapter = new TalkListArrayAdapter(this, mMessageLogs);
listView.setAdapter(mAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
TalkListArrayItem message = mAdapter.getItem(position);
boolean gchat = message.isGroupChat();
if (mTalkListOpenedToSendFile) {
// Open multiple file transfer
SendMultiFile.startActivity(TalkList.this, getIntent(), !gchat,
message.getChatId());
return;
}
if (gchat) {
GroupTalkView.openGroupChat(mCtx, message.getChatId());
} else {
startActivity(OneToOneTalkView.forgeIntentToOpenConversation(mCtx,
message.getContact()));
}
}
});
mOneToOneFileTransferListener = new OneToOneFileTransferListener() {
@Override
public void onStateChanged(ContactId contact, String transferId,
FileTransfer.State state, FileTransfer.ReasonCode reasonCode) {
}
@Override
public void onProgressUpdate(ContactId contact, String transferId, long currentSize,
long totalSize) {
}
@Override
public void onDeleted(final ContactId contact, Set transferIds) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDeleted contact=" + contact + " FT IDs=" + transferIds);
}
updateView();
}
};
mOneToOneChatListener = new OneToOneChatListener() {
@Override
public void onMessageStatusChanged(ContactId contact, String mimeType, String msgId,
ChatLog.Message.Content.Status status,
ChatLog.Message.Content.ReasonCode reasonCode) {
}
@Override
public void onComposingEvent(ContactId contact, boolean status) {
}
@Override
public void onMessagesDeleted(final ContactId contact, Set msgIds) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMessagesDeleted contact=" + contact + " msg IDs=" + msgIds);
}
updateView();
}
};
mGroupFileTransferListener = new GroupFileTransferListener() {
@Override
public void onStateChanged(String chatId, String transferId, FileTransfer.State state,
FileTransfer.ReasonCode reasonCode) {
}
@Override
public void onDeliveryInfoChanged(String chatId, ContactId contact, String transferId,
GroupDeliveryInfo.Status status, GroupDeliveryInfo.ReasonCode reasonCode) {
}
@Override
public void onProgressUpdate(String chatId, String transferId, long currentSize,
long totalSize) {
}
@Override
public void onDeleted(String chatId, Set transferIds) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDeleted ftIds=" + transferIds);
}
updateView();
}
};
mGroupChatListener = new GroupChatListener() {
@Override
public void onStateChanged(String chatId, GroupChat.State state,
GroupChat.ReasonCode reasonCode) {
}
@Override
public void onComposingEvent(String chatId, ContactId contact, boolean status) {
}
@Override
public void onMessageStatusChanged(String chatId, String mimeType, String msgId,
ChatLog.Message.Content.Status status,
ChatLog.Message.Content.ReasonCode reasonCode) {
}
@Override
public void onMessageGroupDeliveryInfoChanged(String chatId, ContactId contact,
String mimeType, String msgId, GroupDeliveryInfo.Status status,
GroupDeliveryInfo.ReasonCode reasonCode) {
}
@Override
public void onParticipantStatusChanged(String chatId, ContactId contact,
GroupChat.ParticipantStatus status) {
}
@Override
public void onDeleted(Set chatIds) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDeleted chatIds=" + chatIds);
}
updateView();
}
@Override
public void onMessagesDeleted(final String chatId, Set msgIds) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onMessagesDeleted msgIds=" + msgIds);
}
updateView();
}
};
mUpdateTalkListListener = new TalkListUpdate.TaskCompleted() {
@Override
public void onTaskComplete(Collection talks) {
if (talks == null) {
TalkList.this.showMessageThenExit(R.string.cannot_read_log);
return;
}
mMessageLogs.clear();
mMessageLogs.addAll(talks);
/* Sort by descending timestamp */
Collections.sort(mMessageLogs);
mAdapter.notifyDataSetChanged();
ChatPendingIntentManager talkPendingIntentManager = ChatPendingIntentManager
.getChatPendingIntentManager(mCtx);
for (TalkListArrayItem talk : talks) {
if (talk.getUnreadCount() == 0) {
/*
* TODO check that if invitation is pending, there is always at least one
* unread message (assertion may be false)
*/
talkPendingIntentManager.clearNotification(talk.getChatId());
}
}
}
};
}
/**
* Notify new conversation event
*
* @param ctx the context
* @param action the action intent
*/
public static void notifyNewConversationEvent(Context ctx, String action) {
if (sActivityVisible) {
Intent intent = new Intent(ctx, TalkList.class);
intent.setAction(action);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(intent);
}
}
private void addServiceListeners() {
if (!isServiceConnected(RcsServiceName.FILE_TRANSFER, RcsServiceName.CHAT)) {
return;
}
try {
if (!mGroupChatListenerSet) {
mChatService.addEventListener(mGroupChatListener);
mGroupChatListenerSet = true;
}
if (!mOneToOneChatListenerSet) {
mChatService.addEventListener(mOneToOneChatListener);
mOneToOneChatListenerSet = true;
}
if (!mFileTransferListenerSet) {
mFileTransferService.addEventListener(mOneToOneFileTransferListener);
mFileTransferListenerSet = true;
}
if (!mGroupFileTransferListenerSet) {
mFileTransferService.addEventListener(mGroupFileTransferListener);
mGroupFileTransferListenerSet = true;
}
} catch (RcsServiceNotAvailableException ignore) {
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
private void removeServiceListeners() {
if (!isServiceConnected(RcsServiceName.FILE_TRANSFER, RcsServiceName.CHAT)) {
return;
}
try {
if (mGroupChatListenerSet) {
mChatService.removeEventListener(mGroupChatListener);
mGroupChatListenerSet = false;
}
if (mOneToOneChatListenerSet) {
mChatService.removeEventListener(mOneToOneChatListener);
mOneToOneChatListenerSet = false;
}
if (mFileTransferListenerSet) {
mFileTransferService.removeEventListener(mOneToOneFileTransferListener);
mFileTransferListenerSet = false;
}
if (mGroupFileTransferListenerSet) {
mFileTransferService.removeEventListener(mGroupFileTransferListener);
mGroupFileTransferListenerSet = false;
}
} catch (RcsServiceNotAvailableException ignore) {
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/TalkListUpdate.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.ri.messaging.adapter.TalkListArrayItem;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import com.gsma.services.rcs.history.HistoryLog;
import com.gsma.services.rcs.history.HistoryUriBuilder;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* A class to update the talk list in background
*
* @author Philippe LEMORDANT
*/
public class TalkListUpdate extends AsyncTask> {
// @formatter:off
private static final String[] PROJECTION = new String[]{
HistoryLog.BASECOLUMN_ID,
HistoryLog.ID,
HistoryLog.CHAT_ID,
HistoryLog.PROVIDER_ID,
HistoryLog.MIME_TYPE,
HistoryLog.CONTENT,
HistoryLog.FILENAME,
HistoryLog.TIMESTAMP,
HistoryLog.DIRECTION,
HistoryLog.CONTACT,
HistoryLog.READ_STATUS,
HistoryLog.STATUS
};
// @formatter:on
private final TaskCompleted mTaskCompleted;
private final Context mCtx;
private static final String LOGTAG = LogUtils.getTag(TalkListUpdate.class.getSimpleName());
public TalkListUpdate(Context ctx, TaskCompleted taskCompleted) {
mCtx = ctx;
mTaskCompleted = taskCompleted;
}
@Override
protected Collection doInBackground(Void... params) {
try {
/*
* The MMS sending is performed in background because the API returns a message instance
* only once it is persisted and to persist MMS, the core stack computes the file icon
* for image attached files.
*/
return queryHistoryLogAndRefreshView();
} catch (RuntimeException e) {
Log.e(LOGTAG, ExceptionUtil.getFullStackTrace(e));
return null;
}
}
@Override
protected void onPostExecute(Collection result) {
if (mTaskCompleted != null) {
mTaskCompleted.onTaskComplete(result);
}
}
public interface TaskCompleted {
void onTaskComplete(Collection result);
}
private boolean isUnread(int providerId, RcsService.Direction dir,
RcsService.ReadStatus readStatus, int status) {
switch (providerId) {
case ChatLog.GroupChat.HISTORYLOG_MEMBER_ID:
return false;
case ChatLog.Message.HISTORYLOG_MEMBER_ID:
return RcsService.Direction.INCOMING == dir
&& RcsService.ReadStatus.UNREAD == readStatus;
case FileTransferLog.HISTORYLOG_MEMBER_ID:
if (RcsService.Direction.INCOMING != dir) {
return false;
}
FileTransfer.State state = FileTransfer.State.valueOf(status);
switch (state) {
case INVITED:
case TRANSFERRED:
case ACCEPTING:
case PAUSED:
case STARTED:
return RcsService.ReadStatus.UNREAD == readStatus;
default:
/*
* We consider that if the file transfer is rejected or failed then it
* cannot be read but should not be considered as unread.
*/
return false;
}
}
throw new IllegalArgumentException("Invalid provider ID=" + providerId);
}
Collection queryHistoryLogAndRefreshView() {
HistoryUriBuilder uriBuilder = new HistoryUriBuilder(HistoryLog.CONTENT_URI);
uriBuilder.appendProvider(ChatLog.GroupChat.HISTORYLOG_MEMBER_ID);
uriBuilder.appendProvider(ChatLog.Message.HISTORYLOG_MEMBER_ID);
uriBuilder.appendProvider(FileTransferLog.HISTORYLOG_MEMBER_ID);
Uri mUriHistoryProvider = uriBuilder.build();
Map dataMap = new HashMap<>();
Cursor cursor = null;
try {
cursor = mCtx.getContentResolver().query(mUriHistoryProvider, PROJECTION, null, null,
null);
if (cursor == null) {
throw new SQLException("Cannot query History Log");
}
int columnTimestamp = cursor.getColumnIndexOrThrow(HistoryLog.TIMESTAMP);
int columnProviderId = cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID);
int columnDirection = cursor.getColumnIndexOrThrow(HistoryLog.DIRECTION);
int columnChatId = cursor.getColumnIndexOrThrow(HistoryLog.CHAT_ID);
int columnContact = cursor.getColumnIndexOrThrow(HistoryLog.CONTACT);
int columnContent = cursor.getColumnIndexOrThrow(HistoryLog.CONTENT);
int columnMimeType = cursor.getColumnIndexOrThrow(HistoryLog.MIME_TYPE);
int columnReadStatus = cursor.getColumnIndexOrThrow(HistoryLog.READ_STATUS);
int columnFilename = cursor.getColumnIndexOrThrow(HistoryLog.FILENAME);
int columnStatus = cursor.getColumnIndexOrThrow(HistoryLog.STATUS);
while (cursor.moveToNext()) {
long timestamp = cursor.getLong(columnTimestamp);
String chatId = cursor.getString(columnChatId);
String phoneNumber = cursor.getString(columnContact);
ContactId contact = null;
if (phoneNumber != null) {
contact = ContactUtil.formatContact(phoneNumber);
}
RcsService.Direction dir = RcsService.Direction.valueOf(cursor
.getInt(columnDirection));
RcsService.ReadStatus readStatus = RcsService.ReadStatus.valueOf(cursor
.getInt(columnReadStatus));
TalkListArrayItem item = dataMap.get(chatId);
int providerId = cursor.getInt(columnProviderId);
int status = cursor.getInt(columnStatus);
boolean unread = isUnread(providerId, dir, readStatus, status);
int unreadCount = unread ? 1 : 0;
if (item != null) {
/* Is it the newest item ? */
if (timestamp < item.getTimestamp()) {
/*
* it is not the newest item then increment unread count of newest one then
* read next
*/
if (unread) {
item.incrementUnreadCount();
}
continue;
}
unreadCount += item.getUnreadCount();
}
String content;
if (FileTransferLog.HISTORYLOG_MEMBER_ID != providerId) {
/* There is not body text message for RCS file transfer */
content = cursor.getString(columnContent);
} else {
content = cursor.getString(columnFilename);
}
String mimeType = cursor.getString(columnMimeType);
if (ChatLog.GroupChat.HISTORYLOG_MEMBER_ID == providerId) {
if (item != null) {
item.setSubject(content);
} else {
item = new TalkListArrayItem(chatId, contact, timestamp, dir, null,
mimeType, unreadCount);
item.setSubject(content);
}
} else if (item != null) {
String subject = item.getSubject();
item = new TalkListArrayItem(chatId, contact, timestamp, dir, content,
mimeType, unreadCount);
item.setSubject(subject);
} else {
item = new TalkListArrayItem(chatId, contact, timestamp, dir, content,
mimeType, unreadCount);
}
dataMap.put(chatId, item);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return dataMap.values();
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/TestMessagingApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.chat.TestChatApi;
import com.gsma.rcs.ri.messaging.filetransfer.TestFileTransferApi;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* Messaging API
*
* @author Jean-Marc AUFFRET
*/
public class TestMessagingApi extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Set items
// @formatter:off
String[] items = {
getString(R.string.menu_file_transfer),
getString(R.string.menu_chat),
getString(R.string.menu_messaging_log)
};
// @formatter:on
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, TestFileTransferApi.class));
break;
case 1:
startActivity(new Intent(this, TestChatApi.class));
break;
case 2:
startActivity(new Intent(this, TalkList.class));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/BasicViewHolder.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.rcs.ri.R;
import com.gsma.services.rcs.history.HistoryLog;
import android.database.Cursor;
import android.view.View;
import android.widget.TextView;
/**
* A ViewHolder class keeps references to children views to avoid unnecessary calls to
* findViewById() or getColumnIndex() on each row.
*/
public class BasicViewHolder {
protected final int mColumnContactIdx;
protected final TextView mStatusText;
protected final TextView mTimestampText;
protected final int mColumnDirectionIdx;
protected final int mColumnTimestampIdx;
protected final TextView mContactText;
protected final int mColumnStatusIdx;
protected final int mColumnReasonCodeIdx;
protected final int mColumnMimetypeIdx;
protected final int mColumnReadStatusIdx;
protected final int mColumnIdIdx;
/**
* Constructor
*
* @param base view
* @param cursor cursor
*/
public BasicViewHolder(View base, Cursor cursor) {
/* Save column indexes */
mColumnDirectionIdx = cursor.getColumnIndexOrThrow(HistoryLog.DIRECTION);
mColumnTimestampIdx = cursor.getColumnIndexOrThrow(HistoryLog.TIMESTAMP);
mColumnStatusIdx = cursor.getColumnIndexOrThrow(HistoryLog.STATUS);
mColumnMimetypeIdx = cursor.getColumnIndexOrThrow(HistoryLog.MIME_TYPE);
mColumnReasonCodeIdx = cursor.getColumnIndexOrThrow(HistoryLog.REASON_CODE);
mColumnContactIdx = cursor.getColumnIndexOrThrow(HistoryLog.CONTACT);
mColumnReadStatusIdx = cursor.getColumnIndexOrThrow(HistoryLog.READ_STATUS);
mColumnIdIdx = cursor.getColumnIndexOrThrow(HistoryLog.ID);
/* Save children views */
mStatusText = (TextView) base.findViewById(R.id.status_text);
mTimestampText = (TextView) base.findViewById(R.id.timestamp_text);
mContactText = (TextView) base.findViewById(R.id.contact_text);
}
public TextView getStatusText() {
return mStatusText;
}
public TextView getTimestampText() {
return mTimestampText;
}
public TextView getContactText() {
return mContactText;
}
public int getColumnDirectionIdx() {
return mColumnDirectionIdx;
}
public int getColumnTimestampIdx() {
return mColumnTimestampIdx;
}
public int getColumnStatusIdx() {
return mColumnStatusIdx;
}
public int getColumnMimetypeIdx() {
return mColumnMimetypeIdx;
}
public int getColumnReasonCodeIdx() {
return mColumnReasonCodeIdx;
}
public int getColumnContactIdx() {
return mColumnContactIdx;
}
public int getColumnReadStatusIdx() {
return mColumnReadStatusIdx;
}
public int getColumnIdIdx() {
return mColumnIdIdx;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/GroupDeliveryInfoCursorAdapter.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfo;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfoLog;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.Locale;
/**
* @author YPLO6403
*/
public class GroupDeliveryInfoCursorAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private final static SimpleDateFormat df = new SimpleDateFormat("yy-MM-dd HH:mm:ss",
Locale.getDefault());
private Context mContext;
/**
* Constructor
*
* @param context The context
*/
public GroupDeliveryInfoCursorAdapter(Context context) {
super(context, null, 0);
mInflater = LayoutInflater.from(context);
mContext = context;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.delivery_info_item, parent, false);
// Save columns indexes and children views in cache
view.setTag(new ViewHolder(view, cursor));
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder holder = (ViewHolder) view.getTag();
// Set the deliver date/time field
long timestampDeliver = cursor.getLong(holder.columnTimestampDeliver);
// Set the deliver date/time field
long timestampDisplay = cursor.getLong(holder.columnTimestampDisplay);
// Set the status text
GroupDeliveryInfo.Status status = GroupDeliveryInfo.Status.valueOf(cursor
.getInt(holder.columnStatus));
// Set the reason text
GroupDeliveryInfo.ReasonCode reason = GroupDeliveryInfo.ReasonCode.valueOf(cursor
.getInt(holder.columnReason));
// Set the display name
String number = cursor.getString(holder.columnContact);
String displayName = RcsContactUtil.getInstance(mContext).getDisplayName(number);
holder.contactText.setText(mContext.getString(R.string.label_from_args, displayName));
if (timestampDeliver == 0) {
holder.deliverText.setVisibility(View.GONE);
} else {
holder.deliverText.setVisibility(View.VISIBLE);
holder.deliverText.setText(mContext.getString(R.string.label_state_delivered_at,
df.format(timestampDeliver)));
}
/* Display information is only applicable to file transfers */
if (timestampDisplay == 0) {
holder.displayText.setVisibility(View.GONE);
} else {
holder.displayText.setVisibility(View.VISIBLE);
holder.displayText.setText(mContext.getString(R.string.label_state_displayed_at,
df.format(timestampDisplay)));
}
String _status = mContext.getString(R.string.label_status,
RiApplication.sDeliveryStatuses[status.toInt()]);
holder.statusText.setText(_status);
if (reason != GroupDeliveryInfo.ReasonCode.UNSPECIFIED) {
holder.reasonText.setVisibility(View.VISIBLE);
String _reason = mContext.getString(R.string.label_reason_code_args,
RiApplication.sDeliveryReasonCode[reason.toInt()]);
holder.reasonText.setText(_reason);
} else {
holder.reasonText.setVisibility(View.GONE);
}
}
/**
* A ViewHolder class keeps references to children views to avoid unnecessary calls to
* findViewById() or getColumnIndex() on each row.
*/
private class ViewHolder {
TextView statusText;
TextView reasonText;
TextView deliverText;
TextView displayText;
TextView contactText;
int columnContact;
int columnTimestampDeliver;
int columnTimestampDisplay;
int columnStatus;
int columnReason;
/**
* Constructor
*
* @param base the view
* @param cursor cursor
*/
ViewHolder(View base, Cursor cursor) {
columnContact = cursor.getColumnIndexOrThrow(GroupDeliveryInfoLog.CONTACT);
columnTimestampDeliver = cursor
.getColumnIndexOrThrow(GroupDeliveryInfoLog.TIMESTAMP_DELIVERED);
columnTimestampDisplay = cursor
.getColumnIndexOrThrow(GroupDeliveryInfoLog.TIMESTAMP_DISPLAYED);
columnStatus = cursor.getColumnIndexOrThrow(GroupDeliveryInfoLog.STATUS);
columnReason = cursor.getColumnIndexOrThrow(GroupDeliveryInfoLog.REASON_CODE);
statusText = (TextView) base.findViewById(R.id.status);
contactText = (TextView) base.findViewById(R.id.contact);
deliverText = (TextView) base.findViewById(R.id.deliver);
displayText = (TextView) base.findViewById(R.id.display);
reasonText = (TextView) base.findViewById(R.id.reason);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/RcsChatInViewHolder.java
================================================
/*
* ******************************************************************************
* * Software Name : RCS IMS Stack
* *
* * Copyright (C) 2010-2016 Orange.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *****************************************************************************
*/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.rcs.ri.R;
import com.gsma.services.rcs.history.HistoryLog;
import android.database.Cursor;
import android.view.View;
import android.widget.TextView;
/**
* Created by yplo6403 on 01/12/2015.
*/
public class RcsChatInViewHolder extends BasicViewHolder {
private final TextView mContentText;
private final int mColumnContentIdx;
public RcsChatInViewHolder(View view, Cursor cursor) {
super(view, cursor);
/* Save column indexes */
mColumnContentIdx = cursor.getColumnIndexOrThrow(HistoryLog.CONTENT);
/* Save children views */
mContentText = (TextView) view.findViewById(R.id.content_text);
}
public TextView getContentText() {
return mContentText;
}
public int getColumnContentIdx() {
return mColumnContentIdx;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/RcsChatOutViewHolder.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.services.rcs.history.HistoryLog;
import android.database.Cursor;
import android.view.View;
public class RcsChatOutViewHolder extends RcsChatInViewHolder {
private final int mColumnExpiredDeliveryIdx;
public RcsChatOutViewHolder(View view, Cursor cursor) {
super(view, cursor);
/* Save column indexes */
mColumnExpiredDeliveryIdx = cursor.getColumnIndexOrThrow(HistoryLog.EXPIRED_DELIVERY);
}
public int getColumnExpiredDeliveryIdx() {
return mColumnExpiredDeliveryIdx;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/RcsFileTransferInViewHolder.java
================================================
/*
* ******************************************************************************
* * Software Name : RCS IMS Stack
* *
* * Copyright (C) 2010-2016 Orange.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *****************************************************************************
*/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.rcs.ri.R;
import com.gsma.services.rcs.history.HistoryLog;
import android.database.Cursor;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
* A ViewHolder class keeps references to children views to avoid unnecessary calls to
* findViewById() or getColumnIndex() on each row.
*/
public class RcsFileTransferInViewHolder extends BasicViewHolder {
private final TextView mProgressText;
private final ImageView mFileImageView;
private final int mColumnContentIdx;
private final int mColumnFilenameIdx;
private final int mColumnFilesizeIdx;
private final int mColumnTransferredIdx;
private final int mColumnContactIdx;
/**
* Constructor
*
* @param base view
* @param cursor cursor
*/
RcsFileTransferInViewHolder(View base, Cursor cursor) {
super(base, cursor);
/* Save column indexes */
mColumnContentIdx = cursor.getColumnIndexOrThrow(HistoryLog.CONTENT);
mColumnFilenameIdx = cursor.getColumnIndexOrThrow(HistoryLog.FILENAME);
mColumnFilesizeIdx = cursor.getColumnIndexOrThrow(HistoryLog.FILESIZE);
mColumnTransferredIdx = cursor.getColumnIndexOrThrow(HistoryLog.TRANSFERRED);
mColumnContactIdx = cursor.getColumnIndexOrThrow(HistoryLog.CONTACT);
/* Save children views */
mProgressText = (TextView) base.findViewById(R.id.progress_text);
mFileImageView = (ImageView) base.findViewById(R.id.file_image);
}
public int getColumnContentIdx() {
return mColumnContentIdx;
}
public TextView getProgressText() {
return mProgressText;
}
public ImageView getFileImageView() {
return mFileImageView;
}
public int getColumnFilenameIdx() {
return mColumnFilenameIdx;
}
public int getColumnFilesizeIdx() {
return mColumnFilesizeIdx;
}
public int getColumnTransferredIdx() {
return mColumnTransferredIdx;
}
public int getColumnContactIdx() {
return mColumnContactIdx;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/RcsFileTransferOutViewHolder.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.services.rcs.history.HistoryLog;
import android.database.Cursor;
import android.view.View;
/**
* A ViewHolder class keeps references to children views to avoid unnecessary calls to
* findViewById() or getColumnIndex() on each row.
*/
public class RcsFileTransferOutViewHolder extends RcsFileTransferInViewHolder {
private final int mColumnExpiredDeliveryIdx;
/**
* Constructor
*
* @param base view
* @param cursor cursor
*/
RcsFileTransferOutViewHolder(View base, Cursor cursor) {
super(base, cursor);
/* Save column indexes */
mColumnExpiredDeliveryIdx = cursor.getColumnIndexOrThrow(HistoryLog.EXPIRED_DELIVERY);
}
public int getColumnExpiredDeliveryIdx() {
return mColumnExpiredDeliveryIdx;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/TalkCursorAdapter.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.BitmapCache;
import com.gsma.rcs.ri.utils.BitmapLoader;
import com.gsma.rcs.ri.utils.BitmapLoader.BitmapCacheInfo;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.ImageBitmapLoader;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.SmileyParser;
import com.gsma.rcs.ri.utils.Smileys;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsPersistentStorageException;
import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.ChatLog.Message.Content;
import com.gsma.services.rcs.chat.ChatLog.Message.MimeType;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransfer.ReasonCode;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.history.HistoryLog;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.net.Uri;
import android.support.v4.util.LruCache;
import android.support.v4.widget.CursorAdapter;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Map;
public class TalkCursorAdapter extends CursorAdapter {
private static final String LOGTAG = LogUtils.getTag(TalkCursorAdapter.class.getName());
private static final int MAX_IMAGE_HEIGHT = 100;
private static final int MAX_IMAGE_WIDTH = 100;
private static final int VIEW_TYPE_RCS_CHAT_IN = 0;
private static final int VIEW_TYPE_RCS_CHAT_OUT = 1;
private static final int VIEW_TYPE_RCS_FILE_TRANSFER_IN = 2;
private static final int VIEW_TYPE_RCS_FILE_TRANSFER_OUT = 3;
private static final int VIEW_TYPE_RCS_GROUP_CHAT_EVENT = 4;
private final ChatService mChatService;
private final FileTransferService mFileTransferService;
private BitmapCache bitmapCache;
private final Activity mActivity;
private Map mContactIdDisplayNameMap;
private final LayoutParams mImageParams;
private final LayoutParams mImageParamsDefault;
private LayoutInflater mInflater;
private final boolean mSingleChat;
private Smileys mSmileyResources;
/**
* Constructor
*
* @param activity The activity
* @param singleChat True if single chat
* @param chatService the chat service
* @param fileTransferService the file transfer service
*/
public TalkCursorAdapter(Activity activity, boolean singleChat, ChatService chatService,
FileTransferService fileTransferService) {
super(activity, null, 0);
mContactIdDisplayNameMap = new HashMap<>();
mActivity = activity;
mInflater = LayoutInflater.from(activity);
int size100Dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100.0f,
mContext.getResources().getDisplayMetrics());
mImageParams = new LayoutParams(size100Dp, size100Dp);
Options opt = new Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeResource(mActivity.getResources(), R.drawable.ri_filetransfer_off, opt);
mImageParamsDefault = new LayoutParams(opt.outWidth * 2, opt.outHeight * 2);
bitmapCache = BitmapCache.getInstance();
mSmileyResources = new Smileys(activity);
mSingleChat = singleChat;
mChatService = chatService;
mFileTransferService = fileTransferService;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
int viewType = getItemViewType(cursor);
switch (viewType) {
case VIEW_TYPE_RCS_CHAT_IN:
View view = mInflater.inflate(mSingleChat ? R.layout.talk_item_rcs_chat_in
: R.layout.gchat_item_rcs_chat_in, parent, false);
view.setTag(new RcsChatInViewHolder(view, cursor));
return view;
case VIEW_TYPE_RCS_CHAT_OUT:
view = mInflater.inflate(mSingleChat ? R.layout.talk_item_rcs_chat_out
: R.layout.gchat_item_rcs_chat_out, parent, false);
view.setTag(new RcsChatOutViewHolder(view, cursor));
return view;
case VIEW_TYPE_RCS_FILE_TRANSFER_IN:
view = mInflater.inflate(mSingleChat ? R.layout.talk_item_rcs_file_transfer_in
: R.layout.gchat_item_rcs_file_transfer_in, parent, false);
view.setTag(new RcsFileTransferInViewHolder(view, cursor));
return view;
case VIEW_TYPE_RCS_FILE_TRANSFER_OUT:
view = mInflater.inflate(mSingleChat ? R.layout.talk_item_rcs_file_transfer_out
: R.layout.gchat_item_rcs_file_transfer_out, parent, false);
view.setTag(new RcsFileTransferOutViewHolder(view, cursor));
return view;
case VIEW_TYPE_RCS_GROUP_CHAT_EVENT:
view = mInflater.inflate(R.layout.groupchat_event_view_item, parent, false);
view.setTag(new BasicViewHolder(view, cursor));
return view;
default:
throw new IllegalArgumentException("Invalid view type: '" + viewType + "'!");
}
}
private void bindRemoteContact(View view, Context ctx, Cursor cursor) {
BasicViewHolder holder = (BasicViewHolder) view.getTag();
String displayName = null;
if (Direction.OUTGOING != Direction.valueOf(cursor.getInt(holder.getColumnDirectionIdx()))) {
String number = cursor.getString(holder.getColumnContactIdx());
if (number != null) {
ContactId contact = ContactUtil.formatContact(number);
if (mContactIdDisplayNameMap.containsKey(contact)) {
displayName = mContactIdDisplayNameMap.get(contact);
} else {
displayName = RcsContactUtil.getInstance(ctx).getDisplayName(contact);
mContactIdDisplayNameMap.put(contact, displayName);
}
}
holder.getContactText().setText(displayName);
}
}
@Override
public void bindView(View view, Context ctx, Cursor cursor) {
if (!mSingleChat) {
bindRemoteContact(view, ctx, cursor);
}
int viewType = getItemViewType(cursor);
switch (viewType) {
case VIEW_TYPE_RCS_CHAT_IN:
bindRcsChatInView(view, cursor);
break;
case VIEW_TYPE_RCS_CHAT_OUT:
bindRcsChatOutView(view, cursor);
break;
case VIEW_TYPE_RCS_FILE_TRANSFER_IN:
bindRcsFileTransferInView(view, cursor);
break;
case VIEW_TYPE_RCS_FILE_TRANSFER_OUT:
bindRcsFileTransferOutView(view, cursor);
break;
case VIEW_TYPE_RCS_GROUP_CHAT_EVENT:
bindRcsGroupChatEvent(view, cursor);
break;
default:
throw new IllegalArgumentException("Invalid view type: '" + viewType + "'!");
}
}
public int getItemViewType(Cursor cursor) {
int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID));
Direction direction = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.DIRECTION)));
String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.MIME_TYPE));
switch (providerId) {
case ChatLog.Message.HISTORYLOG_MEMBER_ID:
switch (mimeType) {
case ChatLog.Message.MimeType.GROUPCHAT_EVENT:
return VIEW_TYPE_RCS_GROUP_CHAT_EVENT;
case ChatLog.Message.MimeType.GEOLOC_MESSAGE:
case ChatLog.Message.MimeType.TEXT_MESSAGE:
if (Direction.INCOMING == direction) {
return VIEW_TYPE_RCS_CHAT_IN;
}
return VIEW_TYPE_RCS_CHAT_OUT;
}
throw new IllegalArgumentException("Invalid mime type: '" + mimeType + "'!");
case FileTransferLog.HISTORYLOG_MEMBER_ID:
if (Direction.INCOMING == direction) {
return VIEW_TYPE_RCS_FILE_TRANSFER_IN;
}
return VIEW_TYPE_RCS_FILE_TRANSFER_OUT;
}
throw new IllegalArgumentException("Invalid provider ID: '" + providerId + "'!");
}
@Override
public int getItemViewType(int position) {
return getItemViewType((Cursor) getItem(position));
}
@Override
public int getViewTypeCount() {
return 5;
}
private void bindRcsGroupChatEvent(View view, Cursor cursor) {
BasicViewHolder holder = (BasicViewHolder) view.getTag();
holder.getTimestampText().setText(
DateUtils.getRelativeTimeSpanString(cursor.getLong(holder.getColumnTimestampIdx()),
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
String event = RiApplication.sGroupChatEvents[cursor.getInt(holder.getColumnStatusIdx())];
holder.getStatusText().setText(mActivity.getString(R.string.label_groupchat_event, event));
}
private void bindRcsFileTransferOutView(View view, Cursor cursor) {
RcsFileTransferOutViewHolder holder = (RcsFileTransferOutViewHolder) view.getTag();
holder.getTimestampText().setText(
DateUtils.getRelativeTimeSpanString(cursor.getLong(holder.getColumnTimestampIdx()),
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
String mimeType = cursor.getString(holder.getColumnMimetypeIdx());
StringBuilder sb = new StringBuilder(cursor.getString(holder.getColumnFilenameIdx()));
long filesize = cursor.getLong(holder.getColumnFilesizeIdx());
long transferred = cursor.getLong(holder.getColumnTransferredIdx());
final ImageView imageView = holder.getFileImageView();
imageView.setOnClickListener(null);
imageView.setLayoutParams(mImageParamsDefault);
imageView.setImageResource(R.drawable.ri_filetransfer_on);
if (filesize != transferred) {
holder.getProgressText().setText(
sb.append(" : ").append(Utils.getProgressLabel(transferred, filesize))
.toString());
} else {
holder.getProgressText().setText(
sb.append(" (").append(FileUtils.humanReadableByteCount(filesize, true))
.append(")").toString());
}
final Uri file = Uri.parse(cursor.getString(holder.getColumnContentIdx()));
if (Utils.isImageType(mimeType)) {
String filePath = FileUtils.getPath(mContext, file);
Bitmap imageBitmap = null;
if (filePath != null) {
LruCache memoryCache = bitmapCache.getMemoryCache();
BitmapCacheInfo bitmapCacheInfo = memoryCache.get(filePath);
if (bitmapCacheInfo == null) {
ImageBitmapLoader loader = new ImageBitmapLoader(mContext, memoryCache,
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT, new BitmapLoader.SetViewCallback() {
@Override
public void loadView(BitmapCacheInfo cacheInfo) {
imageView.setImageBitmap(cacheInfo.getBitmap());
imageView.setLayoutParams(mImageParams);
}
});
loader.execute(filePath);
} else {
imageBitmap = bitmapCacheInfo.getBitmap();
}
if (imageBitmap != null) {
imageView.setImageBitmap(imageBitmap);
imageView.setLayoutParams(mImageParams);
}
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Utils.showPicture(mActivity, file);
}
});
}
} else if (Utils.isAudioType(mimeType)) {
imageView.setImageResource(R.drawable.headphone);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Utils.playAudio(mActivity, file);
}
});
}
holder.getStatusText().setText(getRcsFileTransferStatus(cursor, holder));
boolean undeliveredExpiration = cursor.getInt(holder.getColumnExpiredDeliveryIdx()) == 1;
holder.getStatusText().setCompoundDrawablesWithIntrinsicBounds(
undeliveredExpiration ? R.drawable.chat_view_undelivered : 0, 0, 0, 0);
}
private void bindRcsFileTransferInView(View view, Cursor cursor) {
final RcsFileTransferInViewHolder holder = (RcsFileTransferInViewHolder) view.getTag();
holder.getTimestampText().setText(
DateUtils.getRelativeTimeSpanString(cursor.getLong(holder.getColumnTimestampIdx()),
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
String mimeType = cursor.getString(holder.getColumnMimetypeIdx());
StringBuilder sb = new StringBuilder(cursor.getString(holder.getColumnFilenameIdx()));
long filesize = cursor.getLong(holder.getColumnFilesizeIdx());
long transferred = cursor.getLong(holder.getColumnTransferredIdx());
final ImageView imageView = holder.getFileImageView();
imageView.setOnClickListener(null);
imageView.setLayoutParams(mImageParamsDefault);
imageView.setImageResource(R.drawable.ri_filetransfer_off);
if (filesize != transferred) {
holder.getProgressText().setText(
sb.append(" : ").append(Utils.getProgressLabel(transferred, filesize))
.toString());
} else {
imageView.setImageResource(R.drawable.ri_filetransfer_on);
final Uri file = Uri.parse(cursor.getString(holder.getColumnContentIdx()));
final RcsService.ReadStatus readStatus = RcsService.ReadStatus.valueOf(cursor
.getInt(holder.getColumnReadStatusIdx()));
final String id = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID));
if (Utils.isImageType(mimeType)) {
final String filePath = FileUtils.getPath(mContext, file);
if (filePath != null) {
LruCache memoryCache = bitmapCache.getMemoryCache();
BitmapCacheInfo bitmapCacheInfo = memoryCache.get(filePath);
if (bitmapCacheInfo == null) {
ImageBitmapLoader loader = new ImageBitmapLoader(mContext, memoryCache,
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT,
new BitmapLoader.SetViewCallback() {
@Override
public void loadView(BitmapCacheInfo cacheInfo) {
imageView.setImageBitmap(cacheInfo.getBitmap());
imageView.setLayoutParams(mImageParams);
}
});
loader.execute(filePath);
} else {
Bitmap imageBitmap = bitmapCacheInfo.getBitmap();
imageView.setImageBitmap(imageBitmap);
imageView.setLayoutParams(mImageParams);
}
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Utils.showPicture(mActivity, file);
markFileTransferAsRead(id, readStatus);
}
});
}
} else if (Utils.isAudioType(mimeType)) {
imageView.setImageResource(R.drawable.headphone);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Utils.playAudio(mActivity, file);
markFileTransferAsRead(id, readStatus);
}
});
}
holder.getProgressText().setText(
sb.append(" (").append(FileUtils.humanReadableByteCount(filesize, true))
.append(")").toString());
}
holder.getStatusText().setText(getRcsFileTransferStatus(cursor, holder));
}
private void markFileTransferAsRead(String ftId, RcsService.ReadStatus readStatus) {
try {
if (RcsService.ReadStatus.UNREAD == readStatus) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Mark file transfer " + ftId + " as read");
}
mFileTransferService.markFileTransferAsRead(ftId);
}
} catch (RcsServiceNotAvailableException e) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Cannot mark message as read: service not available");
}
} catch (RcsGenericException | RcsPersistentStorageException e) {
Log.e(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
private RcsChatInViewHolder bindRcsChatView(View view, Cursor cursor) {
RcsChatInViewHolder holder = (RcsChatInViewHolder) view.getTag();
holder.getTimestampText().setText(
DateUtils.getRelativeTimeSpanString(cursor.getLong(holder.getColumnTimestampIdx()),
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
String mimeType = cursor.getString(holder.getColumnMimetypeIdx());
TextView contentText = holder.getContentText();
String data = cursor.getString(holder.getColumnContentIdx());
if (MimeType.TEXT_MESSAGE.equals(mimeType)) {
contentText.setText(formatMessageWithSmiley(data));
} else {
contentText.setText(formatGeolocation(mContext, new Geoloc(data)));
}
holder.getStatusText().setText(getRcsChatStatus(cursor, holder));
return holder;
}
private void bindRcsChatOutView(View view, Cursor cursor) {
RcsChatOutViewHolder holder = (RcsChatOutViewHolder) bindRcsChatView(view, cursor);
boolean undeliveredExpiration = cursor.getInt(holder.getColumnExpiredDeliveryIdx()) == 1;
holder.getStatusText().setCompoundDrawablesWithIntrinsicBounds(
undeliveredExpiration ? R.drawable.chat_view_undelivered : 0, 0, 0, 0);
}
private void bindRcsChatInView(View view, Cursor cursor) {
RcsChatInViewHolder holder = bindRcsChatView(view, cursor);
// Only mark message as read when actually displayed on screen
markChatMessageAsRead(cursor, holder);
}
private void markChatMessageAsRead(Cursor cursor, RcsChatInViewHolder holder) {
try {
RcsService.ReadStatus readStatus = RcsService.ReadStatus.valueOf(cursor.getInt(holder
.getColumnReadStatusIdx()));
if (RcsService.ReadStatus.UNREAD == readStatus) {
String msgId = cursor.getString(holder.getColumnIdIdx());
if (LogUtils.isActive) {
Log.d(LOGTAG, "Mark message " + msgId + " as read");
}
mChatService.markMessageAsRead(msgId);
}
} catch (RcsServiceNotAvailableException e) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Cannot mark message as read: service not available");
}
} catch (RcsGenericException | RcsPersistentStorageException e) {
Log.e(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
private String getRcsFileTransferStatus(Cursor cursor, RcsFileTransferInViewHolder holder) {
FileTransfer.State state = FileTransfer.State.valueOf((int) cursor.getLong(holder
.getColumnStatusIdx()));
StringBuilder status = new StringBuilder(RiApplication.sFileTransferStates[state.toInt()]);
ReasonCode reason = ReasonCode
.valueOf((int) cursor.getLong(holder.getColumnReasonCodeIdx()));
if (ReasonCode.UNSPECIFIED != reason) {
status.append(" / ");
status.append(RiApplication.sFileTransferReasonCodes[reason.toInt()]);
}
return status.toString();
}
private String getRcsChatStatus(Cursor cursor, RcsChatInViewHolder holder) {
ChatLog.Message.Content.Status state = ChatLog.Message.Content.Status.valueOf((int) cursor
.getLong(holder.getColumnStatusIdx()));
StringBuilder status = new StringBuilder(RiApplication.sMessagesStatuses[state.toInt()]);
Content.ReasonCode reason = Content.ReasonCode.valueOf((int) cursor.getLong(holder
.getColumnReasonCodeIdx()));
if (Content.ReasonCode.UNSPECIFIED != reason) {
status.append(" / ");
status.append(RiApplication.sMessageReasonCodes[reason.toInt()]);
}
return status.toString();
}
/**
* Format geolocation
*
* @param context context
* @param geoloc The geolocation
* @return a formatted text
*/
public static String formatGeolocation(Context context, Geoloc geoloc) {
StringBuilder result = new StringBuilder(context.getString(R.string.label_geolocation_msg))
.append("\n");
String label = geoloc.getLabel();
if (label != null) {
result.append(context.getString(R.string.label_location)).append(" ").append(label)
.append("\n");
}
return result.append(context.getString(R.string.label_latitude)).append(" ")
.append(geoloc.getLatitude()).append("\n")
.append(context.getString(R.string.label_longitude)).append(" ")
.append(geoloc.getLongitude()).append("\n")
.append(context.getString(R.string.label_accuracy)).append(" ")
.append(geoloc.getAccuracy()).toString();
}
private CharSequence formatMessageWithSmiley(String txt) {
SpannableStringBuilder buf = new SpannableStringBuilder();
if (!TextUtils.isEmpty(txt)) {
SmileyParser smileyParser = new SmileyParser(txt, mSmileyResources);
smileyParser.parse();
buf.append(smileyParser.getSpannableString(mContext));
}
return buf;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/TalkListArrayAdapter.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.contact.ContactId;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.text.format.DateUtils;
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 java.util.List;
/**
* Conversation cursor adapter
*/
public class TalkListArrayAdapter extends ArrayAdapter {
private final RcsContactUtil mRcsContactUtil;
private final LayoutInflater mInflater;
private final Context mCtx;
private static final int VIEW_TYPE_1TO1_CHAT = 0;
private static final int VIEW_TYPE_GROUP_CHAT = 1;
public TalkListArrayAdapter(Context context, List messageLogs) {
super(context, R.layout.chat_list, messageLogs);
mCtx = context;
mRcsContactUtil = RcsContactUtil.getInstance(context);
mInflater = ((Activity) context).getLayoutInflater();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
int viewType = getItemViewType(position);
if (VIEW_TYPE_1TO1_CHAT == viewType) {
convertView = mInflater.inflate(R.layout.talk_log_one_to_one_item, parent, false);
convertView.setTag(new TalkListArrayItem.ViewHolderOneToOne(convertView));
} else {
convertView = mInflater.inflate(R.layout.talk_log_group_item, parent, false);
convertView.setTag(new TalkListArrayItem.ViewHolderGroup(convertView));
}
}
bindView(convertView, position);
return convertView;
}
@Override
public int getItemViewType(int position) {
TalkListArrayItem item = getItem(position);
return item.isGroupChat() ? VIEW_TYPE_GROUP_CHAT : VIEW_TYPE_1TO1_CHAT;
}
@Override
public int getViewTypeCount() {
return 2;
}
public void bindView(View view, int position) {
TalkListArrayItem item = getItem(position);
int viewType = getItemViewType(position);
TalkListArrayItem.ViewHolder holder = (TalkListArrayItem.ViewHolder) view.getTag();
setTimestamp(holder, item.getTimestamp());
String content = item.getContent();
if (ChatLog.Message.MimeType.GEOLOC_MESSAGE.equals(item.getMimeType())) {
content = TalkCursorAdapter.formatGeolocation(mCtx, new Geoloc(item.getContent()));
}
setContent(holder, content);
setStatus(holder, item.getUnreadCount());
switch (viewType) {
case VIEW_TYPE_1TO1_CHAT:
bindViewOneToOneTalk(item, (TalkListArrayItem.ViewHolderOneToOne) holder);
break;
case VIEW_TYPE_GROUP_CHAT:
bindViewGroupChat(item, (TalkListArrayItem.ViewHolderGroup) holder);
break;
default:
throw new IllegalArgumentException("Invalid view type: '" + viewType + "'!");
}
}
private void bindViewGroupChat(TalkListArrayItem item, TalkListArrayItem.ViewHolderGroup holder) {
holder.getSubjectText().setText(item.getSubject());
}
private void bindViewOneToOneTalk(TalkListArrayItem item,
TalkListArrayItem.ViewHolderOneToOne holder) {
ImageView avatar = holder.getAvatarImage();
ContactId contact = item.getContact();
Bitmap photo = mRcsContactUtil.getPhotoFromContactId(contact);
if (photo != null) {
avatar.setImageBitmap(photo);
} else {
avatar.setImageResource(R.drawable.person);
}
setContact(holder, item.getContact());
}
private void setContent(TalkListArrayItem.ViewHolder holder, String content) {
holder.getContentText().setText(content != null ? content : "");
}
private void setTimestamp(TalkListArrayItem.ViewHolder holder, long timestamp) {
/* Set the date/time field by mixing relative and absolute times */
holder.getTimestampText().setText(
DateUtils.getRelativeTimeSpanString(timestamp, System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE));
}
private void setStatus(TalkListArrayItem.ViewHolder holder, int unReads) {
TextView statusText = holder.getStatusText();
if (unReads == 0) {
statusText.setVisibility(View.INVISIBLE);
} else {
statusText.setVisibility(View.VISIBLE);
String countUnReads = Integer.valueOf(unReads).toString();
if (unReads <= 9) {
countUnReads = " ".concat(countUnReads);
}
statusText.setText(countUnReads);
}
}
private void setContact(TalkListArrayItem.ViewHolderOneToOne holder, ContactId contact) {
String displayName = mRcsContactUtil.getDisplayName(contact);
holder.getContactText().setText(displayName);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/adapter/TalkListArrayItem.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.adapter;
import com.gsma.rcs.ri.R;
import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.contact.ContactId;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Created by yplo6403 on 12/01/2016.
*/
public class TalkListArrayItem implements Comparable {
private final long mTimestamp;
private final RcsService.Direction mDirection;
private String mSubject;
private final String mChatId;
private final ContactId mContact;
private String mContent;
private final String mMimeType;
private int mUnreadCount;
/**
* Constructor for XMS and RCS chat information
*
* @param chatId the chat ID
* @param contact the contact ID
* @param timestamp the timestamp
* @param direction the direction
* @param content the content
* @param mimeType the mime type
* @param unreadCount the read status
*/
public TalkListArrayItem(String chatId, ContactId contact, long timestamp,
RcsService.Direction direction, String content, String mimeType, int unreadCount) {
mChatId = chatId;
mContact = contact;
mTimestamp = timestamp;
mDirection = direction;
mContent = content;
mMimeType = mimeType;
mUnreadCount = unreadCount;
}
public long getTimestamp() {
return mTimestamp;
}
public RcsService.Direction getDirection() {
return mDirection;
}
public ContactId getContact() {
return mContact;
}
public void setContent(String content) {
mContent = content;
}
public String getContent() {
return mContent;
}
public String getMimeType() {
return mMimeType;
}
public String getChatId() {
return mChatId;
}
public String getSubject() {
return mSubject;
}
public void setSubject(String subject) {
mSubject = subject;
}
@Override
public int compareTo(TalkListArrayItem another) {
if (another == null) {
throw new NullPointerException("Cannot compare to null");
}
return Long.valueOf(another.getTimestamp()).compareTo(mTimestamp);
}
public int getUnreadCount() {
return mUnreadCount;
}
public void incrementUnreadCount() {
mUnreadCount++;
}
public boolean isGroupChat() {
return mContact == null || !mChatId.equals(mContact.toString());
}
static public class ViewHolder {
private final TextView mStatusText;
private final TextView mTimestampText;
private final TextView mContentText;
private final ImageView mAvatarImage;
ViewHolder(View view) {
mAvatarImage = (ImageView) view.findViewById(R.id.avatar);
mStatusText = (TextView) view.findViewById(R.id.status_text);
mTimestampText = (TextView) view.findViewById(R.id.timestamp_text);
mContentText = (TextView) view.findViewById(R.id.content_text);
}
public TextView getStatusText() {
return mStatusText;
}
public TextView getTimestampText() {
return mTimestampText;
}
public TextView getContentText() {
return mContentText;
}
public ImageView getAvatarImage() {
return mAvatarImage;
}
}
static public class ViewHolderOneToOne extends ViewHolder {
private final TextView mContactText;
public ViewHolderOneToOne(View view) {
super(view);
mContactText = (TextView) view.findViewById(R.id.contact_text);
}
public TextView getContactText() {
return mContactText;
}
}
static public class ViewHolderGroup extends ViewHolder {
private final TextView mSubjectText;
public ViewHolderGroup(View view) {
super(view);
mSubjectText = (TextView) view.findViewById(R.id.subject_text);
}
public TextView getSubjectText() {
return mSubjectText;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/ChatCursorObserver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Handler;
import android.support.v4.content.Loader;
/**
* A content observer which holds the cursor loader in order to notify change on it.
*
* @author yplo6403
*/
public class ChatCursorObserver extends ContentObserver {
final private Loader mLoader;
public ChatCursorObserver(Handler handler, Loader loader) {
super(handler);
mLoader = loader;
}
public Loader getLoader() {
return mLoader;
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
if (null != mLoader) {
mLoader.onContentChanged();
}
super.onChange(selfChange);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/ChatMessageDAO.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.RcsService.ReadStatus;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.ChatLog.Message;
import com.gsma.services.rcs.chat.ChatLog.Message.Content.ReasonCode;
import com.gsma.services.rcs.chat.ChatLog.Message.Content.Status;
import com.gsma.services.rcs.chat.ChatLog.Message.GroupChatEvent;
import com.gsma.services.rcs.contact.ContactId;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
/**
* CHAT Message Data Object
*
* @author YPLO6403
*/
public class ChatMessageDAO {
private final String mMsgId;
private final ContactId mContact;
private final String mChatId;
private final Message.Content.Status mStatus;
private final Message.GroupChatEvent.Status mChatEvent;
private final Message.Content.ReasonCode mReasonCode;
private final ReadStatus mReadStatus;
private final Direction mDirection;
private final String mMimeType;
private final String mContent;
private final long mTimestamp;
private final long mTimestampSent;
private final long mTimestampDelivered;
private final long mTimestampDisplayed;
private static ContentResolver sContentResolver;
private final boolean mExpiredDelivery;
public Message.Content.Status getStatus() {
return mStatus;
}
public Message.GroupChatEvent.Status getChatEvent() {
return mChatEvent;
}
public ReadStatus getReadStatus() {
return mReadStatus;
}
public long getTimestampSent() {
return mTimestampSent;
}
public long getTimestampDelivered() {
return mTimestampDelivered;
}
public long getTimestampDisplayed() {
return mTimestampDisplayed;
}
public ContactId getContact() {
return mContact;
}
public String getChatId() {
return mChatId;
}
public String getMimeType() {
return mMimeType;
}
public Direction getDirection() {
return mDirection;
}
public long getTimestamp() {
return mTimestamp;
}
public String getContent() {
return mContent;
}
public Message.Content.ReasonCode getReasonCode() {
return mReasonCode;
}
public boolean isExpiredDelivery() {
return mExpiredDelivery;
}
@Override
public String toString() {
return "ChatMessageDAO [msgId=" + mMsgId + ", contact=" + mContact + ", chatId=" + mChatId
+ ", direction=" + mDirection + ", mimeType=" + mMimeType + ", body='" + mContent
+ "']";
}
private ChatMessageDAO(String msgId, ContactId contact, String chatId, Status status,
GroupChatEvent.Status chatEvent, ReasonCode reasonCode, ReadStatus readStatus,
Direction direction, String mimeType, String content, long timestamp,
long timestampSent, long timestampDelivered, long timestampDisplayed,
boolean expiredDelivery) {
mMsgId = msgId;
mContact = contact;
mChatId = chatId;
mStatus = status;
mChatEvent = chatEvent;
mReasonCode = reasonCode;
mReadStatus = readStatus;
mDirection = direction;
mMimeType = mimeType;
mContent = content;
mTimestamp = timestamp;
mTimestampSent = timestampSent;
mTimestampDelivered = timestampDelivered;
mTimestampDisplayed = timestampDisplayed;
mExpiredDelivery = expiredDelivery;
}
/**
* Gets instance of chat message from RCS provider
*
* @param ctx the context
* @param msgId the message ID
* @return instance or null if entry not found
*/
public static ChatMessageDAO getChatMessageDAO(Context ctx, String msgId) {
if (sContentResolver == null) {
sContentResolver = ctx.getContentResolver();
}
Cursor cursor = null;
try {
cursor = sContentResolver.query(
Uri.withAppendedPath(ChatLog.Message.CONTENT_URI, msgId), null, null, null,
null);
if (cursor == null) {
throw new SQLException("Cannot query chat message ID=" + msgId);
}
if (!cursor.moveToFirst()) {
return null;
}
String chatId = cursor.getString(cursor.getColumnIndexOrThrow(ChatLog.Message.CHAT_ID));
String contact = cursor
.getString(cursor.getColumnIndexOrThrow(ChatLog.Message.CONTACT));
ContactId contactId = null;
if (contact != null) {
contactId = ContactUtil.formatContact(contact);
}
String mimeType = cursor.getString(cursor
.getColumnIndexOrThrow(ChatLog.Message.MIME_TYPE));
String content = null;
int status = cursor.getInt(cursor.getColumnIndexOrThrow(ChatLog.Message.STATUS));
GroupChatEvent.Status chatEvent = null;
ReasonCode reasonCode = null;
Message.Content.Status contentStatus;
if (Message.MimeType.GROUPCHAT_EVENT.equals(mimeType)) {
chatEvent = GroupChatEvent.Status.valueOf(status);
contentStatus = null;
} else {
content = cursor.getString(cursor.getColumnIndexOrThrow(ChatLog.Message.CONTENT));
contentStatus = Message.Content.Status.valueOf(status);
reasonCode = Message.Content.ReasonCode.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ChatLog.Message.REASON_CODE)));
}
ReadStatus readStatus = ReadStatus.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ChatLog.Message.READ_STATUS)));
Direction dir = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ChatLog.Message.DIRECTION)));
long timestamp = cursor
.getLong(cursor.getColumnIndexOrThrow(ChatLog.Message.TIMESTAMP));
long timestampSent = cursor.getLong(cursor
.getColumnIndexOrThrow(ChatLog.Message.TIMESTAMP_SENT));
long timestampDelivered = cursor.getLong(cursor
.getColumnIndexOrThrow(ChatLog.Message.TIMESTAMP_DELIVERED));
long timestampDisplayed = cursor.getLong(cursor
.getColumnIndexOrThrow(ChatLog.Message.TIMESTAMP_DISPLAYED));
boolean expiredDelivery = cursor.getInt(cursor
.getColumnIndexOrThrow(Message.EXPIRED_DELIVERY)) == 1;
return new ChatMessageDAO(msgId, contactId, chatId, contentStatus, chatEvent,
reasonCode, readStatus, dir, mimeType, content, timestamp, timestampSent,
timestampDelivered, timestampDisplayed, expiredDelivery);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/ChatMessageLogView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.contact.ContactId;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* A class to view the persisted information for chat message
* Created by Philippe LEMORDANT.
*/
public class ChatMessageLogView extends RcsActivity {
private static final String EXTRA_MESSAGE_ID = "id";
private String mMessageId;
private TextView mTxtViewChatId;
private TextView mTxtViewContact;
private TextView mTxtViewContent;
private TextView mTxtViewDate;
private TextView mTxtViewDir;
private TextView mTxtViewMime;
private TextView mTxtViewReason;
private TextView mTxtViewState;
private TextView mTxtViewDateSent;
private TextView mTxtViewDateDelivered;
private TextView mTxtViewDateDisplayed;
private TextView mTxtViewRead;
private TextView mTxtViewExpiredDelivery;
private static DateFormat sDateFormat;
private TextView mTxtViewId;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat_message_log_item);
mMessageId = getIntent().getStringExtra(EXTRA_MESSAGE_ID);
initialize();
}
private void initialize() {
mTxtViewId = (TextView) findViewById(R.id.history_log_item_id);
mTxtViewChatId = (TextView) findViewById(R.id.history_log_item_chat_id);
mTxtViewContact = (TextView) findViewById(R.id.history_log_item_contact);
mTxtViewState = (TextView) findViewById(R.id.history_log_item_state);
mTxtViewReason = (TextView) findViewById(R.id.history_log_item_reason);
mTxtViewDir = (TextView) findViewById(R.id.history_log_item_direction);
mTxtViewDate = (TextView) findViewById(R.id.history_log_item_date);
mTxtViewMime = (TextView) findViewById(R.id.history_log_item_mime);
mTxtViewContent = (TextView) findViewById(R.id.history_log_item_content);
mTxtViewDateSent = (TextView) findViewById(R.id.history_log_item_date_sent);
mTxtViewDateDelivered = (TextView) findViewById(R.id.history_log_item_date_delivered);
mTxtViewDateDisplayed = (TextView) findViewById(R.id.history_log_item_date_displayed);
mTxtViewRead = (TextView) findViewById(R.id.history_log_item_read_status);
mTxtViewExpiredDelivery = (TextView) findViewById(R.id.history_log_item_expired_delivery);
}
private String getDateFromDb(long timestamp) {
if (0 == timestamp) {
return "";
}
if (sDateFormat == null) {
sDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
}
return sDateFormat.format(new Date(timestamp));
}
@Override
protected void onResume() {
super.onResume();
ChatMessageDAO dao = ChatMessageDAO.getChatMessageDAO(this, mMessageId);
if (dao == null) {
showMessageThenExit(R.string.error_item_not_found);
return;
}
mTxtViewId.setText(mMessageId);
mTxtViewChatId.setText(dao.getChatId());
ContactId contact = dao.getContact();
if (contact != null) {
mTxtViewContact.setText(contact.toString());
} else {
mTxtViewContact.setText("");
}
String mime = dao.getMimeType();
if (ChatLog.Message.MimeType.GROUPCHAT_EVENT.equals(mime)) {
mTxtViewState.setText(RiApplication.sGroupChatEvents[dao.getChatEvent().toInt()]);
mTxtViewContent.setText("");
mTxtViewReason.setText("");
} else {
mTxtViewState.setText(RiApplication.sMessagesStatuses[dao.getStatus().toInt()]);
mTxtViewReason.setText(RiApplication.sMessageReasonCodes[dao.getReasonCode().toInt()]);
mTxtViewContent.setText(dao.getContent());
}
mTxtViewDir.setText(RiApplication.getDirection(dao.getDirection()));
mTxtViewDate.setText(getDateFromDb(dao.getTimestamp()));
mTxtViewDateSent.setText(getDateFromDb(dao.getTimestampSent()));
mTxtViewDateDelivered.setText(getDateFromDb(dao.getTimestampDelivered()));
mTxtViewDateDisplayed.setText(getDateFromDb(dao.getTimestampDisplayed()));
mTxtViewRead.setText(dao.getReadStatus().toString());
mTxtViewExpiredDelivery.setText(Boolean.toString(dao.isExpiredDelivery()));
mTxtViewMime.setText(mime);
}
/**
* Start activity to view details of chat message record
*
* @param context the context
* @param messageId the message ID
*/
public static void startActivity(Context context, String messageId) {
Intent intent = new Intent(context, ChatMessageLogView.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EXTRA_MESSAGE_ID, messageId);
context.startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/ChatPendingIntentManager.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import com.gsma.rcs.ri.RI;
import com.gsma.rcs.ri.utils.Utils;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.util.LruCache;
/**
* There should only have 1 pending intent for a given chat conversation.
* If the conversation is already on the foreground, the pending intent is useless and the activity
* is (re)started.
*
* @author Philippe LEMORDANT
*/
public class ChatPendingIntentManager {
private static volatile ChatPendingIntentManager sChatPendingIntentManager;
private final IForegroundInfo mForegroundInfo;
private static final int MAX_CHAT_HAVING_PENDING_MESSAGE = 10;
/*
* A cache of notification ID associated with each chat conversation having pending message. The
* key is the chat ID and the value is the notification ID.
*/
private final LruCache mPendingNotificationIdCache;
private final NotificationManager mNotifManager;
private final Context mCtx;
public interface IForegroundInfo {
boolean isConversationOnForeground(String chatId);
}
private ChatPendingIntentManager(Context ctx, IForegroundInfo foregroundInfo) {
mCtx = ctx;
mForegroundInfo = foregroundInfo;
mNotifManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
mPendingNotificationIdCache = new LruCache(MAX_CHAT_HAVING_PENDING_MESSAGE) {
@Override
protected void entryRemoved(boolean evicted, String key, Integer oldValue,
Integer newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
if (evicted) {
mNotifManager.cancel(oldValue);
}
}
};
}
/**
* Try to continue chat conversation
*
* @param continueChat the intent to continue the chat conversation
* @param chatId the chat ID
* @return the unique ID to be used to create the pending intent if chat conversation is not on
* foreground or null if continuing the conversation succeeded
*/
public Integer tryContinueChatConversation(Intent continueChat, String chatId) {
if (mForegroundInfo.isConversationOnForeground(chatId)) {
/*
* Do not display notification if activity is on foreground for this chatId
*/
Integer pendingIntentId = mPendingNotificationIdCache.get(chatId);
if (pendingIntentId != null) {
mPendingNotificationIdCache.remove(chatId);
mNotifManager.cancel(pendingIntentId);
}
/* This will trigger onNewIntent for the target activity */
mCtx.startActivity(continueChat);
return null;
}
Integer uniquePendingIntentId = mPendingNotificationIdCache.get(chatId);
if (uniquePendingIntentId == null) {
/*
* If the PendingIntent has the same operation, action, data, categories, components,
* and flags it will be replaced. Invitation should be notified individually so we use a
* random generator to provide a unique request code and reuse it for the notification.
*/
uniquePendingIntentId = Utils.getUniqueIdForPendingIntent();
mPendingNotificationIdCache.put(chatId, uniquePendingIntentId);
}
return uniquePendingIntentId;
}
public void postNotification(Integer id, Notification notification) {
mNotifManager.notify(id, notification);
}
public void clearNotification(String chatId) {
Integer pendingIntentId = mPendingNotificationIdCache.get(chatId);
if (pendingIntentId != null) {
mPendingNotificationIdCache.remove(chatId);
mNotifManager.cancel(pendingIntentId);
}
}
/**
* Gets Chat pending intent manager
*
* @param ctx the context
* @return the instance
*/
public static ChatPendingIntentManager getChatPendingIntentManager(Context ctx) {
/* "Double-Checked Locking" idiom for singleton creation */
if (sChatPendingIntentManager != null) {
return sChatPendingIntentManager;
}
synchronized (ChatPendingIntentManager.class) {
if (sChatPendingIntentManager == null) {
sChatPendingIntentManager = new ChatPendingIntentManager(
ctx.getApplicationContext(),
new ChatPendingIntentManager.IForegroundInfo() {
@Override
public boolean isConversationOnForeground(String chatId) {
return RI.sChatIdOnForeground != null
&& chatId.equals(RI.sChatIdOnForeground);
}
});
}
return sChatPendingIntentManager;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/ChatServiceConfigActivity.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.chat.ChatServiceConfiguration;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
import android.widget.TextView;
import java.util.Locale;
/**
* Display/update the chat service configuration
*
* @author Philippe LEMORDANT
*/
public class ChatServiceConfigActivity extends RcsActivity {
private ChatServiceConfiguration mConfig;
private CheckBox mRespondToDisplayReports;
private static final String LOGTAG = LogUtils.getTag(ChatServiceConfigActivity.class
.getSimpleName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.chat_service_config);
// Register to API connection manager
if (!isServiceConnected(RcsServiceName.CHAT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
try {
mConfig = getChatApi().getConfiguration();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return;
}
startMonitorServices(RcsServiceName.CHAT);
mRespondToDisplayReports = (CheckBox) findViewById(R.id.RespondToDisplayReports);
mRespondToDisplayReports.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Boolean enable = mRespondToDisplayReports.isChecked();
try {
mConfig.setRespondToDisplayReports(enable);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onClick RespondToDisplayReports ".concat(enable.toString()));
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
});
}
@Override
protected void onResume() {
super.onResume();
if (isExiting()) {
return;
}
try {
displayChatServiceConfig();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void displayChatServiceConfig() throws RcsServiceException {
Locale local = Locale.getDefault();
CheckBox checkBox = (CheckBox) findViewById(R.id.WarnSF);
checkBox.setChecked(mConfig.isChatWarnSF());
TextView textView = (TextView) findViewById(R.id.IsComposingTimeout);
textView.setText(String.format(local, "%d", mConfig.getIsComposingTimeout()));
textView = (TextView) findViewById(R.id.MinGroupChatParticipants);
textView.setText(String.format(local, "%d", mConfig.getGroupChatMinParticipants()));
textView = (TextView) findViewById(R.id.MaxGroupChatParticipants);
textView.setText(String.format(local, "%d", mConfig.getGroupChatMaxParticipants()));
textView = (TextView) findViewById(R.id.MaxMsgLengthGroupChat);
textView.setText(String.format(local, "%d", mConfig.getGroupChatMessageMaxLength()));
textView = (TextView) findViewById(R.id.GroupChatSubjectMaxLength);
textView.setText(String.format(local, "%d", mConfig.getGroupChatSubjectMaxLength()));
textView = (TextView) findViewById(R.id.MaxMsgLengthOneToOneChat);
textView.setText(String.format(local, "%d", mConfig.getOneToOneChatMessageMaxLength()));
checkBox = (CheckBox) findViewById(R.id.SmsFallback);
checkBox.setChecked(mConfig.isSmsFallback());
mRespondToDisplayReports.setChecked(mConfig.isRespondToDisplayReportsEnabled());
textView = (TextView) findViewById(R.id.MaxGeolocLabelLength);
textView.setText(String.format(local, "%d", mConfig.getGeolocLabelMaxLength()));
textView = (TextView) findViewById(R.id.GeolocExpireTime);
textView.setText(String.format(local, "%d", mConfig.getGeolocExpirationTime()));
checkBox = (CheckBox) findViewById(R.id.GroupChatSupported);
checkBox.setChecked(mConfig.isGroupChatSupported());
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/ISendFile.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import android.net.Uri;
/**
* @author Philippe LEMORDANT
*/
public interface ISendFile {
/**
* Initialize
*/
void initialize();
/**
* Transfer file
*
* @param file Uri of file to transfer
* @param disposition the file disposition
* @param fileIcon File icon option. If true, the stack tries to attach fileicon.
* @return True if file transfer is successful
*/
boolean transferFile(Uri file, FileTransfer.Disposition disposition, boolean fileIcon);
/**
* Add file transfer event listener
*
* @param fileTransferService the file transfer service
*/
void addFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException;
/**
* Remove file transfer event listener
*
* @param fileTransferService the file transfer service
*/
void removeFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException;
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/IsComposingManager.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import android.os.Handler;
import android.os.Message;
/**
* Utility class to handle is_typing timers (see RFC3994)
*/
public class IsComposingManager {
// Idle time out (in ms)
private long idleTimeOut = 0;
// Active state refresh interval (in ms)
private final static int ACTIVE_STATE_REFRESH = 60 * 1000;
// Clock handler
private ClockHandler handler = new ClockHandler();
// Is composing state
private boolean isComposing = false;
// Event IDs
private final static int IS_STARTING_COMPOSING = 1;
private final static int IS_STILL_COMPOSING = 2;
private final static int MESSAGE_WAS_SENT = 3;
private final static int ACTIVE_MESSAGE_NEEDS_REFRESH = 4;
private final static int IS_IDLE = 5;
private INotifyComposing mNotifyComposing;
/**
* Constructor
*
* @param timeout the composing timeout
* @param notifyComposing interface to notify isComposing status
*/
public IsComposingManager(long timeout, INotifyComposing notifyComposing) {
idleTimeOut = timeout;
mNotifyComposing = notifyComposing;
}
/**
* Interface to notify isComposing status
*/
public interface INotifyComposing {
/**
* @param status typing status
*/
void setTypingStatus(boolean status);
}
// Clock handler class
private class ClockHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case IS_STARTING_COMPOSING: {
// Send a typing status "active"
mNotifyComposing.setTypingStatus(true);
// In IDLE_TIME_OUT we will need to send a is-idle status
// message
handler.sendEmptyMessageDelayed(IS_IDLE, idleTimeOut);
// In ACTIVE_STATE_REFRESH we will need to send an active
// status message refresh
handler.sendEmptyMessageDelayed(ACTIVE_MESSAGE_NEEDS_REFRESH,
ACTIVE_STATE_REFRESH);
break;
}
case IS_STILL_COMPOSING: {
// Cancel the IS_IDLE messages in queue, if there was one
handler.removeMessages(IS_IDLE);
// In IDLE_TIME_OUT we will need to send a is-idle status
// message
handler.sendEmptyMessageDelayed(IS_IDLE, idleTimeOut);
break;
}
case MESSAGE_WAS_SENT: {
// We are now going to idle state
hasNoActivity();
// Cancel the IS_IDLE messages in queue, if there was one
handler.removeMessages(IS_IDLE);
// Cancel the ACTIVE_MESSAGE_NEEDS_REFRESH messages in
// queue, if there was one
handler.removeMessages(ACTIVE_MESSAGE_NEEDS_REFRESH);
break;
}
case ACTIVE_MESSAGE_NEEDS_REFRESH: {
// We have to refresh the "active" state
mNotifyComposing.setTypingStatus(true);
// In ACTIVE_STATE_REFRESH we will need to send an active
// status message refresh
handler.sendEmptyMessageDelayed(ACTIVE_MESSAGE_NEEDS_REFRESH,
ACTIVE_STATE_REFRESH);
break;
}
case IS_IDLE: {
// End of typing
hasNoActivity();
// Send a typing status "idle"
mNotifyComposing.setTypingStatus(false);
// Cancel the ACTIVE_MESSAGE_NEEDS_REFRESH messages in
// queue, if there was one
handler.removeMessages(ACTIVE_MESSAGE_NEEDS_REFRESH);
break;
}
}
}
}
/**
* Edit text has activity
*/
public void hasActivity() {
// We have activity on the edit text
if (!isComposing) {
// If we were not already in isComposing state
handler.sendEmptyMessage(IS_STARTING_COMPOSING);
isComposing = true;
} else {
// We already were composing
handler.sendEmptyMessage(IS_STILL_COMPOSING);
}
}
/**
* Edit text has no activity anymore
*/
public void hasNoActivity() {
isComposing = false;
}
/**
* The message was sent
*/
public void messageWasSent() {
handler.sendEmptyMessage(MESSAGE_WAS_SENT);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/SendFile.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.filetransfer.AudioMessageRecordActivity;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* Send file
*
* @author Philippe LEMORDANT
*/
public abstract class SendFile extends RcsActivity implements ISendFile {
private final static int RC_SELECT_IMAGE = 0;
private final static int RC_SELECT_AUDIO = 1;
private final static int RC_RECORD_AUDIO = 2;
/**
* UI handler
*/
protected final Handler mHandler = new Handler();
protected String mTransferId;
protected String mFilename;
private Uri mFile;
protected long mFilesize = -1;
protected FileTransfer mFileTransfer;
protected Button mResumeBtn;
protected Button mPauseBtn;
private Button mInviteBtn;
private Button mSelectBtn;
protected FileTransferService mFileTransferService;
private CheckBox mCheckThumNail;
private CheckBox mCheckAudio;
private static final String LOGTAG = LogUtils.getTag(SendFile.class.getSimpleName());
protected TextView mStatusView;
protected ProgressBar mProgressBar;
private TextView mUriEdit;
private TextView mSizeEdit;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.chat_send_file);
initialize();
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.CHAT, RcsServiceName.FILE_TRANSFER,
RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
} else {
startMonitorServices(RcsServiceName.CHAT, RcsServiceName.FILE_TRANSFER,
RcsServiceName.CONTACT);
mFileTransferService = getFileTransferApi();
try {
addFileTransferEventListener(mFileTransferService);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mFileTransferService != null && isServiceConnected(RcsServiceName.FILE_TRANSFER)) {
try {
removeFileTransferEventListener(mFileTransferService);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
private void initiateTransfer() {
transferFile(mFile, mCheckAudio.isChecked() ? FileTransfer.Disposition.RENDER
: FileTransfer.Disposition.ATTACH, mCheckThumNail.isChecked());
/* Hide buttons */
mInviteBtn.setVisibility(View.INVISIBLE);
mSelectBtn.setVisibility(View.INVISIBLE);
/* Disable checkboxes */
mCheckThumNail.setEnabled(false);
mCheckAudio.setEnabled(false);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case RC_SELECT_IMAGE:
case RC_SELECT_AUDIO:
if (data != null && data.getData() != null) {
displayFileInfo(data);
boolean imageFile = RC_SELECT_IMAGE == requestCode;
boolean audioFile = !imageFile;
mCheckThumNail.setChecked(imageFile);
mCheckThumNail.setEnabled(imageFile);
mCheckAudio.setEnabled(audioFile);
mCheckAudio.setChecked(audioFile);
}
break;
case RC_RECORD_AUDIO:
displayFileInfo(data);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Created audio file:" + mFile);
}
mInviteBtn.setEnabled(true);
mCheckAudio.setEnabled(true);
mCheckThumNail.setEnabled(false);
mCheckThumNail.setChecked(false);
break;
}
}
/**
* Display a alert dialog to select the kind of file to transfer
*/
private void selectDocument() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_select_file);
builder.setCancelable(true);
builder.setItems(R.array.select_filetotransfer, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (RC_SELECT_IMAGE == which) {
FileUtils.openFile(SendFile.this, "image/*", RC_SELECT_IMAGE);
return;
}
FileUtils.openFile(SendFile.this, "audio/*", RC_SELECT_AUDIO);
}
});
registerDialog(builder.show());
}
private void displayFileInfo(Intent data) {
mFile = data.getData();
/* Display the selected filename attribute */
mFilename = FileUtils.getFileName(this, mFile);
mFilesize = FileUtils.getFileSize(this, mFile);
mSizeEdit.setText(FileUtils.humanReadableByteCount(mFilesize, true));
mUriEdit.setText(mFilename);
mInviteBtn.setEnabled(true);
}
/**
* Show the transfer progress
*
* @param currentSize Current size transferred
* @param totalSize Total size to be transferred
*/
protected void updateProgressBar(long currentSize, long totalSize) {
mStatusView.setText(Utils.getProgressLabel(currentSize, totalSize));
double position = ((double) currentSize / (double) totalSize) * 100.0;
mProgressBar.setProgress((int) position);
}
private void quitSession() {
try {
if (mFileTransfer != null
&& RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) {
mFileTransfer.abortTransfer();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mFileTransfer = null;
/* Exit activity */
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
try {
if (KeyEvent.KEYCODE_BACK == keyCode) {
if (mFileTransfer == null
|| !RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) {
finish();
return true;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
/* Exit activity */
finish();
}
});
builder.setCancelable(true);
registerDialog(builder.show());
return true;
}
} catch (RcsServiceException e) {
showException(e);
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_initiate_ft, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.menu_record_audio);
item.setVisible(mFileTransfer == null);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_record_audio:
startActivityForResult(new Intent(this, AudioMessageRecordActivity.class),
RC_RECORD_AUDIO);
break;
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
@Override
public void initialize() {
mUriEdit = (TextView) findViewById(R.id.uri);
mUriEdit.setText("");
mSizeEdit = (TextView) findViewById(R.id.size);
mSizeEdit.setText("");
mStatusView = (TextView) findViewById(R.id.progress_status);
mStatusView.setText("");
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
OnClickListener btnInviteListener = new OnClickListener() {
public void onClick(View v) {
long warnSize = 0;
try {
warnSize = mFileTransferService.getConfiguration().getWarnSize();
} catch (RcsServiceException e) {
showException(e);
}
if (warnSize > 0 && mFilesize >= warnSize) {
// Display a warning message
AlertDialog.Builder builder = new AlertDialog.Builder(SendFile.this);
builder.setMessage(getString(R.string.label_sharing_warn_size,
FileUtils.humanReadableByteCount(mFilesize, true)));
builder.setCancelable(false);
builder.setPositiveButton(R.string.label_yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int position) {
initiateTransfer();
}
});
builder.setNegativeButton(R.string.label_no, null);
registerDialog(builder.show());
} else {
initiateTransfer();
}
}
};
mInviteBtn = (Button) findViewById(R.id.invite_btn);
mInviteBtn.setOnClickListener(btnInviteListener);
mInviteBtn.setEnabled(false);
OnClickListener btnSelectListener = new OnClickListener() {
public void onClick(View v) {
/**
* Display a alert dialog to select the kind of file to transfer
*/
selectDocument();
}
};
mSelectBtn = (Button) findViewById(R.id.select_btn);
mSelectBtn.setOnClickListener(btnSelectListener);
OnClickListener btnPauseListener = new OnClickListener() {
public void onClick(View v) {
try {
if (mFileTransfer.isAllowedToPauseTransfer()) {
mFileTransfer.pauseTransfer();
} else {
mPauseBtn.setEnabled(false);
showMessage(R.string.label_pause_ft_not_allowed);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mPauseBtn = (Button) findViewById(R.id.pause_btn);
mPauseBtn.setOnClickListener(btnPauseListener);
mPauseBtn.setEnabled(false);
OnClickListener btnResumeListener = new OnClickListener() {
public void onClick(View v) {
try {
if (mFileTransfer.isAllowedToResumeTransfer()) {
mFileTransfer.resumeTransfer();
} else {
mResumeBtn.setEnabled(false);
showMessage(R.string.label_resume_ft_not_allowed);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mResumeBtn = (Button) findViewById(R.id.resume_btn);
mResumeBtn.setOnClickListener(btnResumeListener);
mResumeBtn.setEnabled(false);
mCheckThumNail = (CheckBox) findViewById(R.id.ft_thumb);
mCheckThumNail.setEnabled(false);
mCheckAudio = (CheckBox) findViewById(R.id.send_audio_msg);
mCheckAudio.setEnabled(false);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/TestChatApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat;
import com.gsma.rcs.api.connection.utils.RcsListActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.chat.group.InitiateGroupChat;
import com.gsma.rcs.ri.messaging.chat.single.InitiateSingleChat;
import com.gsma.rcs.ri.messaging.geoloc.DisplayGeoloc;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.capability.CapabilitiesLog;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.contact.ContactId;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.HashSet;
import java.util.Set;
/**
* Chat API
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class TestChatApi extends RcsListActivity {
private static final String[] PROJECTION = new String[] {
CapabilitiesLog.CONTACT
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// @formatter:off
String[] items = {
getString(R.string.menu_initiate_chat),
getString(R.string.menu_initiate_group_chat),
getString(R.string.menu_chat_service_config),
getString(R.string.menu_showus_map),
};
// @formatter:on
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, InitiateSingleChat.class));
break;
case 1:
/* Check if Group chat initialization is allowed */
ChatService chatService = getChatApi();
try {
if (chatService.isAllowedToInitiateGroupChat()) {
startActivity(new Intent(this, InitiateGroupChat.class));
} else {
showMessage(R.string.label_NotAllowedToInitiateGroupChat);
}
} catch (RcsServiceNotAvailableException e) {
showMessage(R.string.label_service_not_available);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
break;
case 2:
startActivity(new Intent(this, ChatServiceConfigActivity.class));
break;
case 3:
Set contacts = new HashSet<>();
Cursor cursor = null;
try {
cursor = getContentResolver().query(CapabilitiesLog.CONTENT_URI, PROJECTION,
null, null, null);
if (cursor == null) {
showMessageThenExit(R.string.label_db_failed);
return;
}
if (!cursor.moveToFirst()) {
showMessage(getString(R.string.label_geoloc_not_found));
return;
}
int contactColumIdx = cursor.getColumnIndexOrThrow(CapabilitiesLog.CONTACT);
do {
String contact = cursor.getString(contactColumIdx);
contacts.add(ContactUtil.formatContact(contact));
} while (cursor.moveToNext());
DisplayGeoloc.showContactsOnMap(this, contacts);
} finally {
if (cursor != null) {
cursor.close();
}
}
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/group/GroupChatDAO.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.group;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.GroupChat;
import com.gsma.services.rcs.chat.GroupChat.ReasonCode;
import com.gsma.services.rcs.chat.GroupChat.State;
import com.gsma.services.rcs.contact.ContactId;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
/**
* Group CHAT Data Object
*
* @author YPLO6403
*/
public class GroupChatDAO {
private final String mChatId;
private final Direction mDirection;
private final ContactId mContact;
private final String mParticipants;
private final GroupChat.State mState;
private final String mSubject;
private final long mTimestamp;
private static ContentResolver sContentResolver;
private final GroupChat.ReasonCode mReasonCode;
public GroupChat.State getState() {
return mState;
}
public String getChatId() {
return mChatId;
}
public String getParticipants() {
return mParticipants;
}
public String getSubject() {
return mSubject;
}
public Direction getDirection() {
return mDirection;
}
public long getTimestamp() {
return mTimestamp;
}
public GroupChat.ReasonCode getReasonCode() {
return mReasonCode;
}
public ContactId getContact() {
return mContact;
}
private GroupChatDAO(String chatId, ContactId contact, Direction direction,
String participants, State state, String subject, long timestamp, ReasonCode reasonCode) {
mChatId = chatId;
mContact = contact;
mDirection = direction;
mParticipants = participants;
mState = state;
mSubject = subject;
mTimestamp = timestamp;
mReasonCode = reasonCode;
}
@Override
public String toString() {
return "GroupChatDAO [chatId=" + mChatId + ", direction=" + mDirection + ", state="
+ mState + ", subject=" + mSubject + ", participants=" + mParticipants + "]";
}
/**
* Gets instance of Group Chat from RCS provider
*
* @param ctx The context
* @param chatId The chat ID
* @return instance or null if entry not found
*/
public static GroupChatDAO getGroupChatDao(Context ctx, String chatId) {
if (sContentResolver == null) {
sContentResolver = ctx.getContentResolver();
}
Cursor cursor = null;
try {
cursor = sContentResolver.query(
Uri.withAppendedPath(ChatLog.GroupChat.CONTENT_URI, chatId), null, null, null,
null);
if (cursor == null) {
throw new SQLException("Cannot query group chat ID" + chatId);
}
if (!cursor.moveToFirst()) {
return null;
}
String subject = cursor.getString(cursor
.getColumnIndexOrThrow(ChatLog.GroupChat.SUBJECT));
State state = GroupChat.State.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ChatLog.GroupChat.STATE)));
Direction dir = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ChatLog.GroupChat.DIRECTION)));
long timestamp = cursor.getLong(cursor
.getColumnIndexOrThrow(ChatLog.GroupChat.TIMESTAMP));
String participants = cursor.getString(cursor
.getColumnIndexOrThrow(ChatLog.GroupChat.PARTICIPANTS));
ReasonCode reasonCode = GroupChat.ReasonCode.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ChatLog.GroupChat.REASON_CODE)));
String contact = cursor.getString(cursor
.getColumnIndexOrThrow(ChatLog.GroupChat.CONTACT));
ContactId contactId = null;
if (contact != null) {
contactId = ContactUtil.formatContact(contact);
}
return new GroupChatDAO(chatId, contactId, dir, participants, state, subject,
timestamp, reasonCode);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Checks if chatId is a group chat Id
*
* @param chatId the chat ID
* @param contact The contact
* @return True chatId is a group chat Id
*/
public static boolean isGroupChat(String chatId, ContactId contact) {
return (contact == null) || !chatId.equals(contact.toString());
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/group/GroupChatIntentService.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.group;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.GroupTalkView;
import com.gsma.rcs.ri.messaging.TalkList;
import com.gsma.rcs.ri.messaging.chat.ChatMessageDAO;
import com.gsma.rcs.ri.messaging.chat.ChatPendingIntentManager;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.GroupChat;
import com.gsma.services.rcs.chat.GroupChatIntent;
import com.gsma.services.rcs.contact.ContactId;
import android.app.IntentService;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.media.RingtoneManager;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
/**
* File transfer intent service
*
* @author Philippe LEMORDANT
*/
public class GroupChatIntentService extends IntentService {
private ChatPendingIntentManager mChatPendingIntentManager;
private static final String LOGTAG = LogUtils.getTag(GroupChatIntentService.class
.getSimpleName());
/**
* Creates an IntentService.
*/
public GroupChatIntentService() {
super("GroupChatIntentService");
}
@Override
public void onCreate() {
super.onCreate();
mChatPendingIntentManager = ChatPendingIntentManager.getChatPendingIntentManager(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// We want this service to stop running if forced stop
// so return not sticky.
return START_NOT_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
String action;
if ((action = intent.getAction()) == null) {
return;
}
if (GroupChatIntent.ACTION_NEW_GROUP_CHAT_MESSAGE.equals(action)) {
// Gets message ID from the incoming Intent
String messageId = intent.getStringExtra(GroupChatIntent.EXTRA_MESSAGE_ID);
if (messageId == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read message ID");
}
return;
}
String mimeType = intent.getStringExtra(GroupChatIntent.EXTRA_MIME_TYPE);
if (mimeType == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read message mime-type");
}
return;
}
handleNewGroupChatMessage(intent, messageId);
} else if (GroupChatIntent.ACTION_NEW_INVITATION.equals(action)) {
/* Gets chat ID from the incoming Intent */
String chatId = intent.getStringExtra(GroupChatIntent.EXTRA_CHAT_ID);
if (chatId != null) {
handleNewGroupChatInvitation(intent, chatId);
}
} else {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Unknown action ".concat(action));
}
}
}
private void handleNewGroupChatInvitation(Intent invitation, String chatId) {
/* Get Chat from provider */
GroupChatDAO groupChatDAO = GroupChatDAO.getGroupChatDao(this, chatId);
if (groupChatDAO == null) {
Log.e(LOGTAG, "Cannot find group chat with ID=".concat(chatId));
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Group chat invitation =".concat(groupChatDAO.toString()));
}
/* Check if it's a spam */
if (groupChatDAO.getReasonCode() == GroupChat.ReasonCode.REJECTED_SPAM) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Do nothing on a spam");
}
return;
}
forwardGCInvitation2UI(invitation, chatId, groupChatDAO);
TalkList.notifyNewConversationEvent(this, GroupChatIntent.ACTION_NEW_INVITATION);
}
private void handleNewGroupChatMessage(Intent newGroupChatMessage, String messageId) {
/* Get ChatMessage from provider */
ChatMessageDAO messageDAO = ChatMessageDAO.getChatMessageDAO(this, messageId);
if (messageDAO == null) {
Log.e(LOGTAG, "Cannot find group chat message with ID=".concat(messageId));
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Group chat message =".concat(messageDAO.toString()));
}
forwardGCMessage2UI(newGroupChatMessage, messageDAO);
TalkList.notifyNewConversationEvent(this, GroupChatIntent.ACTION_NEW_GROUP_CHAT_MESSAGE);
}
private void forwardGCMessage2UI(Intent newGroupChatMessage, ChatMessageDAO message) {
String chatId = message.getChatId();
Intent intent = GroupTalkView.forgeIntentNewMessage(this, newGroupChatMessage, chatId);
String content = message.getContent();
Integer uniqueId = mChatPendingIntentManager.tryContinueChatConversation(intent, chatId);
if (uniqueId != null) {
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
/* Create notification */
ContactId contact = message.getContact();
String mimeType = message.getMimeType();
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
String title = getString(R.string.title_recv_chat, displayName);
String msg;
if (ChatLog.Message.MimeType.GEOLOC_MESSAGE.equals(mimeType)) {
msg = getString(R.string.label_geoloc_msg);
} else if (ChatLog.Message.MimeType.TEXT_MESSAGE.equals(mimeType)) {
msg = content;
} else {
/* If the GC message does not convey user content then discards */
if (LogUtils.isActive) {
Log.w(LOGTAG, "Discard message of type '" + mimeType + "' for chatId " + chatId);
}
return;
}
Notification notif = buildNotification(contentIntent, title, msg);
/* Send notification */
mChatPendingIntentManager.postNotification(uniqueId, notif);
}
}
private void forwardGCInvitation2UI(Intent invitation, String chatId, GroupChatDAO groupChat) {
/* Create pending intent */
Intent intent = GroupTalkView.forgeIntentInvitation(this, invitation);
/*
* If the PendingIntent has the same operation, action, data, categories, components, and
* flags it will be replaced. Invitation should be notified individually so we use a random
* generator to provide a unique request code and reuse it for the notification.
*/
Integer uniqueId = mChatPendingIntentManager.tryContinueChatConversation(intent, chatId);
if (uniqueId != null) {
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
/* Create notification */
String title = getString(R.string.title_group_chat);
/* Try to retrieve display name of remote contact */
String displayName = RcsContactUtil.getInstance(this).getDisplayName(
groupChat.getContact());
if (displayName != null) {
title = getString(R.string.title_recv_group_chat, displayName);
}
String subject = groupChat.getSubject();
if (TextUtils.isEmpty(subject)) {
subject = "<" + getString(R.string.label_no_subject) + ">";
}
String msg = getString(R.string.label_subject_notif, subject);
Notification notif = buildNotification(contentIntent, title, msg);
/* Send notification */
mChatPendingIntentManager.postNotification(uniqueId, notif);
} else {
if (LogUtils.isActive) {
Log.w(LOGTAG, "Received invitation for an existing group chat conversation chatId="
+ chatId + "!");
}
}
}
private Notification buildNotification(PendingIntent invitation, String title, String message) {
// Create notification
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(invitation);
notif.setSmallIcon(R.drawable.ri_notif_chat_icon);
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(true);
notif.setOnlyAlertOnce(true);
notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
notif.setDefaults(Notification.DEFAULT_VIBRATE);
notif.setContentTitle(title);
notif.setContentText(message);
return notif.build();
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/group/GroupChatInvitationReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.group;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Group chat invitation receiver
*
* @author Jean-Marc AUFFRET
*/
public class GroupChatInvitationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, GroupChatIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/group/GroupChatMessageReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.group;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Group chat message receiver
*
* @author YPLO6403
*/
public class GroupChatMessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, GroupChatIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/group/InitiateGroupChat.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.group;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.GroupTalkView;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.contact.ContactService;
import com.gsma.services.rcs.contact.RcsContact;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Initiate group chat
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class InitiateGroupChat extends RcsActivity implements OnItemClickListener {
private ArrayList mParticipants;
private ListView mContactList;
private List mAllowedContactIds;
private Button mInviteBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.chat_initiate_group);
if (!getChatApi().isServiceConnected()) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
mContactList = (ListView) findViewById(R.id.contacts);
/* Check if Group chat initialization is allowed */
ContactService contactService = getContactApi();
ChatService chatService = getChatApi();
try {
Set rcsContacts = contactService.getRcsContacts();
mAllowedContactIds = new ArrayList<>();
List allowedContacts = new ArrayList<>();
for (RcsContact rcsContact : rcsContacts) {
ContactId contact = rcsContact.getContactId();
if (chatService.isAllowedToInitiateGroupChat(contact)) {
mAllowedContactIds.add(contact);
if (rcsContact.getDisplayName() != null) {
allowedContacts.add(rcsContact.getDisplayName() + " ("
+ rcsContact.getContactId() + ")");
} else {
allowedContacts.add(contact.toString());
}
}
}
if (allowedContacts.size() > 0) {
String[] contacts = allowedContacts.toArray(new String[allowedContacts.size()]);
ArrayAdapter adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_multiple_choice, contacts);
mContactList.setAdapter(adapter);
mContactList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mContactList.setOnItemClickListener(this);
for (int i = 0; i < mContactList.getCount(); i++) {
mContactList.setItemChecked(i, false);
}
// Set button callback
mInviteBtn = (Button) findViewById(R.id.invite_btn);
mInviteBtn.setOnClickListener(btnInviteListener);
mInviteBtn.setEnabled(false);
} else {
showMessage(R.string.label_no_participant_found);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
/**
* Invite button listener
*/
private OnClickListener btnInviteListener = new OnClickListener() {
public void onClick(View v) {
// Get subject
EditText subjectTxt = (EditText) findViewById(R.id.subject);
String subject = subjectTxt.getText().toString();
GroupTalkView.initiateGroupChat(InitiateGroupChat.this, subject, mParticipants);
// Exit activity
finish();
}
};
public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
/* Build list of participant numbers */
SparseBooleanArray checkedArray = mContactList.getCheckedItemPositions();
mParticipants = new ArrayList<>();
for (int i = 0; i < checkedArray.size(); i++) {
if (checkedArray.get(i)) {
mParticipants.add(mAllowedContactIds.get(i).toString());
}
}
/* Disable the invite button if no contact selected */
if (mParticipants.size() == 0) {
mInviteBtn.setEnabled(false);
} else {
mInviteBtn.setEnabled(true);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/group/SendGroupFile.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.group;
import static com.gsma.rcs.ri.utils.FileUtils.takePersistableContentUriPermission;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.messaging.chat.SendFile;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.GroupFileTransferListener;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfo;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.util.Set;
/**
* Send file to group
*
* @author jexa7410
* @author Philippe LEMORDANT
*/
public class SendGroupFile extends SendFile {
private final static String EXTRA_CHAT_ID = "chat_id";
private String mChatId;
private static final String LOGTAG = LogUtils.getTag(SendGroupFile.class.getSimpleName());
private GroupFileTransferListener mFileTransferListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mChatId = getIntent().getStringExtra(EXTRA_CHAT_ID);
}
/**
* Start SendGroupFile activity
*
* @param context The context
* @param chatId The chat ID
*/
public static void startActivity(Context context, String chatId) {
Intent intent = new Intent(context, SendGroupFile.class);
intent.putExtra(EXTRA_CHAT_ID, chatId);
context.startActivity(intent);
}
@Override
public boolean transferFile(Uri file, FileTransfer.Disposition disposition, boolean fileIcon) {
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "transferFile filename=" + mFilename + " size=" + mFilesize
+ " chatId=" + mChatId);
}
/* Only take persistable permission for content Uris */
takePersistableContentUriPermission(this, file);
/* Initiate transfer */
mFileTransfer = mFileTransferService.transferFileToGroupChat(mChatId, file,
disposition, fileIcon);
if (mFileTransfer != null) {
mTransferId = mFileTransfer.getTransferId();
return true;
}
Log.e(LOGTAG, "Cannot transfer file: not found");
return false;
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return false;
}
}
@Override
public void addFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
fileTransferService.addEventListener(mFileTransferListener);
}
@Override
public void removeFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
fileTransferService.removeEventListener(mFileTransferListener);
}
@Override
public void initialize() {
super.initialize();
mFileTransferListener = new GroupFileTransferListener() {
@Override
public void onDeliveryInfoChanged(String chatId, ContactId contact, String transferId,
GroupDeliveryInfo.Status status, GroupDeliveryInfo.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDeliveryInfoChanged chatId=" + chatId + " contact=" + contact
+ " trasnferId=" + transferId + " state=" + status + " reason="
+ reasonCode);
}
}
@Override
public void onProgressUpdate(String chatId, String transferId, final long currentSize,
final long totalSize) {
/* Discard event if not for current transferId */
if (mTransferId == null || !mTransferId.equals(transferId)) {
return;
}
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(String chatId, String transferId,
final FileTransfer.State state, final FileTransfer.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged chatId=" + chatId + " transferId=" + transferId
+ " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current transferId */
if (mTransferId == null || !mTransferId.equals(transferId)) {
return;
}
final String _reasonCode = RiApplication.sFileTransferReasonCodes[reasonCode
.toInt()];
final String _state = RiApplication.sFileTransferStates[state.toInt()];
mHandler.post(new Runnable() {
public void run() {
if (mFileTransfer != null) {
try {
mResumeBtn.setEnabled(mFileTransfer.isAllowedToResumeTransfer());
} catch (RcsServiceException e) {
mResumeBtn.setEnabled(false);
showException(e);
}
try {
mPauseBtn.setEnabled(mFileTransfer.isAllowedToPauseTransfer());
} catch (RcsServiceException e) {
mPauseBtn.setEnabled(false);
showException(e);
}
}
switch (state) {
case STARTED:
//$FALL-THROUGH$
case TRANSFERRED:
/* Display transfer state started */
mStatusView.setText(_state);
break;
case ABORTED:
showMessageThenExit(getString(R.string.label_transfer_aborted,
_reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_transfer_rejected,
_reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_transfer_failed,
_reasonCode));
break;
default:
mStatusView.setText(_state);
}
}
});
}
@Override
public void onDeleted(String chatId, Set transferIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted chatId=" + chatId + " transferIds=" + transferIds);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/single/InitiateSingleChat.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.single;
import com.gsma.rcs.api.connection.ConnectionManager;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.OneToOneTalkView;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.capability.Capabilities;
import com.gsma.services.rcs.capability.CapabilitiesListener;
import com.gsma.services.rcs.capability.CapabilityService;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.contact.ContactId;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;
import java.util.Collections;
import java.util.HashSet;
/**
* Initiate chat
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class InitiateSingleChat extends RcsActivity {
private static final String LOGTAG = LogUtils.getTag(InitiateSingleChat.class.getSimpleName());
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
private ChatService mChatService;
private CapabilityService mCapabilityService;
private Button mInviteBtn;
private CapabilitiesListener mCapabilitiesListener;
private Handler mHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.chat_initiate_single);
initialize();
if (!getChatApi().isServiceConnected()) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(ConnectionManager.RcsServiceName.CAPABILITY);
try {
getCapabilityApi().addCapabilitiesListener(mCapabilitiesListener);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (isServiceConnected(ConnectionManager.RcsServiceName.CAPABILITY)
&& mCapabilityService != null) {
try {
mCapabilityService.removeCapabilitiesListener(mCapabilitiesListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
private void initialize() {
mChatService = getChatApi();
mHandler = new Handler();
mCapabilityService = getCapabilityApi();
mCapabilitiesListener = new CapabilitiesListener() {
@Override
public void onCapabilitiesReceived(ContactId contact, Capabilities capabilities) {
if (contact.equals(getSelectedContact())
&& capabilities.hasCapabilities(Capabilities.CAPABILITY_IM)) {
mHandler.post(new Runnable() {
@Override
public void run() {
mInviteBtn.setEnabled(true);
}
});
}
}
};
OnClickListener btnInviteListener = new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(OneToOneTalkView.forgeIntentToOpenConversation(
InitiateSingleChat.this, getSelectedContact()));
finish();
}
};
mInviteBtn = (Button) findViewById(R.id.invite_btn);
mInviteBtn.setOnClickListener(btnInviteListener);
mInviteBtn.setEnabled(false);
/* Set contact selector */
AdapterView.OnItemSelectedListener mListenerContact = new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
try {
ContactId contact = getSelectedContact();
if (mChatService.getOneToOneChat(contact).isAllowedToSendMessage()) {
mInviteBtn.setEnabled(true);
return;
}
mInviteBtn.setEnabled(false);
mCapabilityService.requestContactCapabilities(new HashSet<>(Collections
.singletonList(contact)));
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onNothingSelected(AdapterView> parent) {
}
};
mSpinner = (Spinner) findViewById(R.id.contact);
mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this));
mSpinner.setOnItemSelectedListener(mListenerContact);
}
private ContactId getSelectedContact() {
/* get selected phone number */
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
return ContactUtil.formatContact(phoneNumber);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/single/SendSingleFile.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.single;
import static com.gsma.rcs.ri.utils.FileUtils.takePersistableContentUriPermission;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.messaging.chat.SendFile;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import java.util.Set;
/**
* Send file to contact
*
* @author jexa7410
* @author Philippe LEMORDANT
*/
public class SendSingleFile extends SendFile {
private final static String EXTRA_CONTACT = "contact";
private ContactId mContact;
private static final String LOGTAG = LogUtils.getTag(SendSingleFile.class.getSimpleName());
private OneToOneFileTransferListener mFileTransferListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContact = getIntent().getParcelableExtra(EXTRA_CONTACT);
}
/**
* Start SendFile activity
*
* @param context The context
* @param contact The contact ID
*/
public static void startActivity(Context context, ContactId contact) {
Intent intent = new Intent(context, SendSingleFile.class);
intent.putExtra(EXTRA_CONTACT, (Parcelable) contact);
context.startActivity(intent);
}
@Override
public boolean transferFile(Uri file, FileTransfer.Disposition dispo, boolean fileicon) {
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "transferFile filename=" + mFilename + " size=" + mFilesize);
}
/* Only take persistable permission for content Uris */
takePersistableContentUriPermission(this, file);
/* Initiate transfer */
mFileTransfer = mFileTransferService.transferFile(mContact, file, dispo, fileicon);
if (mFileTransfer != null) {
mTransferId = mFileTransfer.getTransferId();
return true;
}
Log.e(LOGTAG, "Cannot transfer file: not found");
return false;
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return false;
}
}
@Override
public void addFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
fileTransferService.addEventListener(mFileTransferListener);
}
@Override
public void removeFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
fileTransferService.removeEventListener(mFileTransferListener);
}
@Override
public void initialize() {
super.initialize();
mFileTransferListener = new OneToOneFileTransferListener() {
@Override
public void onProgressUpdate(ContactId contact, String transferId,
final long currentSize, final long totalSize) {
/* Discard event if not for current transferId */
if (mTransferId == null || !mTransferId.equals(transferId)) {
return;
}
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(ContactId contact, String transferId,
final FileTransfer.State state, final FileTransfer.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " transferId=" + transferId
+ " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current transferId */
if (mTransferId == null || !mTransferId.equals(transferId)) {
return;
}
final String _reasonCode = RiApplication.sFileTransferReasonCodes[reasonCode
.toInt()];
final String _state = RiApplication.sFileTransferStates[state.toInt()];
mHandler.post(new Runnable() {
public void run() {
if (mFileTransfer != null) {
try {
mResumeBtn.setEnabled(mFileTransfer.isAllowedToResumeTransfer());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
try {
mPauseBtn.setEnabled(mFileTransfer.isAllowedToPauseTransfer());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
switch (state) {
case STARTED:
//$FALL-THROUGH$
case TRANSFERRED:
/* Display transfer state started */
mStatusView.setText(_state);
break;
case ABORTED:
showMessageThenExit(getString(R.string.label_transfer_aborted,
_reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_transfer_rejected,
_reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_transfer_failed,
_reasonCode));
break;
default:
mStatusView.setText(_state);
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set transferIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " transferIds=" + transferIds);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/single/SingleChatIntentService.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.single;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.OneToOneTalkView;
import com.gsma.rcs.ri.messaging.TalkList;
import com.gsma.rcs.ri.messaging.chat.ChatMessageDAO;
import com.gsma.rcs.ri.messaging.chat.ChatPendingIntentManager;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.services.rcs.chat.ChatLog;
import com.gsma.services.rcs.chat.OneToOneChatIntent;
import com.gsma.services.rcs.contact.ContactId;
import android.app.IntentService;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.media.RingtoneManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.util.HashSet;
import java.util.Set;
/**
* Single chat intent service
*
* @author Philippe LEMORDANT
*/
public class SingleChatIntentService extends IntentService {
private static final String[] PROJ_UNDELIVERED_MSG = new String[] {
ChatLog.Message.MESSAGE_ID
};
private static final String SEL_UNDELIVERED_MESSAGES = ChatLog.Message.CHAT_ID + "=? AND "
+ ChatLog.Message.EXPIRED_DELIVERY + "='1'";
private ChatPendingIntentManager mChatPendingIntentManager;
private static final String LOGTAG = LogUtils.getTag(SingleChatIntentService.class
.getSimpleName());
/**
* Creates an IntentService.
*/
public SingleChatIntentService() {
super("SingleChatIntentService");
}
@Override
public void onCreate() {
super.onCreate();
mChatPendingIntentManager = ChatPendingIntentManager.getChatPendingIntentManager(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
/* We want this service to stop running if forced stop so return not sticky. */
return START_NOT_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
String action;
if ((action = intent.getAction()) == null) {
return;
}
String msgId = intent.getStringExtra(OneToOneChatIntent.EXTRA_MESSAGE_ID);
if (msgId == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read message ID");
}
return;
}
switch (action) {
case OneToOneChatIntent.ACTION_NEW_ONE_TO_ONE_CHAT_MESSAGE:
handleNewOneToOneChatMessage(intent, msgId);
break;
case OneToOneChatIntent.ACTION_MESSAGE_DELIVERY_EXPIRED:
handleUndeliveredMessage(intent, msgId);
break;
default:
Log.e(LOGTAG, "Unknown action ".concat(action));
break;
}
}
private void handleUndeliveredMessage(Intent intent, String msgId) {
ContactId contact = intent.getParcelableExtra(OneToOneChatIntent.EXTRA_CONTACT);
if (contact == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read contact for message ID=".concat(msgId));
}
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Undelivered message ID=" + msgId + " for contact " + contact);
}
forwardUndeliveredMessage2UI(intent, contact);
}
/**
* Handle new one to one chat message
*
* @param messageIntent intent with chat message
*/
private void handleNewOneToOneChatMessage(Intent messageIntent, String msgId) {
String mimeType = messageIntent.getStringExtra(OneToOneChatIntent.EXTRA_MIME_TYPE);
if (mimeType == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read message mime-type");
}
return;
}
/* Read message from provider */
ChatMessageDAO msgDAO = ChatMessageDAO.getChatMessageDAO(this, msgId);
if (msgDAO == null) {
Log.e(LOGTAG, "Cannot find group chat message with ID=".concat(msgId));
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "One to one chat message ".concat(msgDAO.toString()));
}
forwardSingleChatMessage2UI(messageIntent, msgDAO);
}
/**
* Forward one to one chat message to view activity
*
* @param messageIntent intent
* @param message the chat message DAO
*/
private void forwardSingleChatMessage2UI(Intent messageIntent, ChatMessageDAO message) {
ContactId contact = message.getContact();
String content = message.getContent();
Intent intent = OneToOneTalkView.forgeIntentOnStackEvent(this, contact, messageIntent);
Integer uniqueId = mChatPendingIntentManager.tryContinueChatConversation(intent,
message.getChatId());
if (uniqueId != null) {
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
String title = getString(R.string.title_recv_chat, displayName);
String mimeType = message.getMimeType();
String msg;
switch (mimeType) {
case ChatLog.Message.MimeType.GEOLOC_MESSAGE:
msg = getString(R.string.label_geoloc_msg);
break;
case ChatLog.Message.MimeType.TEXT_MESSAGE:
msg = content;
break;
default:
if (LogUtils.isActive) {
Log.e(LOGTAG, "Discard message type '".concat(mimeType));
}
return;
}
Notification notif = buildNotification(contentIntent, title, msg);
mChatPendingIntentManager.postNotification(uniqueId, notif);
TalkList.notifyNewConversationEvent(this,
OneToOneChatIntent.ACTION_NEW_ONE_TO_ONE_CHAT_MESSAGE);
}
}
private void forwardUndeliveredMessage2UI(Intent undeliveredMessageIntent, ContactId contact) {
Intent intent = OneToOneTalkView.forgeIntentOnStackEvent(this, contact,
undeliveredMessageIntent);
Integer uniqueId = mChatPendingIntentManager.tryContinueChatConversation(intent,
contact.toString());
if (uniqueId != null) {
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
String title = getString(R.string.title_undelivered_message);
String msg = getString(R.string.label_undelivered_message, displayName);
Notification notif = buildNotification(contentIntent, title, msg);
mChatPendingIntentManager.postNotification(uniqueId, notif);
}
}
/**
* Generate a notification
*
* @param invitation invitation
* @param title title
* @param message message
* @return the notification
*/
private Notification buildNotification(PendingIntent invitation, String title, String message) {
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(invitation);
notif.setSmallIcon(R.drawable.ri_notif_chat_icon);
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(true);
notif.setOnlyAlertOnce(true);
notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
notif.setDefaults(Notification.DEFAULT_VIBRATE);
notif.setContentTitle(title);
notif.setContentText(message);
return notif.build();
}
/**
* Get set of undelivered messages
*
* @param ctx The context
* @param contact The contact
* @return set of undelivered messages
*/
public static Set getUndelivered(Context ctx, ContactId contact) {
Set messageIds = new HashSet<>();
Cursor cursor = null;
try {
cursor = ctx.getContentResolver().query(ChatLog.Message.CONTENT_URI,
PROJ_UNDELIVERED_MSG, SEL_UNDELIVERED_MESSAGES, new String[] {
contact.toString()
}, null);
if (cursor == null) {
throw new SQLException("Cannot query undelivered message for contact=" + contact);
}
if (!cursor.moveToFirst()) {
return messageIds;
}
int messageIdColumnIdx = cursor.getColumnIndexOrThrow(ChatLog.Message.MESSAGE_ID);
do {
messageIds.add(cursor.getString(messageIdColumnIdx));
} while (cursor.moveToNext());
return messageIds;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/single/SingleChatInvitationReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.single;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* CHAT invitation receiver
*
* @author YPLO6403
*/
public class SingleChatInvitationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, SingleChatIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/chat/single/UndeliveredMessageReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.chat.single;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Undelivered message receiver
*
* @author YPLO6403
*/
public class UndeliveredMessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, SingleChatIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/AudioMediaPlayer.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.ri.utils.LogUtils;
import android.content.Context;
import android.media.MediaPlayer;
import android.net.Uri;
import android.util.Log;
import java.io.IOException;
/**
* AudioMediaPlayer class can be used to control playback of audio files and streams.
*
* @author Sandrine Lacharme on 12/01/2016.
*/
public class AudioMediaPlayer extends MediaPlayer {
private static final String LOGTAG = LogUtils.getTag(AudioRecorder.class.getSimpleName());
private final Uri mFile;
private final Context mCtx;
private final IAudioPlayerListener mListener;
/**
* Constructor
*/
public AudioMediaPlayer(Context ctx, Uri file, IAudioPlayerListener listener) {
super();
mCtx = ctx;
mFile = file;
mListener = listener;
}
/**
* Start playing an audio file
*/
public void startPlay() throws IOException {
setDataSource(mCtx, mFile);
prepareAsync();
setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
setVolume(1.0f, 1.0f);
mp.start();
}
});
setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.reset();
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCompletion");
}
mListener.onCompletion();
}
});
}
/**
* Stop playing an audio file and reset the peripheral
*/
public void stopPlay() {
stop();
reset();
if (LogUtils.isActive) {
Log.d(LOGTAG, "stopPlay");
}
}
/**
* Gets audio file duration
*
* @param ctx the context
* @param file the file Uri
* @return the duration (msec)
*/
public static long getDuration(Context ctx, Uri file) {
MediaPlayer mp = MediaPlayer.create(ctx, file);
if (mp == null) {
return -1;
}
int duration = mp.getDuration();
mp.release();
return duration;
}
/**
* Interface to notify AudioPlayer events
*/
public interface IAudioPlayerListener {
/**
* Called when record is played
*/
void onCompletion();
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/AudioMessageRecordActivity.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.api.connection.ConnectionManager;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.filetransfer.FileTransferServiceConfiguration;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.util.Locale;
/**
* @author Philippe LEMORDANT
* @author Sandrine LACHARME
*/
public class AudioMessageRecordActivity extends RcsActivity {
private static final String LOGTAG = LogUtils.getTag(AudioRecorder.class.getName());
private Button mBtnPlay;
private Button mBtnStop;
private Button mBtnRecord;
private TextView mTextUri;
private TextView mTextSize;
private TextView mTextDuration;
private boolean mRecording;
private boolean mPlaying;
private RcsActivity mThisActivity;
private AudioMediaPlayer mAudioPlayer;
private AudioRecorder mAudioRecorder;
private Uri mFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.audio_msg_record);
initialize();
startMonitorServices(ConnectionManager.RcsServiceName.FILE_TRANSFER);
/** Register to API manager */
if (!isServiceConnected(ConnectionManager.RcsServiceName.FILE_TRANSFER)) {
showMessageThenExit(R.string.label_service_not_available);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mAudioPlayer != null) {
mAudioPlayer.release();
}
if (mAudioRecorder != null) {
mAudioRecorder.stopRecord();
mAudioRecorder.release();
}
}
private boolean deleteAudioRecord(Uri file) {
File fileToDelete = new File(file.getPath());
return !fileToDelete.exists() || fileToDelete.delete();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode && mAudioRecorder != null) {
mAudioRecorder.stopRecord();
mFile = mAudioRecorder.getFile();
if (mFile != null) {
MediaScannerConnection.scanFile(mThisActivity, new String[] {
mFile.getPath()
}, null, null);
Intent in = new Intent();
in.setData(mFile);
setResult(Activity.RESULT_OK, in);
mThisActivity.finish();
}
}
return super.onKeyDown(keyCode, event);
}
private void displayAudioFileInfo() {
mTextUri.setText(FileUtils.getFileName(mThisActivity, mFile));
long duration = AudioMediaPlayer.getDuration(mThisActivity, mFile) / 1000L;
mTextDuration.setText(String.format(Locale.getDefault(), "%d", duration));
long size = FileUtils.getFileSize(mThisActivity, mFile);
mTextSize.setText(FileUtils.humanReadableByteCount(size, true));
if (LogUtils.isActive) {
Log.w(LOGTAG, "Audio recorded file='" + mFile + "' duration(sec)=" + duration
+ " size=" + size);
}
}
private void initialize() {
mThisActivity = this;
mBtnPlay = (Button) findViewById(R.id.buttonPlay);
mBtnStop = (Button) findViewById(R.id.buttonStop);
mBtnRecord = (Button) findViewById(R.id.buttonRecord);
mTextDuration = (TextView) findViewById(R.id.duration);
mTextUri = (TextView) findViewById(R.id.uri);
mTextSize = (TextView) findViewById(R.id.size);
mBtnStop.setEnabled(false);
mBtnPlay.setEnabled(false);
mBtnRecord.setEnabled(true);
AudioRecorder.IAudioMessageRecordListener listenerRecorder = new AudioRecorder.IAudioMessageRecordListener() {
@Override
public void onMaxDurationReached() {
mBtnStop.setEnabled(false);
mBtnRecord.setEnabled(true);
mBtnPlay.setEnabled(true);
mRecording = false;
mThisActivity.showMessage(R.string.max_audio_record_reached);
mFile = mAudioRecorder.getFile();
displayAudioFileInfo();
}
};
try {
FileTransferServiceConfiguration config = getFileTransferApi().getConfiguration();
mAudioRecorder = new AudioRecorder(config.getMaxAudioMessageDuration(),
listenerRecorder);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return;
}
mBtnRecord.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if (mFile != null) {
deleteAudioRecord(mFile);
}
mBtnStop.setEnabled(true);
mBtnPlay.setEnabled(false);
mBtnRecord.setEnabled(false);
mTextUri.setText("");
mTextDuration.setText("");
mTextSize.setText("");
mAudioRecorder.launchRecord();
mRecording = true;
mPlaying = false;
} catch (IOException e) {
mThisActivity.showExceptionThenExit(e);
}
}
});
mBtnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBtnStop.setEnabled(false);
mBtnRecord.setEnabled(true);
mBtnPlay.setEnabled(true);
if (mRecording) {
mRecording = false;
mAudioRecorder.stopRecord();
mFile = mAudioRecorder.getFile();
displayAudioFileInfo();
return;
}
if (mPlaying) {
mPlaying = false;
mAudioPlayer.stopPlay();
}
}
});
final AudioMediaPlayer.IAudioPlayerListener listenerPlayer = new AudioMediaPlayer.IAudioPlayerListener() {
@Override
public void onCompletion() {
mBtnStop.setEnabled(false);
mBtnRecord.setEnabled(true);
mBtnPlay.setEnabled(true);
mPlaying = false;
}
};
mBtnPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBtnStop.setEnabled(true);
mBtnRecord.setEnabled(false);
mBtnPlay.setEnabled(false);
mPlaying = true;
try {
mAudioPlayer = new AudioMediaPlayer(mThisActivity, mFile, listenerPlayer);
mAudioPlayer.startPlay();
} catch (IOException e) {
mThisActivity.showExceptionThenExit(e);
}
}
});
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/AudioRecorder.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.ri.utils.LogUtils;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.IOException;
public class AudioRecorder extends MediaRecorder {
private static final String LOGTAG = LogUtils.getTag(AudioRecorder.class.getSimpleName());
private static final String AUDIO_FILE_DIR = "/ri/audios";
private static final String AUDIO_FILE_EXTENSION = ".mp4";
private static final String AUDIO_FILE_PREFIX = "audio_";
private final long mMaxAudioDuration;
private final IAudioMessageRecordListener mListener;
private Uri mFile;
private MediaRecorder mCurrentRecord;
private boolean mStartRecording;
/**
* Constructor
*
* @param maxAudioDuration maximum duration
* @param listener callback
*/
public AudioRecorder(long maxAudioDuration, IAudioMessageRecordListener listener) {
mMaxAudioDuration = maxAudioDuration;
mListener = listener;
}
private MediaRecorder createMediaRecorder() {
MediaRecorder record = new MediaRecorder();
mFile = createAudioUri();
record.setAudioSource(AudioSource.VOICE_COMMUNICATION);
record.setOutputFormat(OutputFormat.DEFAULT);
record.setAudioEncoder(AudioEncoder.AMR_WB);
record.setOutputFile(mFile.getPath());
record.setMaxDuration((int) mMaxAudioDuration);
record.setOnInfoListener(new OnInfoListener() {
@Override
public void onInfo(MediaRecorder mr, int what, int extra) {
if (MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED == what) {
mStartRecording = false;
mr.stop();
mr.reset();
mListener.onMaxDurationReached();
}
}
});
return record;
}
/**
* Launches audio recording
*
* @throws IOException
*/
public void launchRecord() throws IOException {
mCurrentRecord = createMediaRecorder();
mCurrentRecord.prepare();
mCurrentRecord.start();
mStartRecording = true;
}
/**
* Stops audio recording
*/
public void stopRecord() {
if (mCurrentRecord != null) {
if (mStartRecording) {
mStartRecording = false;
mCurrentRecord.stop();
mCurrentRecord.reset();
}
}
}
/**
* Generates a random filename for audio file
*/
private Uri createAudioUri() {
File audio = new File(getAudioDirectory() + "/" + AUDIO_FILE_PREFIX
+ System.currentTimeMillis() + AUDIO_FILE_EXTENSION);
Uri result = Uri.fromFile(audio);
if (LogUtils.isActive) {
Log.w(LOGTAG, "Audio file Uri=".concat(result.toString()));
}
return result;
}
public Uri getFile() {
return mFile;
}
/**
* Interface to notify AudioMessageRecord events
*/
public interface IAudioMessageRecordListener {
/**
* Called when max duration is reached
*/
void onMaxDurationReached();
}
/**
* Get sent audio root directory
*
* @return Path of audio directory
*/
private String getAudioDirectory() {
File directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
.concat(AUDIO_FILE_DIR));
if (!directory.exists()) {
directory.mkdirs();
}
return directory.getPath();
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/FileTransferDAO.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.RcsService.ReadStatus;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
/**
* File transfer Data Object
*
* @author Philippe LEMORDANT
*/
public class FileTransferDAO implements Parcelable {
private final String mTransferId;
private final ContactId mContact;
private final Uri mFile;
private final String mFilename;
private final String mChatId;
private final String mMimeType;
private final FileTransfer.State mState;
private final ReadStatus mReadStatus;
private final Direction mDirection;
private final long mTimestamp;
private final long mTimestampSent;
private final long mTimestampDelivered;
private final long mTimestampDisplayed;
private final long mSizeTransferred;
private final long mSize;
private final Uri mThumbnail;
private final long mFileExpiration;
private final long mFileIconExpiration;
private final FileTransfer.ReasonCode mReasonCode;
private static ContentResolver sContentResolver;
private final FileTransfer.Disposition mDisposition;
private final String mIconMimeType;
private final boolean mExpiredDelivery;
public String getIconMimeType() {
return mIconMimeType;
}
public FileTransfer.State getState() {
return mState;
}
public ReadStatus getReadStatus() {
return mReadStatus;
}
public long getTimestampSent() {
return mTimestampSent;
}
public long getTimestampDelivered() {
return mTimestampDelivered;
}
public long getTimestampDisplayed() {
return mTimestampDisplayed;
}
public long getSizeTransferred() {
return mSizeTransferred;
}
public String getTransferId() {
return mTransferId;
}
public ContactId getContact() {
return mContact;
}
public Uri getFile() {
return mFile;
}
public String getFilename() {
return mFilename;
}
public String getChatId() {
return mChatId;
}
public String getMimeType() {
return mMimeType;
}
public Direction getDirection() {
return mDirection;
}
public long getTimestamp() {
return mTimestamp;
}
public long getSize() {
return mSize;
}
public Uri getThumbnail() {
return mThumbnail;
}
public FileTransfer.Disposition getDisposition() {
return mDisposition;
}
/**
* Returns the time when the file on the content server is no longer valid to download.
*
* @return time
*/
public long getFileExpiration() {
return mFileExpiration;
}
/**
* Returns the time when the file icon on the content server is no longer valid to download.
*
* @return time
*/
public long getFileIconExpiration() {
return mFileIconExpiration;
}
public FileTransfer.ReasonCode getReasonCode() {
return mReasonCode;
}
private FileTransferDAO(FileTransfer.Disposition disposition, String transferId,
ContactId contact, Uri file, String filename, String chatId, String mimeType,
FileTransfer.State state, ReadStatus readStatus, Direction direction, long timestamp,
long timestampSent, long timestampDelivered, long timestampDisplayed,
long sizeTransferred, long size, Uri thumbnail, long fileExpiration,
long fileIconExpiration, FileTransfer.ReasonCode reasonCode, boolean expiredDelivery,
String iconMimeType) {
mDisposition = disposition;
mTransferId = transferId;
mContact = contact;
mFile = file;
mFilename = filename;
mChatId = chatId;
mMimeType = mimeType;
mState = state;
mReadStatus = readStatus;
mDirection = direction;
mTimestamp = timestamp;
mTimestampSent = timestampSent;
mTimestampDelivered = timestampDelivered;
mTimestampDisplayed = timestampDisplayed;
mSizeTransferred = sizeTransferred;
mSize = size;
mThumbnail = thumbnail;
mFileExpiration = fileExpiration;
mFileIconExpiration = fileIconExpiration;
mReasonCode = reasonCode;
mExpiredDelivery = expiredDelivery;
mIconMimeType = iconMimeType;
}
/**
* Constructor
*
* @param source Parcelable source
*/
public FileTransferDAO(Parcel source) {
mTransferId = source.readString();
boolean containsContactId = source.readInt() != 0;
if (containsContactId) {
mContact = ContactId.CREATOR.createFromParcel(source);
} else {
mContact = null;
}
boolean containsFile = source.readInt() != 0;
if (containsFile) {
mFile = Uri.parse(source.readString());
} else {
mFile = null;
}
mFilename = source.readString();
mChatId = source.readString();
mMimeType = source.readString();
mState = FileTransfer.State.valueOf(source.readInt());
mReadStatus = ReadStatus.valueOf(source.readInt());
mDirection = Direction.valueOf(source.readInt());
mTimestamp = source.readLong();
mTimestampSent = source.readLong();
mTimestampDelivered = source.readLong();
mTimestampDisplayed = source.readLong();
mSizeTransferred = source.readLong();
mSize = source.readLong();
boolean containsThumbnail = source.readInt() != 0;
if (containsThumbnail) {
mThumbnail = Uri.parse(source.readString());
} else {
mThumbnail = null;
}
mReasonCode = FileTransfer.ReasonCode.valueOf(source.readInt());
mFileExpiration = source.readLong();
mFileIconExpiration = source.readLong();
mExpiredDelivery = source.readInt() == 1;
boolean containsIconMimeType = source.readInt() != 0;
if (containsIconMimeType) {
mIconMimeType = source.readString();
} else {
mIconMimeType = null;
}
mDisposition = FileTransfer.Disposition.valueOf(source.readInt());
}
@Override
public String toString() {
return "FileTransferDAO [ftId=" + mTransferId + ", contact=" + mContact + ", filename="
+ mFilename + ", chatId=" + mChatId + ", mimeType=" + mMimeType + ", state="
+ mState + ", size=" + mSize + ", expiration=" + mFileExpiration + ", thumbnail="
+ mThumbnail + ", iconExpiration=" + mFileIconExpiration + "]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mTransferId);
if (mContact != null) {
dest.writeInt(1);
mContact.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
if (mFile != null) {
dest.writeInt(1);
dest.writeString(mFile.toString());
} else {
dest.writeInt(0);
}
dest.writeString(mFilename);
dest.writeString(mChatId);
dest.writeString(mMimeType);
dest.writeInt(mState.toInt());
dest.writeInt(mReadStatus.toInt());
dest.writeInt(mDirection.toInt());
dest.writeLong(mTimestamp);
dest.writeLong(mTimestampSent);
dest.writeLong(mTimestampDelivered);
dest.writeLong(mTimestampDisplayed);
dest.writeLong(mSizeTransferred);
dest.writeLong(mSize);
if (mThumbnail != null) {
dest.writeInt(1);
dest.writeString(mThumbnail.toString());
} else {
dest.writeInt(0);
}
dest.writeInt(mReasonCode.toInt());
dest.writeLong(mFileExpiration);
dest.writeLong(mFileIconExpiration);
dest.writeInt(mExpiredDelivery ? 1 : 0);
if (mIconMimeType != null) {
dest.writeInt(1);
dest.writeString(mIconMimeType);
} else {
dest.writeInt(0);
}
dest.writeInt(mDisposition.toInt());
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public FileTransferDAO createFromParcel(Parcel in) {
return new FileTransferDAO(in);
}
@Override
public FileTransferDAO[] newArray(int size) {
return new FileTransferDAO[size];
}
};
/**
* Gets instance of File Transfer from RCS provider
*
* @param context the context
* @param fileTransferId the file tr ansfer ID
* @return instance or null if entry not found
*/
public static FileTransferDAO getFileTransferDAO(final Context context,
final String fileTransferId) {
if (sContentResolver == null) {
sContentResolver = context.getContentResolver();
}
Cursor cursor = null;
try {
cursor = sContentResolver.query(
Uri.withAppendedPath(FileTransferLog.CONTENT_URI, fileTransferId), null, null,
null, null);
if (cursor == null) {
throw new SQLException(
"Failed to find Filetransfer with ID: ".concat(fileTransferId));
}
if (!cursor.moveToFirst()) {
return null;
}
String chatId = cursor.getString(cursor.getColumnIndexOrThrow(FileTransferLog.CHAT_ID));
String number = cursor.getString(cursor.getColumnIndexOrThrow(FileTransferLog.CONTACT));
ContactId contact = null;
if (number != null) {
contact = ContactUtil.formatContact(number);
}
Uri file = Uri.parse(cursor.getString(cursor
.getColumnIndexOrThrow(FileTransferLog.FILE)));
String filename = cursor.getString(cursor
.getColumnIndexOrThrow(FileTransferLog.FILENAME));
String mimeType = cursor.getString(cursor
.getColumnIndexOrThrow(FileTransferLog.MIME_TYPE));
FileTransfer.State state = FileTransfer.State.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(FileTransferLog.STATE)));
ReadStatus readStatus = ReadStatus.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(FileTransferLog.READ_STATUS)));
Direction direction = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(FileTransferLog.DIRECTION)));
long timestamp = cursor
.getLong(cursor.getColumnIndexOrThrow(FileTransferLog.TIMESTAMP));
long timestampSent = cursor.getLong(cursor
.getColumnIndexOrThrow(FileTransferLog.TIMESTAMP_SENT));
long timestampDelivered = cursor.getLong(cursor
.getColumnIndexOrThrow(FileTransferLog.TIMESTAMP_DELIVERED));
long timestampDisplayed = cursor.getLong(cursor
.getColumnIndexOrThrow(FileTransferLog.TIMESTAMP_DISPLAYED));
long sizeTransferred = cursor.getLong(cursor
.getColumnIndexOrThrow(FileTransferLog.TRANSFERRED));
long size = cursor.getLong(cursor.getColumnIndexOrThrow(FileTransferLog.FILESIZE));
FileTransfer.Disposition disposition = FileTransfer.Disposition.valueOf(cursor
.getInt(cursor.getColumnIndexOrThrow(FileTransferLog.DISPOSITION)));
String fileIcon = cursor.getString(cursor
.getColumnIndexOrThrow(FileTransferLog.FILEICON));
Uri thumbnail = null;
if (fileIcon != null) {
thumbnail = Uri.parse(fileIcon);
}
FileTransfer.ReasonCode reasonCode = FileTransfer.ReasonCode.valueOf(cursor
.getInt(cursor.getColumnIndexOrThrow(FileTransferLog.REASON_CODE)));
long fileExpiration = cursor.getLong(cursor
.getColumnIndexOrThrow(FileTransferLog.FILE_EXPIRATION));
long fileIconExpiration = cursor.getLong(cursor
.getColumnIndexOrThrow(FileTransferLog.FILEICON_EXPIRATION));
boolean expiredDelivery = cursor.getInt(cursor
.getColumnIndexOrThrow(FileTransferLog.EXPIRED_DELIVERY)) == 1;
String iconMimeType = cursor.getString(cursor
.getColumnIndexOrThrow(FileTransferLog.FILEICON_MIME_TYPE));
return new FileTransferDAO(disposition, fileTransferId, contact, file, filename,
chatId, mimeType, state, readStatus, direction, timestamp, timestampSent,
timestampDelivered, timestampDisplayed, sizeTransferred, size, thumbnail,
fileExpiration, fileIconExpiration, reasonCode, expiredDelivery, iconMimeType);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
public boolean isOneToOne() {
return mContact != null && mContact.toString().equals(mChatId);
}
public boolean isExpiredDelivery() {
return mExpiredDelivery;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/FileTransferIntentService.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.messaging.OneToOneTalkView;
import com.gsma.rcs.ri.messaging.TalkList;
import com.gsma.rcs.ri.messaging.chat.ChatPendingIntentManager;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferIntent;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.media.RingtoneManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.util.HashSet;
import java.util.Set;
/**
* File transfer intent service
*
* @author Philippe LEMORDANT
*/
public class FileTransferIntentService extends IntentService {
private static final String LOGTAG = LogUtils.getTag(FileTransferIntentService.class.getName());
private final static String[] PROJ_UNDELIVERED_FT = new String[] {
FileTransferLog.FT_ID
};
private static final String SEL_UNDELIVERED_FTS = FileTransferLog.CHAT_ID + "=? AND "
+ FileTransferLog.EXPIRED_DELIVERY + "='1'";
/**
* Constructor
*/
public FileTransferIntentService() {
super("FileTransferIntentService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
/*
* We want this service to stop running if forced stop so return not sticky.
*/
return START_NOT_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
String action;
if ((action = intent.getAction()) == null) {
return;
}
String transferId = intent.getStringExtra(FileTransferIntent.EXTRA_TRANSFER_ID);
if (transferId == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read transfer ID");
}
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onHandleIntent file transfer with ID ".concat(transferId));
}
switch (action) {
case FileTransferIntent.ACTION_FILE_TRANSFER_DELIVERY_EXPIRED:
handleUndeliveredFileTransfer(intent, transferId);
break;
case FileTransferIntent.ACTION_NEW_INVITATION:
handleFileTransferInvitation(intent, transferId);
break;
case FileTransferIntent.ACTION_RESUME:
handleFileTransferResume(intent, transferId);
break;
default:
Log.e(LOGTAG, "Unknown action ".concat(action));
}
}
private void handleFileTransferResume(Intent intent, String transferId) {
FileTransferDAO ftDao = FileTransferDAO.getFileTransferDAO(this, transferId);
if (ftDao != null) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onHandleIntent file transfer resume with ID ".concat(transferId));
}
if (Direction.INCOMING == ftDao.getDirection()) {
startActivity(ReceiveFileTransfer.forgeResumeIntent(this, ftDao, intent));
} else {
startActivity(InitiateFileTransfer.forgeResumeIntent(this, ftDao, intent));
}
}
}
private void handleFileTransferInvitation(Intent intent, String transferId) {
FileTransferDAO ftDao = FileTransferDAO.getFileTransferDAO(this, transferId);
if (ftDao != null) {
if (FileTransfer.State.REJECTED == ftDao.getState()) {
Log.e(LOGTAG, "File transfer already rejected. Id=".concat(transferId));
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "File Transfer invitation filename=" + ftDao.getFilename() + " size="
+ ftDao.getSize());
}
forwardFileTransferInvitationToUi(intent, ftDao);
}
}
/**
* Forward file transfer invitation to UI
*
* @param invitation Intent invitation
* @param ftDao the file transfer data object
*/
private void forwardFileTransferInvitationToUi(Intent invitation, FileTransferDAO ftDao) {
ContactId contact = ftDao.getContact();
if (ftDao.getContact() == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "forwardFileTransferInvitationToUi failed: cannot parse contact");
}
return;
}
Intent intent = ReceiveFileTransfer.forgeInvitationIntent(this, ftDao, invitation);
/*
* If the PendingIntent has the same operation, action, data, categories, components, and
* flags it will be replaced. Invitation should be notified individually so we use a random
* generator to provide a unique request code and reuse it for the notification.
*/
int uniqueId = Utils.getUniqueIdForPendingIntent();
PendingIntent pi = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_ONE_SHOT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
String title = getString(R.string.title_recv_file_transfer);
String message = getString(R.string.label_from_args, displayName);
/* Send notification */
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = buildNotification(pi, title, message);
notificationManager.notify(uniqueId, notif);
TalkList.notifyNewConversationEvent(this, FileTransferIntent.ACTION_NEW_INVITATION);
}
private void handleUndeliveredFileTransfer(Intent intent, String transferId) {
ContactId contact = intent.getParcelableExtra(FileTransferIntent.EXTRA_CONTACT);
if (contact == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read contact for ftId=".concat(transferId));
}
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Undelivered file transfer ID=" + transferId + " for contact " + contact);
}
forwardUndeliveredFileTransferToUi(intent, contact);
}
private void forwardUndeliveredFileTransferToUi(Intent undeliveredIntent, ContactId contact) {
Intent intent = OneToOneTalkView.forgeIntentOnStackEvent(this, contact, undeliveredIntent);
ChatPendingIntentManager pendingIntentmanager = ChatPendingIntentManager
.getChatPendingIntentManager(this);
Integer uniqueId = pendingIntentmanager.tryContinueChatConversation(intent,
contact.toString());
if (uniqueId != null) {
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
String title = getString(R.string.title_undelivered_filetransfer);
String msg = getString(R.string.label_undelivered_filetransfer, displayName);
Notification notif = buildNotification(contentIntent, title, msg);
pendingIntentmanager.postNotification(uniqueId, notif);
}
}
/**
* Generate a notification
*
* @param pendingIntent pending intent
* @param title title
* @param message message
* @return the notification
*/
private Notification buildNotification(PendingIntent pendingIntent, String title, String message) {
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(pendingIntent);
notif.setSmallIcon(R.drawable.ri_notif_file_transfer_icon);
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(true);
notif.setOnlyAlertOnce(true);
notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
notif.setDefaults(Notification.DEFAULT_VIBRATE);
notif.setContentTitle(title);
notif.setContentText(message);
return notif.build();
}
/**
* Get set of undelivered file transfers
*
* @param ctx The context
* @param contact The contact
* @return set of undelivered file transfers
*/
public static Set getUndelivered(Context ctx, ContactId contact) {
Set ids = new HashSet<>();
Cursor cursor = null;
try {
cursor = ctx.getContentResolver().query(FileTransferLog.CONTENT_URI,
PROJ_UNDELIVERED_FT, SEL_UNDELIVERED_FTS, new String[] {
contact.toString()
}, null);
if (cursor == null) {
throw new SQLException("Cannot query undelivered file transfers for contact="
+ contact);
}
if (!cursor.moveToFirst()) {
return ids;
}
int idColumnIdx = cursor.getColumnIndexOrThrow(FileTransferLog.FT_ID);
do {
ids.add(cursor.getString(idColumnIdx));
} while (cursor.moveToNext());
return ids;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/FileTransferInvitationReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* File transfer invitation receiver
*
* @author YPLO6403
*/
public class FileTransferInvitationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, FileTransferIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/FileTransferLogView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.services.rcs.contact.ContactId;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* A class to view the persisted information for chat message
* Created by Philippe LEMORDANT.
*/
public class FileTransferLogView extends RcsActivity {
private static final String EXTRA_FT_ID = "id";
private String mFileTransferId;
private TextView mTxtViewChatId;
private TextView mTxtViewContact;
private TextView mTxtViewDate;
private TextView mTxtViewDir;
private TextView mTxtViewFileSize;
private TextView mTxtViewFilename;
private TextView mTxtViewMime;
private TextView mTxtViewReason;
private TextView mTxtViewState;
private TextView mTxtViewTransferred;
private TextView mTxtViewUri;
private TextView mTxtViewDateSent;
private TextView mTxtViewDateDelivered;
private TextView mTxtViewDateDisplayed;
private TextView mTxtViewRead;
private TextView mTxtViewExpiredDelivery;
private static DateFormat sDateFormat;
private TextView mTxtViewFileExpiration;
private TextView mTxtViewIconExpiration;
private TextView mTxtViewIconUri;
private TextView mTxtViewIconMime;
private TextView mTxtViewId;
private TextView mTxtViewDisposition;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.filetransfer_log_item);
initialize();
mFileTransferId = getIntent().getStringExtra(EXTRA_FT_ID);
}
private void initialize() {
mTxtViewId = (TextView) findViewById(R.id.history_log_item_id);
mTxtViewChatId = (TextView) findViewById(R.id.history_log_item_chat_id);
mTxtViewContact = (TextView) findViewById(R.id.history_log_item_contact);
mTxtViewState = (TextView) findViewById(R.id.history_log_item_state);
mTxtViewReason = (TextView) findViewById(R.id.history_log_item_reason);
mTxtViewDir = (TextView) findViewById(R.id.history_log_item_direction);
mTxtViewDate = (TextView) findViewById(R.id.history_log_item_date);
mTxtViewMime = (TextView) findViewById(R.id.history_log_item_mime);
mTxtViewFilename = (TextView) findViewById(R.id.history_log_item_filename);
mTxtViewFileSize = (TextView) findViewById(R.id.history_log_item_size);
mTxtViewUri = (TextView) findViewById(R.id.history_log_item_uri);
mTxtViewTransferred = (TextView) findViewById(R.id.history_log_item_transferred);
mTxtViewDateSent = (TextView) findViewById(R.id.history_log_item_date_sent);
mTxtViewDateDelivered = (TextView) findViewById(R.id.history_log_item_date_delivered);
mTxtViewDateDisplayed = (TextView) findViewById(R.id.history_log_item_date_displayed);
mTxtViewRead = (TextView) findViewById(R.id.history_log_item_read_status);
mTxtViewExpiredDelivery = (TextView) findViewById(R.id.history_log_item_expired_delivery);
mTxtViewFileExpiration = (TextView) findViewById(R.id.history_log_item_date_file_expiration);
mTxtViewIconExpiration = (TextView) findViewById(R.id.history_log_item_date_icon_expiration);
mTxtViewIconUri = (TextView) findViewById(R.id.history_log_item_icon);
mTxtViewIconMime = (TextView) findViewById(R.id.history_log_item_icon_mime);
mTxtViewDisposition = (TextView) findViewById(R.id.history_log_item_disposition);
}
private String getDateFromDb(long timestamp) {
if (0 == timestamp) {
return "";
}
if (sDateFormat == null) {
sDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
}
return sDateFormat.format(new Date(timestamp));
}
@Override
protected void onResume() {
super.onResume();
FileTransferDAO dao = FileTransferDAO.getFileTransferDAO(this, mFileTransferId);
if (dao == null) {
showMessageThenExit(R.string.error_item_not_found);
return;
}
mTxtViewId.setText(mFileTransferId);
mTxtViewChatId.setText(dao.getChatId());
ContactId contact = dao.getContact();
if (contact != null) {
mTxtViewContact.setText(contact.toString());
} else {
mTxtViewContact.setText("");
}
mTxtViewState.setText(RiApplication.sFileTransferStates[dao.getState().toInt()]);
mTxtViewReason.setText(RiApplication.sFileTransferReasonCodes[dao.getReasonCode().toInt()]);
mTxtViewDir.setText(RiApplication.getDirection(dao.getDirection()));
mTxtViewDate.setText(getDateFromDb(dao.getTimestamp()));
mTxtViewDateSent.setText(getDateFromDb(dao.getTimestampSent()));
mTxtViewDateDelivered.setText(getDateFromDb(dao.getTimestampDelivered()));
mTxtViewDateDisplayed.setText(getDateFromDb(dao.getTimestampDisplayed()));
mTxtViewRead.setText(dao.getReadStatus().toString());
mTxtViewExpiredDelivery.setText(Boolean.toString(dao.isExpiredDelivery()));
mTxtViewMime.setText(dao.getMimeType());
mTxtViewFilename.setText(dao.getFilename());
mTxtViewFileSize.setText(String.valueOf(dao.getSize()));
mTxtViewUri.setText(dao.getFile().toString());
mTxtViewTransferred.setText(String.valueOf(dao.getSizeTransferred()));
mTxtViewFileExpiration.setText(getDateFromDb(dao.getFileExpiration()));
mTxtViewIconExpiration.setText(getDateFromDb(dao.getFileIconExpiration()));
mTxtViewIconUri.setText(dao.getThumbnail() == null ? "" : dao.getThumbnail().toString());
mTxtViewIconMime
.setText(dao.getIconMimeType() == null ? "" : dao.getThumbnail().toString());
mTxtViewDisposition.setText(dao.getDisposition().toString());
}
/**
* Start activity to view details of file transfer record
*
* @param context the context
* @param fileTransferId the file transfer ID
*/
public static void startActivity(Context context, String fileTransferId) {
Intent intent = new Intent(context, FileTransferLogView.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EXTRA_FT_ID, fileTransferId);
context.startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/FileTransferResumeReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* File transfer resume receiver
*
* @author YPLO6403
*/
public class FileTransferResumeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, FileTransferIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/FileTransferServiceConfigActivity.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.filetransfer.FileTransferServiceConfiguration;
import com.gsma.services.rcs.filetransfer.FileTransferServiceConfiguration.ImageResizeOption;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TableRow;
import android.widget.TextView;
import java.util.Locale;
/**
* Display/update the chat service configuration
*
* @author Philippe LEMORDANT
*/
public class FileTransferServiceConfigActivity extends RcsActivity {
private FileTransferServiceConfiguration mConfig;
private Spinner mSpinnerImageResizeOption;
private CheckBox mCheckBoxIsAutoAccept;
private CheckBox mCheckBoxIsAutoAcceptInRoaming;
private static final String LOGTAG = LogUtils.getTag(FileTransferServiceConfigActivity.class
.getSimpleName());
private final static String[] ImageResizeOptionTab = new String[] {
ImageResizeOption.ALWAYS_RESIZE.toString(), ImageResizeOption.ALWAYS_ASK.toString(),
ImageResizeOption.NEVER_RESIZE.toString()
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.filetransfer_service_config);
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.FILE_TRANSFER)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
try {
mConfig = getFileTransferApi().getConfiguration();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return;
}
startMonitorServices(RcsServiceName.FILE_TRANSFER);
mSpinnerImageResizeOption = (Spinner) findViewById(R.id.ft_ImageResizeOption);
ArrayAdapter dataAdapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, ImageResizeOptionTab);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinnerImageResizeOption.setAdapter(dataAdapter);
mSpinnerImageResizeOption.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parentView, View selectedItemView,
int position, long id) {
ImageResizeOption newOption = ImageResizeOption.valueOf(mSpinnerImageResizeOption
.getSelectedItemPosition());
try {
ImageResizeOption oldOption = mConfig.getImageResizeOption();
if (!oldOption.equals(newOption)) {
mConfig.setImageResizeOption(newOption);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onClick ImageResizeOption".concat(newOption.toString()));
}
}
} catch (RcsServiceException e) {
showException(e);
}
}
@Override
public void onNothingSelected(AdapterView> parentView) {
}
});
mCheckBoxIsAutoAccept = (CheckBox) findViewById(R.id.ft_isAutoAccept);
mCheckBoxIsAutoAccept.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Boolean autoAccept = mCheckBoxIsAutoAccept.isChecked();
try {
mConfig.setAutoAccept(autoAccept);
TableRow tableRow = (TableRow) findViewById(R.id.isAutoAcceptInRoaming);
if (autoAccept) {
tableRow.setVisibility(View.VISIBLE);
mCheckBoxIsAutoAcceptInRoaming.setChecked(mConfig
.isAutoAcceptInRoamingEnabled());
} else {
tableRow.setVisibility(View.GONE);
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onClick isAutoAccept ".concat(autoAccept.toString()));
}
} catch (RcsServiceException e) {
showException(e);
}
}
});
mCheckBoxIsAutoAcceptInRoaming = (CheckBox) findViewById(R.id.ft_isAutoAcceptInRoaming);
mCheckBoxIsAutoAcceptInRoaming.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Boolean autoAcceptInRoaming = mCheckBoxIsAutoAcceptInRoaming.isChecked();
try {
mConfig.setAutoAcceptInRoaming(autoAcceptInRoaming);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onClick isAutoAcceptInRoaming ".concat(autoAcceptInRoaming
.toString()));
}
} catch (RcsServiceException e) {
showException(e);
}
}
});
}
@Override
protected void onResume() {
super.onResume();
if (isExiting()) {
return;
}
try {
displayFileTransferServiceConfig();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void displayFileTransferServiceConfig() throws RcsServiceException {
Locale local = Locale.getDefault();
TextView textView = (TextView) findViewById(R.id.ft_WarnSize);
textView.setText(String.format(local, "%d", mConfig.getWarnSize()));
textView = (TextView) findViewById(R.id.ft_MaxSize);
textView.setText(String.format(local, "%d", mConfig.getMaxSize()));
textView = (TextView) findViewById(R.id.MaxAudioDuration);
textView.setText(String.format(local, "%d", mConfig.getMaxAudioMessageDuration()));
if (mConfig.isAutoAcceptModeChangeable()) {
TableRow tableRow = (TableRow) findViewById(R.id.isAutoAccept);
tableRow.setVisibility(View.VISIBLE);
boolean autoAcceptEnabled = mConfig.isAutoAcceptEnabled();
mCheckBoxIsAutoAccept.setChecked(autoAcceptEnabled);
tableRow = (TableRow) findViewById(R.id.isAutoAcceptInRoaming);
if (autoAcceptEnabled) {
tableRow.setVisibility(View.VISIBLE);
mCheckBoxIsAutoAcceptInRoaming.setChecked(mConfig.isAutoAcceptInRoamingEnabled());
} else {
tableRow.setVisibility(View.GONE);
}
} else {
TableRow tableRow = (TableRow) findViewById(R.id.isAutoAccept);
tableRow.setVisibility(View.GONE);
tableRow = (TableRow) findViewById(R.id.isAutoAcceptInRoaming);
tableRow.setVisibility(View.GONE);
}
textView = (TextView) findViewById(R.id.MaxFileTransfers);
textView.setText(String.format(local, "%d", mConfig.getMaxFileTransfers()));
CheckBox checkBox = (CheckBox) findViewById(R.id.GroupFileTransferSupported); // TODO
checkBox.setChecked(true);
mSpinnerImageResizeOption.setSelection(mConfig.getImageResizeOption().toInt());
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/InitiateFileTransfer.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import static com.gsma.rcs.ri.utils.FileUtils.takePersistableContentUriPermission;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferIntent;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TableRow;
import android.widget.TextView;
import java.util.Set;
/**
* Initiate file transfer
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class InitiateFileTransfer extends RcsActivity {
private final static int RC_SELECT_IMAGE = 0;
private final static int RC_SELECT_TEXT_FILE = 1;
private final static int RC_SELECT_AUDIO = 2;
private final static int RC_RECORD_AUDIO = 3;
private static final String BUNDLE_FTDAO_ID = "ftdao";
private static final String LOGTAG = LogUtils.getTag(InitiateFileTransfer.class.getName());
/**
* UI handler
*/
private final Handler mHandler = new Handler();
private String mFilename;
private Uri mFile;
private long mFilesize = -1;
private FileTransfer mFileTransfer;
private String mFileTransferId;
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
private Button mResumeBtn;
private Button mPauseBtn;
private Button mInviteBtn;
private Button mSelectBtn;
private OneToOneFileTransferListener mFileTransferListener;
private FileTransferService mFileTransferService;
private TextView mUriTextView;
private TextView mSizeTextView;
private CheckBox mIconCheckBox;
private CheckBox mAudioMessageCheckBox;
private TextView mStatusView;
private ProgressBar mProgressBar;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
boolean resuming = FileTransferIntent.ACTION_RESUME.equals(intent.getAction());
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.filetransfer_initiate);
intitialize();
if (!isServiceConnected(RcsServiceName.FILE_TRANSFER)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.FILE_TRANSFER);
mFileTransferService = getFileTransferApi();
try {
mFileTransferService.addEventListener(mFileTransferListener);
if (resuming) {
/* Get resuming info */
FileTransferDAO ftdao = (FileTransferDAO) (intent.getExtras()
.getSerializable(BUNDLE_FTDAO_ID));
if (ftdao == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "onCreate cannot read File Transfer resuming info");
}
finish();
return;
}
ContactId remoteContact = ftdao.getContact();
mFileTransferId = ftdao.getTransferId();
mFilename = ftdao.getFilename();
mFilesize = ftdao.getSize();
ArrayAdapter adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, new String[] {
remoteContact.toString()
});
mSpinner.setAdapter(adapter);
mSizeTextView.setText(FileUtils.humanReadableByteCount(mFilesize, true));
mUriTextView.setText(mFilename);
/* Check if session still exists */
if (mFileTransferService.getFileTransfer(mFileTransferId) == null) {
/* Session not found or expired */
showMessageThenExit(R.string.label_transfer_session_has_expired);
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate (file=" + mFilename + ") (size=" + mFilesize
+ ") (contact=" + remoteContact + ")");
}
} else {
mSpinner.setAdapter(ContactListAdapter.createRcsContactListAdapter(this));
/* Enable button if contact available */
if (mSpinner.getAdapter().getCount() != 0) {
mSelectBtn.setEnabled(true);
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate");
}
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onDestroy() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDestroy");
}
super.onDestroy();
if (mFileTransferService != null && isServiceConnected(RcsServiceName.FILE_TRANSFER)) {
// Remove file transfer listener
try {
mFileTransferService.removeEventListener(mFileTransferListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
private void initiateTransfer(ContactId remote) {
/* Get thumbnail option */
boolean tryToSendFileicon = mIconCheckBox.isChecked();
String mimeType = getContentResolver().getType(mFile);
if (tryToSendFileicon && mimeType != null && !mimeType.startsWith("image")) {
tryToSendFileicon = false;
}
try {
FileTransfer.Disposition dispo = (mAudioMessageCheckBox.isChecked()) ? FileTransfer.Disposition.RENDER
: FileTransfer.Disposition.ATTACH;
/* Only take persistable permission for content Uris */
takePersistableContentUriPermission(this, mFile);
/* Initiate transfer */
mFileTransfer = mFileTransferService.transferFile(remote, mFile, dispo,
tryToSendFileicon);
if (mFileTransfer != null) {
mFileTransferId = mFileTransfer.getTransferId();
} else {
Log.e(LOGTAG, "Cannot initiate transfer: ID not found");
return;
}
/* Disable UI */
mSpinner.setEnabled(false);
/* Hide buttons */
mInviteBtn.setVisibility(View.INVISIBLE);
mSelectBtn.setVisibility(View.INVISIBLE);
mIconCheckBox.setEnabled(false);
mAudioMessageCheckBox.setEnabled(false);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
/**
* Display a alert dialog to select the kind of file to transfer
*/
private void selectDocument() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_select_file);
builder.setCancelable(true);
builder.setItems(R.array.select_filetotransfer, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (RC_SELECT_IMAGE == which) {
FileUtils.openFile(InitiateFileTransfer.this, "image/*", RC_SELECT_IMAGE);
return;
}
if (RC_SELECT_TEXT_FILE == which) {
FileUtils
.openFile(InitiateFileTransfer.this, "text/plain", RC_SELECT_TEXT_FILE);
return;
}
FileUtils.openFile(InitiateFileTransfer.this, "audio/*", RC_SELECT_AUDIO);
}
});
registerDialog(builder.show());
}
private void displaySelectedFileInfo() {
/*
* Display file info and the selected filename attribute.
*/
mFilename = FileUtils.getFileName(this, mFile);
mFilesize = FileUtils.getFileSize(this, mFile);
mSizeTextView.setText(FileUtils.humanReadableByteCount(mFilesize, true));
mUriTextView.setText(mFilename);
if (LogUtils.isActive) {
Log.i(LOGTAG, "Select file " + mFilename + " of size " + mFilesize);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK || data == null) {
return;
}
switch (requestCode) {
case RC_SELECT_IMAGE:
case RC_SELECT_TEXT_FILE:
case RC_SELECT_AUDIO:
if (data.getData() == null) {
return;
}
mFile = data.getData();
if (LogUtils.isActive) {
Log.d(LOGTAG, "Selected file uri:".concat(mFile.toString()));
}
displaySelectedFileInfo();
mInviteBtn.setEnabled(true);
if (RC_SELECT_AUDIO == requestCode) {
mAudioMessageCheckBox.setEnabled(true);
mIconCheckBox.setEnabled(false);
mIconCheckBox.setChecked(false);
} else {
mIconCheckBox.setEnabled(true);
mAudioMessageCheckBox.setChecked(false);
mAudioMessageCheckBox.setEnabled(false);
}
break;
case RC_RECORD_AUDIO:
mFile = data.getData();
if (LogUtils.isActive) {
Log.d(LOGTAG, "Created audio file:" + mFile);
}
displaySelectedFileInfo();
mInviteBtn.setEnabled(true);
mAudioMessageCheckBox.setEnabled(true);
mIconCheckBox.setEnabled(false);
mIconCheckBox.setChecked(false);
break;
}
}
private void updateProgressBar(long currentSize, long totalSize) {
mStatusView.setText(Utils.getProgressLabel(currentSize, totalSize));
double position = ((double) currentSize / (double) totalSize) * 100.0;
mProgressBar.setProgress((int) position);
}
private void quitSession() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "quitSession");
}
try {
if (mFileTransfer != null
&& RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) {
mFileTransfer.abortTransfer();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mFileTransfer = null;
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
try {
if (KeyEvent.KEYCODE_BACK == keyCode) {
if (mFileTransfer == null
|| !RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) {
finish();
return true;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
/* Exit activity */
finish();
}
});
builder.setCancelable(true);
registerDialog(builder.show());
return true;
}
} catch (RcsServiceException e) {
showException(e);
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_initiate_ft, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.menu_record_audio);
item.setVisible(mFileTransfer == null);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_record_audio:
startActivityForResult(new Intent(this, AudioMessageRecordActivity.class),
RC_RECORD_AUDIO);
break;
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void displayFileExpiration(long fileExpiration) {
if (FileTransferLog.UNKNOWN_EXPIRATION == fileExpiration) {
return;
}
TableRow expirationTableRow = (TableRow) findViewById(R.id.expiration);
expirationTableRow.setVisibility(View.VISIBLE);
TextView expirationView = (TextView) findViewById(R.id.value_expiration);
CharSequence expirationDate = DateUtils.getRelativeTimeSpanString(fileExpiration,
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE);
expirationView.setText(expirationDate);
}
private void intitialize() {
OnClickListener btnInviteListener = new OnClickListener() {
public void onClick(View v) {
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
final ContactId remote = ContactUtil.formatContact(phoneNumber);
long warnSize;
try {
warnSize = mFileTransferService.getConfiguration().getWarnSize();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return;
}
if ((warnSize > 0) && (mFilesize >= warnSize)) {
// Display a warning message
AlertDialog.Builder builder = new AlertDialog.Builder(InitiateFileTransfer.this);
builder.setMessage(getString(R.string.label_sharing_warn_size, mFilesize));
builder.setCancelable(false);
builder.setPositiveButton(R.string.label_yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int position) {
initiateTransfer(remote);
}
});
builder.setNegativeButton(R.string.label_no, null);
registerDialog(builder.show());
} else {
initiateTransfer(remote);
}
}
};
OnClickListener btnSelectListener = new OnClickListener() {
public void onClick(View v) {
selectDocument();
}
};
OnClickListener btnPauseListener = new OnClickListener() {
public void onClick(View v) {
try {
if (mFileTransfer.isAllowedToPauseTransfer()) {
mFileTransfer.pauseTransfer();
} else {
mPauseBtn.setEnabled(false);
showMessage(R.string.label_pause_ft_not_allowed);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
OnClickListener btnResumeListener = new OnClickListener() {
public void onClick(View v) {
try {
if (mFileTransfer.isAllowedToResumeTransfer()) {
mFileTransfer.resumeTransfer();
} else {
mResumeBtn.setEnabled(false);
showMessage(R.string.label_resume_ft_not_allowed);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
/* Set contact selector */
mSpinner = (Spinner) findViewById(R.id.contact);
/* Set buttons callback */
mInviteBtn = (Button) findViewById(R.id.invite_btn);
mInviteBtn.setOnClickListener(btnInviteListener);
mInviteBtn.setEnabled(false);
mSelectBtn = (Button) findViewById(R.id.select_btn);
mSelectBtn.setOnClickListener(btnSelectListener);
mSelectBtn.setEnabled(false);
mPauseBtn = (Button) findViewById(R.id.pause_btn);
mPauseBtn.setOnClickListener(btnPauseListener);
mPauseBtn.setEnabled(false);
mResumeBtn = (Button) findViewById(R.id.resume_btn);
mResumeBtn.setOnClickListener(btnResumeListener);
mResumeBtn.setEnabled(false);
mUriTextView = (TextView) findViewById(R.id.uri);
mUriTextView.setText("");
mSizeTextView = (TextView) findViewById(R.id.size);
mSizeTextView.setText("");
mStatusView = (TextView) findViewById(R.id.progress_status);
mStatusView.setText("");
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mIconCheckBox = (CheckBox) findViewById(R.id.ft_thumb);
mAudioMessageCheckBox = (CheckBox) findViewById(R.id.send_audio_msg);
TableRow expiration = (TableRow) findViewById(R.id.expiration);
expiration.setVisibility(View.GONE);
mFileTransferListener = new OneToOneFileTransferListener() {
@Override
public void onProgressUpdate(ContactId contact, String transferId,
final long currentSize, final long totalSize) {
/* Discard event if not for current transferId */
if (InitiateFileTransfer.this.mFileTransferId == null
|| !InitiateFileTransfer.this.mFileTransferId.equals(transferId)) {
return;
}
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(ContactId contact, String transferId,
final FileTransfer.State state, final FileTransfer.ReasonCode reasonCode) {
/* Discard event if not for current transferId */
if (InitiateFileTransfer.this.mFileTransferId == null
|| !InitiateFileTransfer.this.mFileTransferId.equals(transferId)) {
return;
}
final String _reasonCode = RiApplication.sFileTransferReasonCodes[reasonCode
.toInt()];
final String _state = RiApplication.sFileTransferStates[state.toInt()];
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " transferId=" + transferId
+ " state=" + _state + " reason=" + _reasonCode);
}
mHandler.post(new Runnable() {
public void run() {
if (mFileTransfer != null) {
try {
mResumeBtn.setEnabled(mFileTransfer.isAllowedToResumeTransfer());
} catch (RcsServiceException e) {
mResumeBtn.setEnabled(false);
showException(e);
}
try {
mPauseBtn.setEnabled(mFileTransfer.isAllowedToPauseTransfer());
} catch (RcsServiceException e) {
mPauseBtn.setEnabled(false);
showException(e);
}
}
switch (state) {
case STARTED:
/* Display session status */
mStatusView.setText(_state);
break;
case ABORTED:
showMessageThenExit(getString(R.string.label_transfer_aborted,
_reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_transfer_rejected,
_reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_transfer_failed,
_reasonCode));
break;
case TRANSFERRED:
/* Display transfer progress */
mStatusView.setText(_state);
try {
displayFileExpiration(mFileTransfer.getFileExpiration());
Uri fileIconUri = mFileTransfer.getFileIcon();
long fileIconExpiration = mFileTransfer.getFileIconExpiration();
if (LogUtils.isActive) {
Log.d(LOGTAG, "File transferred icon uri= " + fileIconUri
+ " expiration=" + fileIconExpiration);
}
} catch (RcsServiceException e) {
showException(e);
}
break;
default:
mStatusView.setText(_state);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged ".concat(getString(
R.string.label_ft_state_changed, _state, _reasonCode)));
}
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set transferIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " transferIds=" + transferIds);
}
}
};
}
/**
* Forge intent to resume Originating File Transfer
*
* @param ctx The context
* @param ftDao The FileTransfer DAO
* @param resume intent
* @return intent
*/
public static Intent forgeResumeIntent(Context ctx, FileTransferDAO ftDao, Intent resume) {
resume.setClass(ctx, InitiateFileTransfer.class);
resume.addFlags(Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_ACTIVITY_NEW_TASK);
/* Save FileTransferDAO into intent */
Bundle bundle = new Bundle();
bundle.putParcelable(BUNDLE_FTDAO_ID, ftDao);
resume.putExtras(bundle);
return resume;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/ReceiveFileTransfer.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.services.rcs.RcsPermissionDeniedException;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferIntent;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.GroupFileTransferListener;
import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfo;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.messaging.chat.group.GroupChatDAO;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsPermissionDeniedException;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferIntent;
import com.gsma.services.rcs.filetransfer.FileTransferLog;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.GroupFileTransferListener;
import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfo;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.StatFs;
import android.provider.MediaStore;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.IOException;
import java.util.Set;
/**
* Received file transfer
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class ReceiveFileTransfer extends RcsActivity {
private static final String LOGTAG = LogUtils.getTag(ReceiveFileTransfer.class.getSimpleName());
private static final String VCARD_MIME_TYPE = "text/x-vcard";
private static final String BUNDLE_FTDAO_ID = "ftdao";
/**
* UI mHandler
*/
private final Handler mHandler = new Handler();
private FileTransfer mFileTransfer;
private FileTransferDAO mFtDao;
private boolean mGroupFileTransfer = false;
private String mTransferId;
private Button mPauseBtn;
private Button mResumeBtn;
private OnClickListener mDeclineBtnListener;
private OnClickListener mAcceptBtnListener;
private OneToOneFileTransferListener mFileTransferListener;
private GroupFileTransferListener mGroupFtListener;
private FileTransferService mFileTransferService;
private ProgressBar mProgressBar;
private TextView mStatusView;
private TextView mSizeTextView;
private TextView mFilenameTextView;
private TextView mExpirationTextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.filetransfer_receive);
initialize();
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.FILE_TRANSFER, RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
} else {
startMonitorServices(RcsServiceName.FILE_TRANSFER, RcsServiceName.CONTACT);
mFileTransferService = getFileTransferApi();
if (!processIntent(getIntent(), false)) {
return;
}
try {
if (mGroupFileTransfer) {
mFileTransferService.addEventListener(mGroupFtListener);
} else {
mFileTransferService.addEventListener(mFileTransferListener);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processIntent(intent, true);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mFileTransferService != null && isServiceConnected(RcsServiceName.FILE_TRANSFER)) {
/* Remove service listener */
try {
if (mGroupFileTransfer) {
mFileTransferService.removeEventListener(mGroupFtListener);
} else {
mFileTransferService.removeEventListener(mFileTransferListener);
}
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
boolean processIntent(Intent intent, boolean newIntent) {
mFtDao = intent.getExtras().getParcelable(BUNDLE_FTDAO_ID);
if (mFtDao == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "processIntent cannot read File Transfer invitation (newIntent="
+ newIntent + ")");
}
finish();
return false;
}
/* Get the file transfer session */
mTransferId = mFtDao.getTransferId();
if (LogUtils.isActive) {
Log.d(LOGTAG, "processIntent (newIntent=" + newIntent + ") " + mFtDao);
}
ContactId contact = mFtDao.getContact();
/* Get invitation info */
boolean resuming = FileTransferIntent.ACTION_RESUME.equals(intent.getAction());
mGroupFileTransfer = GroupChatDAO.isGroupChat(mFtDao.getChatId(), contact);
String from = RcsContactUtil.getInstance(this).getDisplayName(contact);
TextView fromTextView = (TextView) findViewById(R.id.from);
fromTextView.setText(getString(R.string.label_from_args, from));
String size = getString(R.string.label_file_size,
FileUtils.humanReadableByteCount(mFtDao.getSize(), true));
mSizeTextView.setText(size);
mFilenameTextView.setText(getString(R.string.label_filename, mFtDao.getFilename()));
long fileExpiration = mFtDao.getFileExpiration();
if (fileExpiration != FileTransferLog.UNKNOWN_EXPIRATION) {
CharSequence expiration = DateUtils.getRelativeTimeSpanString(
mFtDao.getFileExpiration(), System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
mExpirationTextView.setText(getString(R.string.label_expiration_args, expiration));
} else {
mExpirationTextView.setVisibility(View.GONE);
}
try {
mFileTransfer = mFileTransferService.getFileTransfer(mTransferId);
if (mFileTransfer == null) {
if (FileTransfer.State.TRANSFERRED == mFtDao.getState()) {
displayTransferredFile();
return false;
} else {
String reasonCode = RiApplication.sFileTransferReasonCodes[mFtDao
.getReasonCode().toInt()];
if (LogUtils.isActive) {
Log.e(LOGTAG,
"Transfer ID=" + mTransferId + " failed state=" + mFtDao.getState()
+ " reason=" + reasonCode);
}
showMessageThenExit(getString(R.string.label_transfer_failed, reasonCode));
return false;
}
}
/* Do not consider acceptance if resuming */
if (resuming) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "processIntent resuming ".concat(mTransferId));
}
return true;
}
FileTransfer.State state = mFileTransfer.getState();
/* Check if not already accepted by the stack */
if (FileTransfer.State.INVITED != state) {
/* File Transfer is auto accepted by the stack. Check capacity */
isCapacityOk(mFtDao.getSize());
if (FileTransfer.State.TRANSFERRED == state) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "processIntent file is transferred ".concat(mTransferId));
}
displayTransferredFile();
return false;
}
} else {
/* File Transfer must be accepted/rejected by user */
if (LogUtils.isActive) {
Log.d(LOGTAG, "Wait for user acceptance (transferId=" + mTransferId + ")");
}
// @formatter:off
// The following code is intentionally commented to test the
// CORE.
// UI should check the file size to cancel if it is too big.
// if (isCapacityOk(fileSize) == false) {
// rejectInvitation();
// return;
// }
// @formatter:on
/* Manual accept */
AlertDialog.Builder builder = new AlertDialog.Builder(this);
View titleView = getLayoutInflater().inflate(R.layout.filetransfer_custom_title,
null);
builder.setCustomTitle(titleView);
builder.setMessage(getString(R.string.label_ft_from_size, from,
FileUtils.humanReadableByteCount(mFtDao.getSize(), true)));
builder.setCancelable(false);
/* Make sure progress bar is at the beginning */
mProgressBar.setProgress(0);
ImageView iconView = (ImageView) titleView
.findViewById(R.id.filetransfer_alert_title_icon);
if (mFtDao.getThumbnail() != null) {
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(),
mFtDao.getThumbnail());
iconView.setImageBitmap(bitmap);
} catch (IOException e) {
showException(e);
}
} else {
if (VCARD_MIME_TYPE.equals(mFtDao.getMimeType())) {
iconView.setImageResource(R.drawable.ri_contact_card_icon);
} else {
iconView.setImageResource(R.drawable.ri_notif_file_transfer_icon);
}
}
builder.setPositiveButton(R.string.label_accept, mAcceptBtnListener);
builder.setNegativeButton(R.string.label_decline, mDeclineBtnListener);
registerDialog(builder.show());
}
return true;
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
return false;
}
private void acceptInvitation() {
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Accept invitation (transferId=" + mTransferId + ")");
}
mFileTransfer.acceptInvitation();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void rejectInvitation() {
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Reject invitation (transferId=" + mTransferId + ")");
}
mFileTransfer.rejectInvitation();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void updateProgressBar(long currentSize, long totalSize) {
mStatusView.setText(Utils.getProgressLabel(currentSize, totalSize));
double position = ((double) currentSize / (double) totalSize) * 100.0;
mProgressBar.setProgress((int) position);
}
private void quitSession() {
try {
if (mFileTransfer != null
&& RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) {
mFileTransfer.abortTransfer();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mFileTransfer = null;
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
try {
if (KeyEvent.KEYCODE_BACK == keyCode) {
if (mFileTransfer == null
|| !RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) {
finish();
return true;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
/* Exit activity */
finish();
}
});
builder.setCancelable(true);
registerDialog(builder.show());
return true;
}
} catch (RcsServiceException e) {
showException(e);
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_ft, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
/**
* Check whether file size exceeds the limit
*
* @param size Size of file
* @return {@code true} if file size limit is exceeded, otherwise {@code false}
*/
private boolean isFileSizeExceeded(long size) {
try {
long maxSize = mFileTransferService.getConfiguration().getMaxSize();
return (maxSize > 0 && size > maxSize);
} catch (RcsServiceException e) {
showException(e);
return false;
}
}
/**
* Get available space in external storage, only if external storage is ready to write
*
* @return Available space in bytes, otherwise -1
*/
@SuppressWarnings("deprecation")
private static long getExternalStorageFreeSpace() {
long freeSpace = -1;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
freeSpace = blockSize * availableBlocks;
}
return freeSpace;
}
private enum FileCapacity {
OK, FILE_TOO_BIG, STORAGE_TOO_SMALL
}
/**
* Check if file capacity is acceptable
*
* @param fileSize file size in bytes
* @return FileSharingError or null if file capacity is acceptable
*/
private FileCapacity isFileCapacityAcceptable(long fileSize) {
if (isFileSizeExceeded(fileSize)) {
return FileCapacity.FILE_TOO_BIG;
}
long freeSpage = getExternalStorageFreeSpace();
boolean storageIsTooSmall = (freeSpage > 0) && fileSize > freeSpage;
if (storageIsTooSmall) {
return FileCapacity.STORAGE_TOO_SMALL;
}
return FileCapacity.OK;
}
/**
* Check if file size is less than maximum or then free space on disk
*
* @param fileSize file size in bytes
* @return boolean
*/
private boolean isCapacityOk(long fileSize) {
FileCapacity capacity = isFileCapacityAcceptable(fileSize);
switch (capacity) {
case FILE_TOO_BIG:
showMessageThenExit(R.string.label_transfer_failed_too_big);
return false;
case STORAGE_TOO_SMALL:
showMessageThenExit(R.string.label_transfer_failed_capacity_too_small);
return false;
default:
return true;
}
}
/**
* Update UI on file transfer state change
*
* @param transferId transfer ID
* @param state new FT state
* @param reasonCode reason code
*/
private void onTransferStateChangedUpdateUI(String transferId, final FileTransfer.State state,
final FileTransfer.ReasonCode reasonCode) {
final String _reasonCode = RiApplication.sFileTransferReasonCodes[reasonCode.toInt()];
final String _state = RiApplication.sFileTransferStates[state.toInt()];
if (LogUtils.isActive) {
Log.d(LOGTAG, "TransferStateChanged transferId=" + transferId + " state=" + state
+ " reason=" + reasonCode);
}
mHandler.post(new Runnable() {
public void run() {
if (mFileTransfer != null) {
try {
mResumeBtn.setEnabled(mFileTransfer.isAllowedToResumeTransfer());
} catch (RcsServiceException e) {
mResumeBtn.setEnabled(false);
showException(e);
}
try {
mPauseBtn.setEnabled(mFileTransfer.isAllowedToPauseTransfer());
} catch (RcsServiceException e) {
mPauseBtn.setEnabled(false);
showException(e);
}
}
switch (state) {
case ABORTED:
showMessageThenExit(getString(R.string.label_transfer_aborted, _reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_transfer_failed, _reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_transfer_rejected, _reasonCode));
break;
case TRANSFERRED:
displayTransferredFile();
break;
default:
mStatusView.setText(_state);
}
}
});
}
private void displayTransferredFile() {
mStatusView.setText(RiApplication.sFileTransferStates[FileTransfer.State.TRANSFERRED
.toInt()]);
/* Make sure progress bar is at the end */
mProgressBar.setProgress(mProgressBar.getMax());
try {
mFileTransferService.markFileTransferAsRead(mTransferId);
} catch (RcsServiceNotAvailableException ignore) {
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
String mimeType = mFtDao.getMimeType();
if (VCARD_MIME_TYPE.equals(mimeType)) {
// Show the transferred vCard
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(mFtDao.getFile(), VCARD_MIME_TYPE);
startActivity(intent);
} else if (Utils.isImageType(mimeType)) {
/* Show the transferred image */
Utils.showPicture(this, mFtDao.getFile());
} else if (Utils.isAudioType(mimeType)
&& FileTransfer.Disposition.RENDER == mFtDao.getDisposition()) {
Utils.playAudio(this, mFtDao.getFile());
}
}
private void onTransferProgressUpdateUI(final long currentSize, final long totalSize) {
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(currentSize, totalSize);
}
});
}
private void initialize() {
mGroupFtListener = new GroupFileTransferListener() {
@Override
public void onDeliveryInfoChanged(String chatId, ContactId contact, String transferId,
GroupDeliveryInfo.Status status, GroupDeliveryInfo.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDeliveryInfoChanged contact=" + contact + " transferId="
+ transferId + " state=" + status + " reason=" + reasonCode);
}
}
@Override
public void onProgressUpdate(String chatId, String transferId, long currentSize,
long totalSize) {
/* Discard event if not for current transferId */
if (!mFtDao.getTransferId().equals(transferId)) {
return;
}
onTransferProgressUpdateUI(currentSize, totalSize);
}
@Override
public void onStateChanged(String chatId, String transferId, FileTransfer.State state,
FileTransfer.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged chatId=" + chatId + " transferId=" + transferId);
}
/* Discard event if not for current transferId */
if (!mFtDao.getTransferId().equals(transferId)) {
return;
}
onTransferStateChangedUpdateUI(transferId, state, reasonCode);
}
@Override
public void onDeleted(String chatId, Set transferIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted chatId=" + chatId + " transferIds=" + transferIds);
}
}
};
mAcceptBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
acceptInvitation();
}
};
mDeclineBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
rejectInvitation();
/* Exit activity */
finish();
}
};
View.OnClickListener btnPauseListener = new View.OnClickListener() {
@Override
public void onClick(View arg0) {
try {
if (mFileTransfer.isAllowedToPauseTransfer()) {
mFileTransfer.pauseTransfer();
} else {
mPauseBtn.setEnabled(false);
showMessage(R.string.label_pause_ft_not_allowed);
}
} catch (RcsPermissionDeniedException e) {
Utils.displayToast(ReceiveFileTransfer.this,
getString(R.string.label_pause_failed));
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
View.OnClickListener btnResumeListener = new View.OnClickListener() {
@Override
public void onClick(View arg0) {
try {
if (mFileTransfer.isAllowedToResumeTransfer()) {
mFileTransfer.resumeTransfer();
} else {
mResumeBtn.setEnabled(false);
showMessage(R.string.label_resume_ft_not_allowed);
}
} catch (RcsPermissionDeniedException e) {
Utils.displayToast(ReceiveFileTransfer.this,
getString(R.string.label_resume_failed));
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mFileTransferListener = new OneToOneFileTransferListener() {
@Override
public void onProgressUpdate(ContactId contact, String transferId,
final long currentSize, final long totalSize) {
/* Discard event if not for current transferId */
if (!mFtDao.getTransferId().equals(transferId)) {
return;
}
onTransferProgressUpdateUI(currentSize, totalSize);
}
@Override
public void onStateChanged(ContactId contact, String transferId,
final FileTransfer.State state, FileTransfer.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " transferId=" + transferId);
}
/* Discard event if not for current transferId */
if (!mFtDao.getTransferId().equals(transferId)) {
return;
}
onTransferStateChangedUpdateUI(transferId, state, reasonCode);
}
@Override
public void onDeleted(ContactId contact, Set transferIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " transferIds=" + transferIds);
}
}
};
/* Set pause and resume button */
mPauseBtn = (Button) findViewById(R.id.pause_btn);
mPauseBtn.setOnClickListener(btnPauseListener);
mPauseBtn.setEnabled(false);
mResumeBtn = (Button) findViewById(R.id.resume_btn);
mResumeBtn.setOnClickListener(btnResumeListener);
mResumeBtn.setEnabled(false);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mStatusView = (TextView) findViewById(R.id.progress_status);
mStatusView.setText("");
mSizeTextView = (TextView) findViewById(R.id.image_size);
mSizeTextView.setText("");
mFilenameTextView = (TextView) findViewById(R.id.image_filename);
mFilenameTextView.setText("");
mExpirationTextView = (TextView) findViewById(R.id.expiration);
mExpirationTextView.setText("");
}
/**
* Forge invitation intent to start ReceiveFileTransfer activity
*
* @param ctx The context
* @param ftDao The FileTransfer DAO
* @param invitation intent
* @return intent
*/
public static Intent forgeInvitationIntent(Context ctx, FileTransferDAO ftDao, Intent invitation) {
invitation.setClass(ctx, ReceiveFileTransfer.class);
invitation.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
/* Save FileTransferDAO into intent */
Bundle bundle = new Bundle();
bundle.putParcelable(BUNDLE_FTDAO_ID, ftDao);
invitation.putExtras(bundle);
return invitation;
}
/**
* Forge resume intent to start ReceiveFileTransfer activity
*
* @param ctx The context
* @param ftDao The FileTransfer DAO
* @param resume intent
* @return intent
*/
public static Intent forgeResumeIntent(Context ctx, FileTransferDAO ftDao, Intent resume) {
resume.setClass(ctx, ReceiveFileTransfer.class);
resume.addFlags(Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_ACTIVITY_NEW_TASK);
/* Save FileTransferDAO into intent */
Bundle bundle = new Bundle();
bundle.putParcelable(BUNDLE_FTDAO_ID, ftDao);
resume.putExtras(bundle);
return resume;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/TestFileTransferApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import com.gsma.rcs.ri.R;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* File transfer API
*
* @author Jean-Marc AUFFRET
*/
public class TestFileTransferApi extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Set items
String[] items = {
getString(R.string.menu_transfer_file),
getString(R.string.menu_file_transfer_config)
};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, InitiateFileTransfer.class));
break;
case 1:
startActivity(new Intent(this, FileTransferServiceConfigActivity.class));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/UndeliveredFileReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Undelivered message receiver
*
* @author YPLO6403
*/
public class UndeliveredFileReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, FileTransferIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/multi/FileTransferProperties.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer.multi;
import android.net.Uri;
/**
* FileTransferProperties
*/
public class FileTransferProperties {
private final Uri mUri;
private final String mMimeType;
private boolean mFileIcon;
private boolean mAudioMessage;
private final String mFilename;
private final long mSize;
private String mStatus;
private int mProgress;
private String mReasonCode;
/**
* Default constructor
*
* @param uri the file URI
* @param filename the file name
* @param size the file size
* @param mimeType the mime type
*/
public FileTransferProperties(Uri uri, String filename, long size, String mimeType) {
mUri = uri;
mFilename = filename;
mSize = size;
mMimeType = mimeType;
}
/**
* Gets the file URI
*
* @return the file URI
*/
public Uri getUri() {
return mUri;
}
/**
* Gets the file icon flag
*
* @return the file icon flag
*/
public boolean isFileicon() {
return mFileIcon;
}
/**
* Sets file icon flag
*
* @param fileicon the file icon flag
*/
public void setFileicon(boolean fileicon) {
mFileIcon = fileicon;
}
/**
* @return the FileName
*/
public String getFilename() {
return mFilename;
}
/**
* Gets the file transfer status
*
* @return the Status
*/
public String getStatus() {
return mStatus;
}
/**
* Sets the file transfer status
*
* @param status the status
*/
public void setStatus(String status) {
mStatus = status;
}
/**
* Gets the file transfer progress (in percentage)
*
* @return the file transfer progress
*/
public int getProgress() {
return mProgress;
}
/**
* Sets the file transfer progress (in percentage)
*
* @param progress the file transfer progress
*/
public void setProgress(int progress) {
mProgress = progress;
}
/**
* Gets the file size
*
* @return the size
*/
public long getSize() {
return mSize;
}
/**
* Gets the reason code
*
* @return the reason code
*/
public String getReasonCode() {
return mReasonCode;
}
/**
* Sets the reason code
*
* @param reasonCode the reason code
*/
public void setReasonCode(String reasonCode) {
mReasonCode = reasonCode;
}
/**
* Gets the mime type
*
* @return the mime type
*/
public String getMimeType() {
return mMimeType;
}
/**
* Gets the audio message flag
*
* @return the audio message flag
*/
public boolean isAudioMessage() {
return mAudioMessage;
}
/**
* Sets the audio message flag
*
* @param audioMessage the audio message flag
*/
public void setAudioMessage(boolean audioMessage) {
mAudioMessage = audioMessage;
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/multi/ISendMultiFile.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer.multi;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import java.util.List;
/**
* @author Philippe LEMORDANT
*/
public interface ISendMultiFile {
/**
* Initialize
*/
void initialize();
/**
* Transfers list of files
*
* @param files to transfer
* @return boolean
*/
boolean transferFiles(List files);
/**
* Adds file transfer event listener
*
* @param fileTransferService file transfer service
* @throws RcsServiceException
*/
void addFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException;
/**
* Removes file transfer event listener
*
* @param fileTransferService file transfer service
* @throws RcsServiceException
*/
void removeFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException;
/**
* Checks if it is possible to initiate file transfer to group chat or single contact.
*
* @param chatId the chat ID or contact ID in single chat
* @return True if it is possible to initiate file transfer to group chat or single contact.
* @throws RcsServiceException
*/
boolean checkPermissionToSendFile(String chatId) throws RcsServiceException;
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/multi/SendMultiFile.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer.multi;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* SendMultiFile
*
* @author Philippe LEMORDANT
*/
public abstract class SendMultiFile extends RcsActivity implements ISendMultiFile {
private static final String LOGTAG = LogUtils.getTag(SendMultiFile.class.getSimpleName());
private final static String EXTRA_CHAT_ID = "chat_id";
protected String mChatId;
/**
* UI Handler
*/
protected final Handler mHandler = new Handler();
/**
* The list of file transfer IDs
*/
protected List mTransferIds;
/**
* The list of files to transfer
*/
protected List mFiles;
/**
* A flag to only add listener once and to remove only if previously added
*/
protected boolean mFileTransferListenerAdded = false;
protected Set mFileTransfers;
private ProgressDialog mProgressDialog;
protected FileTransferAdapter mFileTransferAdapter;
protected FileTransferService mFileTransferService;
private boolean mInvited;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialize();
mTransferIds = new ArrayList<>();
if (!parseIntent(getIntent())) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate invalid intent");
}
finish();
return;
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.filetransfer_send_multi_file);
ListView listView = (ListView) findViewById(android.R.id.list);
mFileTransferAdapter = new FileTransferAdapter(this,
R.layout.filetransfer_send_multi_file_item,
mFiles.toArray(new FileTransferProperties[mFiles.size()]));
listView.setAdapter(mFileTransferAdapter);
mFileTransfers = new HashSet<>();
/* Set start button */
Button startButton = (Button) findViewById(R.id.ft_start_btn);
startButton.setVisibility(View.GONE);
startButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO check warn size
initiateTransfer();
}
});
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.CHAT, RcsServiceName.FILE_TRANSFER,
RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
} else {
startMonitorServices(RcsServiceName.CHAT, RcsServiceName.FILE_TRANSFER,
RcsServiceName.CONTACT);
mFileTransferService = getFileTransferApi();
try {
Boolean authorized = checkPermissionToSendFile(mChatId);
if (authorized) {
startButton.setVisibility(View.VISIBLE);
addFileTransferEventListener(mFileTransferService);
} else {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Not allowed to transfer file to ".concat(mChatId));
}
startButton.setVisibility(View.INVISIBLE);
showMessage(R.string.label_ft_not_allowed);
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
private boolean parseIntent(Intent intent) {
mChatId = intent.getStringExtra(EXTRA_CHAT_ID);
String action = intent.getAction();
// Here we get data from the event.
if (Intent.ACTION_SEND.equals(action)) {
mFiles = new ArrayList<>();
// Handle normal one file or text sharing.
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
String fileName = FileUtils.getFileName(this, uri);
long fileSize = FileUtils.getFileSize(this, uri);
String mimeType = FileUtils.getMimeType(this, uri);
mFiles.add(new FileTransferProperties(uri, fileName, fileSize, mimeType));
if (LogUtils.isActive) {
Log.d(LOGTAG, "Transfer single file " + fileName + " (size=" + fileSize + ")");
}
return true;
}
if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
// Handle multiple file sharing.
ArrayList uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (uris != null) {
mFiles = new ArrayList<>();
StringBuilder files = new StringBuilder("Transfer multiple files [");
String loopDelim = "";
for (Uri uri : uris) {
String fileName = FileUtils.getFileName(this, uri);
long fileSize = FileUtils.getFileSize(this, uri);
String mimeType = FileUtils.getMimeType(this, uri);
mFiles.add(new FileTransferProperties(uri, fileName, fileSize, mimeType));
files.append(loopDelim);
files.append(fileName);
files.append("(");
files.append(fileSize);
files.append(")");
loopDelim = ",";
}
files.append("]");
if (LogUtils.isActive) {
Log.d(LOGTAG, files.toString());
}
return true;
}
}
return false;
}
@Override
public void onDestroy() {
super.onDestroy();
if (isServiceConnected(RcsServiceName.FILE_TRANSFER)) {
try {
removeFileTransferEventListener(mFileTransferService);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
private void initiateTransfer() {
if (transferFiles(mFiles)) {
mProgressDialog = showProgressDialog(getString(R.string.label_command_in_progress));
mProgressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
hideProgressDialog();
if (!isThereAnyOnGoingSession()) {
finish();
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onKeyDown abort on going sessions");
}
AlertDialog.Builder builder = new AlertDialog.Builder(SendMultiFile.this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(SendMultiFile.this,
getString(R.string.label_transfer_cancelled),
Toast.LENGTH_SHORT).show();
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
/* Exit activity */
finish();
}
});
builder.setCancelable(true);
registerDialog(builder.show());
}
});
/* Hide start button */
Button inviteBtn = (Button) findViewById(R.id.ft_start_btn);
inviteBtn.setVisibility(View.GONE);
mInvited = true;
}
}
/**
* Hide progress dialog
*/
protected void hideProgressDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
mProgressDialog = null;
}
}
/**
* Show the transfer progress
*
* @param position of the item whose data we want within the adapter's data set.
* @param currentSize Current size transferred
* @param totalSize Total size to be transferred
*/
protected void updateProgressBar(Integer position, long currentSize, long totalSize) {
FileTransferProperties prop = mFileTransferAdapter.getItem(position);
prop.setStatus(Utils.getProgressLabel(currentSize, totalSize));
double progress = ((double) currentSize / (double) totalSize) * 100.0;
prop.setProgress((int) progress);
mFileTransferAdapter.notifyDataSetChanged();
}
private void quitSession() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "quitSession");
}
try {
if (mFileTransfers != null) {
for (FileTransfer fileTransfer : mFileTransfers) {
if (RcsSessionUtil.isAllowedToAbortFileTransferSession(fileTransfer)) {
fileTransfer.abortTransfer();
}
}
mFileTransfers.clear();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mFileTransfers = null;
/* Exit activity */
finish();
}
}
private boolean isThereAnyOnGoingSession() {
if (mFileTransfers == null) {
return false;
}
try {
for (FileTransfer fileTransfer : mFileTransfers) {
if (RcsSessionUtil.isAllowedToAbortFileTransferSession(fileTransfer)) {
return true;
}
}
} catch (RcsServiceException e) {
showException(e);
}
return false;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_ft, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
/**
* Start activity
*
* @param context The context
* @param intent The original intent (with action SEND or SEND_MULTIPLE).
* @param isSingleChat True if file(s) is(are) sent over a single chat.
* @param chatId The chat ID.
*/
public static void startActivity(Context context, Intent intent, boolean isSingleChat,
String chatId) {
if (isSingleChat) {
intent.setClass(context, SendMultiFileSingleChat.class);
} else {
intent.setClass(context, SendMultiFileGroupChat.class);
}
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_CHAT_ID, chatId);
context.startActivity(intent);
}
/**
* FileTransfer adapter
*/
protected class FileTransferAdapter extends ArrayAdapter {
private Context mContext;
private int mLayoutResourceId;
private FileTransferProperties[] mFileTransferViewItems;
/**
* Constructor
*
* @param context The context.
* @param layoutResourceId the layout resource ID.
* @param filetransferViewItems the list of file transfer view items.
*/
public FileTransferAdapter(Context context, int layoutResourceId,
FileTransferProperties[] filetransferViewItems) {
super(context, layoutResourceId, filetransferViewItems);
mContext = context;
mLayoutResourceId = layoutResourceId;
mFileTransferViewItems = filetransferViewItems;
}
private class ViewHolder {
TextView mUri;
TextView mSize;
TextView mProgressStatus;
TableRow mReasonCodeTableRow;
TextView mReasonCodeText;
CheckBox mFileIcon;
CheckBox mAudiomsg;
ProgressBar mProgressBar;
ViewHolder(View view) {
mUri = (TextView) view.findViewById(R.id.uri);
mSize = (TextView) view.findViewById(R.id.filesize);
mProgressStatus = (TextView) view.findViewById(R.id.progress_status);
mFileIcon = (CheckBox) view.findViewById(R.id.ft_thumb);
mProgressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
mReasonCodeTableRow = (TableRow) view.findViewById(R.id.row_reason_code);
mReasonCodeText = (TextView) view.findViewById(R.id.text_reason_code);
mAudiomsg = (CheckBox) view.findViewById(R.id.send_audio_msg);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflater.inflate(mLayoutResourceId, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
final FileTransferProperties item = mFileTransferViewItems[position];
viewHolder.mUri.setText(item.getFilename());
viewHolder.mSize.setText(FileUtils.humanReadableByteCount(item.getSize(), true));
viewHolder.mProgressStatus.setText(item.getStatus());
String mimeType = item.getMimeType();
viewHolder.mAudiomsg.setEnabled(false);
viewHolder.mAudiomsg.setChecked(item.isAudioMessage());
viewHolder.mFileIcon.setEnabled(false);
viewHolder.mFileIcon.setChecked(item.isFileicon());
if (!mInvited) {
if (Utils.isImageType(mimeType)) {
viewHolder.mFileIcon.setEnabled(true);
viewHolder.mFileIcon.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v;
item.setFileicon(cb.isChecked());
}
});
} else if (Utils.isAudioType(mimeType)) {
viewHolder.mAudiomsg.setEnabled(true);
viewHolder.mAudiomsg.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v;
item.setAudioMessage(cb.isChecked());
}
});
}
}
viewHolder.mProgressBar.setProgress(item.getProgress());
if (item.getReasonCode() == null) {
viewHolder.mReasonCodeTableRow.setVisibility(View.GONE);
} else {
viewHolder.mReasonCodeTableRow.setVisibility(View.VISIBLE);
viewHolder.mReasonCodeText.setText(item.getReasonCode());
}
return convertView;
}
}
private boolean isFileTransferFinished(FileTransfer fileTransfer) throws RcsServiceException {
switch (fileTransfer.getState()) {
case STARTED:
case QUEUED:
case PAUSED:
case INITIATING:
return false;
default:
return true;
}
}
protected void closeDialogIfMultipleFileTransferIsFinished() {
if (mFileTransfers == null) {
return;
}
try {
for (FileTransfer fileTransfer : mFileTransfers) {
if (!isFileTransferFinished(fileTransfer)) {
/* There is one ongoing file transfer -> exit */
return;
}
}
/* There is no ongoing file transfer -> hide progress dialog */
hideProgressDialog();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/multi/SendMultiFileGroupChat.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer.multi;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.GroupFileTransferListener;
import com.gsma.services.rcs.groupdelivery.GroupDeliveryInfo;
import android.util.Log;
import java.util.List;
import java.util.Set;
/**
* SendMultiFileGroupChat
*
* @author Philippe LEMORDANT
*/
public class SendMultiFileGroupChat extends SendMultiFile implements ISendMultiFile {
private static final String LOGTAG = LogUtils.getTag(SendMultiFileGroupChat.class
.getSimpleName());
private GroupFileTransferListener mFtListener;
@Override
public boolean transferFiles(List filesToTransfer) {
try {
for (FileTransferProperties fileToTransfer : filesToTransfer) {
/* Initiate transfer */
FileTransfer fileTransfer = mFileTransferService.transferFileToGroupChat(mChatId,
fileToTransfer.getUri(),
fileToTransfer.isAudioMessage() ? FileTransfer.Disposition.RENDER
: FileTransfer.Disposition.ATTACH, fileToTransfer.isFileicon());
if (fileTransfer == null) {
Log.e(LOGTAG, "Cannot transfer file: ID not found!");
return false;
}
String fileTransferId = fileTransfer.getTransferId();
mTransferIds.add(fileTransferId);
mFileTransfers.add(fileTransfer);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Transfer file '" + fileToTransfer.getFilename() + "' to chatId="
+ mChatId + " icon=" + fileToTransfer.isFileicon() + " fileTransferId="
+ fileTransferId);
}
}
return true;
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return false;
}
}
@Override
public void addFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
if (!mFileTransferListenerAdded) {
fileTransferService.addEventListener(mFtListener);
mFileTransferListenerAdded = true;
}
}
@Override
public void removeFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
if (mFileTransferListenerAdded) {
fileTransferService.removeEventListener(mFtListener);
mFileTransferListenerAdded = false;
}
}
@Override
public boolean checkPermissionToSendFile(String chatId) throws RcsServiceException {
return mFileTransferService.isAllowedToTransferFileToGroupChat(chatId);
}
@Override
public void initialize() {
mFtListener = new GroupFileTransferListener() {
@Override
public void onDeliveryInfoChanged(String chatId, ContactId contact, String transferId,
GroupDeliveryInfo.Status status, GroupDeliveryInfo.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDeliveryInfoChanged chatId=" + chatId + " contact=" + contact
+ " transferId=" + transferId + " state=" + status + " reason="
+ reasonCode);
}
}
@Override
public void onProgressUpdate(String chatId, final String transferId,
final long currentSize, final long totalSize) {
/* Discard event if not for current transferId */
if (!mTransferIds.contains(transferId)) {
return;
}
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(mTransferIds.indexOf(transferId), currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(String chatId, final String transferId,
final FileTransfer.State state, FileTransfer.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged chatId=" + chatId + " transferId=" + transferId
+ " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current transferId */
if (!mTransferIds.contains(transferId)) {
return;
}
final String _reasonCode;
if (FileTransfer.ReasonCode.UNSPECIFIED == reasonCode) {
_reasonCode = null;
} else {
_reasonCode = RiApplication.sFileTransferReasonCodes[reasonCode.toInt()];
}
final String _state = RiApplication.sFileTransferStates[state.toInt()];
final FileTransferProperties prop = mFileTransferAdapter.getItem(mTransferIds
.indexOf(transferId));
mHandler.post(new Runnable() {
public void run() {
prop.setStatus(_state);
prop.setReasonCode(_reasonCode);
mFileTransferAdapter.notifyDataSetChanged();
closeDialogIfMultipleFileTransferIsFinished();
}
});
}
@Override
public void onDeleted(String chatId, Set transferIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted chatId=" + chatId + " transferIds=" + transferIds);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/filetransfer/multi/SendMultiFileSingleChat.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.filetransfer.multi;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.contact.ContactUtil;
import com.gsma.services.rcs.filetransfer.FileTransfer;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener;
import android.util.Log;
import java.util.List;
import java.util.Set;
/**
* SendMultiFileSingleChat
*
* @author Philippe LEMORDANT
*/
public class SendMultiFileSingleChat extends SendMultiFile implements ISendMultiFile {
private static final String LOGTAG = LogUtils.getTag(SendMultiFileSingleChat.class
.getSimpleName());
private OneToOneFileTransferListener mFtListener;
@Override
public boolean transferFiles(List filesToTransfer) {
try {
ContactId contact = ContactUtil.getInstance(this).formatContact(mChatId);
for (FileTransferProperties fileToTransfer : filesToTransfer) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Transfer file '" + fileToTransfer.getFilename()
+ "' to contact=" + contact + " icon=" + fileToTransfer.isFileicon());
}
FileTransfer fileTransfer = mFileTransferService.transferFile(contact,
fileToTransfer.getUri(),
fileToTransfer.isAudioMessage() ? FileTransfer.Disposition.RENDER
: FileTransfer.Disposition.ATTACH, fileToTransfer.isFileicon());
if (fileTransfer == null) {
Log.e(LOGTAG, "Cannot transfer file: ID not found!");
return false;
}
mFileTransfers.add(fileTransfer);
mTransferIds.add(fileTransfer.getTransferId());
}
return true;
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return false;
}
}
@Override
public void addFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
if (!mFileTransferListenerAdded) {
fileTransferService.addEventListener(mFtListener);
mFileTransferListenerAdded = true;
}
}
@Override
public void removeFileTransferEventListener(FileTransferService fileTransferService)
throws RcsServiceException {
if (mFileTransferListenerAdded) {
fileTransferService.removeEventListener(mFtListener);
mFileTransferListenerAdded = false;
}
}
@Override
public boolean checkPermissionToSendFile(String chatId) throws RcsServiceException {
ContactId contact = ContactUtil.getInstance(this).formatContact(mChatId);
return mFileTransferService.isAllowedToTransferFile(contact);
}
@Override
public void initialize() {
mFtListener = new OneToOneFileTransferListener() {
@Override
public void onProgressUpdate(ContactId contact, final String transferId,
final long currentSize, final long totalSize) {
/* Discard event if not for current transferId */
if (!mTransferIds.contains(transferId)) {
return;
}
// if (LogUtils.isActive) {
// Log.d(LOGTAG,
// "onProgressUpdate contact " + contact + " transferId:" + transferId
// + " (size=" + currentSize + ") (total=" + totalSize + ") "
// + mTransferIds.size());
// }
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(mTransferIds.indexOf(transferId), currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(ContactId contact, String transferId,
final FileTransfer.State state, FileTransfer.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " transferId=" + transferId
+ " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current transferId */
if (!mTransferIds.contains(transferId)) {
return;
}
final String _reasonCode;
if (FileTransfer.ReasonCode.UNSPECIFIED == reasonCode) {
_reasonCode = null;
} else {
_reasonCode = RiApplication.sFileTransferReasonCodes[reasonCode.toInt()];
}
final String _state = RiApplication.sFileTransferStates[state.toInt()];
final FileTransferProperties prop = mFileTransferAdapter.getItem(mTransferIds
.indexOf(transferId));
mHandler.post(new Runnable() {
public void run() {
prop.setStatus(_state);
prop.setReasonCode(_reasonCode);
mFileTransferAdapter.notifyDataSetChanged();
closeDialogIfMultipleFileTransferIsFinished();
}
});
}
@Override
public void onDeleted(ContactId contact, Set transferIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " transferIds=" + transferIds);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/geoloc/DisplayGeoloc.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.geoloc;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.chat.ChatLog.Message;
import com.gsma.services.rcs.contact.ContactId;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
/**
* Display geoloc on a Google map v2
*
* @author yplo6403
*/
public class DisplayGeoloc extends FragmentActivity implements OnMapReadyCallback {
/**
* Intent parameters: geolocation
*/
public final static String EXTRA_GEOLOC = "geoloc";
private HashMap mMapContactGeoloc;
private static BitmapDescriptor sMarkerIcon;
private static final int LARGE_ZOOM = 12;
private static final int SMALL_ZOOM = 4;
private final static String QUERY_SORT_ORDER = Message.TIMESTAMP + " DESC";
private final static String QUERY_WHERE_CLAUSE = Message.MIME_TYPE + "='"
+ Message.MimeType.GEOLOC_MESSAGE + "' AND " + Message.DIRECTION + " = "
+ Direction.OUTGOING.toInt();
private final static String[] QUERY_PROJECTION = new String[] {
Message.CONTENT
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geoloc_display);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
/* Get geoloc value from intent */
mMapContactGeoloc = (HashMap) getIntent()
.getSerializableExtra(EXTRA_GEOLOC);
if (sMarkerIcon == null) {
sMarkerIcon = BitmapDescriptorFactory.fromResource(R.drawable.ri_map_icon);
}
}
@Override
public void onMapReady(GoogleMap map) {
boolean singleEntry = (mMapContactGeoloc.size() == 1);
LatLng latLng = null;
LatLng myPosition = null;
for (Entry entry : mMapContactGeoloc.entrySet()) {
Geoloc geoloc = entry.getValue();
latLng = new LatLng(geoloc.getLatitude(), geoloc.getLongitude());
String contact = entry.getKey();
MarkerOptions options = new MarkerOptions().position(latLng).title(contact)
.icon(sMarkerIcon);
if (contact.equals(getString(R.string.label_me))) {
myPosition = latLng;
}
Marker marker = map.addMarker(options);
if (singleEntry) {
marker.showInfoWindow();
}
}
LatLng target = latLng;
int zoom = SMALL_ZOOM;
if (singleEntry && latLng != null) {
zoom = LARGE_ZOOM;
} else if (myPosition != null) {
target = myPosition;
}
CameraPosition cameraPosition = new CameraPosition.Builder().target(target).zoom(zoom)
.build();
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
private static Geoloc getLastGeoloc(Context ctx, ContactId contact) {
Cursor cursor = null;
String where = Message.CONTACT + "='" + contact.toString() + "' AND " + Message.MIME_TYPE
+ "='" + Message.MimeType.GEOLOC_MESSAGE + "' AND " + Message.DIRECTION + " = "
+ Direction.INCOMING.toInt();
try {
cursor = ctx.getContentResolver().query(Message.CONTENT_URI, QUERY_PROJECTION, where,
null, QUERY_SORT_ORDER);
if (cursor == null) {
throw new SQLException("Cannot query last geoloc for contact " + contact);
}
if (!cursor.moveToNext()) {
return null;
}
String content = cursor.getString(cursor.getColumnIndexOrThrow(Message.CONTENT));
return new Geoloc(content);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
private static Geoloc getMyLastGeoloc(Context ctx) {
Cursor cursor = null;
try {
cursor = ctx.getContentResolver().query(Message.CONTENT_URI, QUERY_PROJECTION,
QUERY_WHERE_CLAUSE, null, QUERY_SORT_ORDER);
if (cursor == null) {
throw new SQLException("Cannot query my last geoloc");
}
if (!cursor.moveToNext()) {
return null;
}
String content = cursor.getString(cursor.getColumnIndexOrThrow(Message.CONTENT));
return new Geoloc(content);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Show geolocation for a set of contacts
*
* @param ctx context
* @param contacts set of contacts
*/
public static void showContactsOnMap(Context ctx, Set contacts) {
HashMap mapContactGeoloc = new HashMap<>();
RcsContactUtil rcsContactUtil = RcsContactUtil.getInstance(ctx);
for (ContactId contact : contacts) {
Geoloc geoloc = getLastGeoloc(ctx, contact);
if (geoloc != null) {
mapContactGeoloc.put(rcsContactUtil.getDisplayName(contact), geoloc);
}
}
Geoloc myGeoloc = getMyLastGeoloc(ctx);
if (myGeoloc != null) {
mapContactGeoloc.put(ctx.getString(R.string.label_me), myGeoloc);
}
if (mapContactGeoloc.isEmpty()) {
Utils.displayLongToast(ctx, ctx.getString(R.string.label_geoloc_not_found));
return;
}
Intent intent = new Intent(ctx, DisplayGeoloc.class);
intent.putExtra(DisplayGeoloc.EXTRA_GEOLOC, mapContactGeoloc);
ctx.startActivity(intent);
}
/**
* Show geolocation for a one contact
*
* @param ctx context
* @param contact The contact
*/
public static void showContactOnMap(Context ctx, ContactId contact) {
Set set = new HashSet<>();
set.add(contact);
showContactsOnMap(ctx, set);
}
/**
* Show geolocation for a contact
*
* @param ctx The context
* @param contact The contact
* @param geoloc The geolocation
*/
public static void showContactOnMap(Context ctx, ContactId contact, Geoloc geoloc) {
HashMap mapContactGeoloc = new HashMap<>();
String displayName = RcsContactUtil.getInstance(ctx).getDisplayName(contact);
mapContactGeoloc.put(displayName, geoloc);
Intent intent = new Intent(ctx, DisplayGeoloc.class);
intent.putExtra(DisplayGeoloc.EXTRA_GEOLOC, mapContactGeoloc);
ctx.startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/geoloc/EditGeoloc.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.geoloc;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.chat.ChatServiceConfiguration;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.ActivityCompat;
import android.text.InputFilter;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
/**
* Geoloc info editor
*/
public class EditGeoloc extends RcsActivity {
/**
* Intent parameters
*/
public final static String EXTRA_GEOLOC = "geoloc";
private EditText mLocationEdit;
private EditText mLatitudeEdit;
private EditText mLongitudeEdit;
private EditText mAccuracyEdit;
private final static int REQUEST_CODE_SELECT_GEOLOC = 0;
private long geolocExpirationTime = 0;
private int geolocLabelMaxLength = 0;
private static final String LOGTAG = LogUtils.getTag(EditGeoloc.class.getSimpleName());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geoloc_edit);
/* Create editors */
mLocationEdit = (EditText) findViewById(R.id.location);
mLatitudeEdit = (EditText) findViewById(R.id.latitude);
mLongitudeEdit = (EditText) findViewById(R.id.longitude);
mAccuracyEdit = (EditText) findViewById(R.id.accuracy);
/* Set button callback */
Button validateBtn = (Button) findViewById(R.id.validate_btn);
validateBtn.setOnClickListener(btnValidateListener);
Button selectBtn = (Button) findViewById(R.id.select_geoloc_btn);
selectBtn.setOnClickListener(btnSelectListener);
// Display my current location
setMyLocation();
// Register to API connection manager
if (isServiceConnected(RcsServiceName.CHAT)) {
try {
ChatServiceConfiguration configuration = getChatApi().getConfiguration();
geolocExpirationTime = configuration.getGeolocExpirationTime();
geolocLabelMaxLength = configuration.getGeolocLabelMaxLength();
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
if (geolocLabelMaxLength > 0) {
InputFilter maxLengthFilter = new InputFilter.LengthFilter(geolocLabelMaxLength);
mLocationEdit.setFilters(new InputFilter[] {
maxLengthFilter
});
}
}
/**
* Set the location of the device
*/
protected void setMyLocation() {
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
String bestProvider = lm.getBestProvider(criteria, false);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
Location lastKnownLoc = lm.getLastKnownLocation(bestProvider);
if (lastKnownLoc != null) {
mLatitudeEdit.setText(String.valueOf(lastKnownLoc.getLatitude()));
mLongitudeEdit.setText(String.valueOf(lastKnownLoc.getLongitude()));
mAccuracyEdit.setText(String.valueOf(lastKnownLoc.getAccuracy()));
}
super.onResume();
}
/**
* Validate button listener
*/
private OnClickListener btnValidateListener = new OnClickListener() {
public void onClick(View v) {
String lat = mLatitudeEdit.getText().toString().trim();
if (lat.length() == 0) {
mLatitudeEdit.setText("0.0");
}
String lon = mLongitudeEdit.getText().toString().trim();
if (lon.length() == 0) {
mLongitudeEdit.setText("0.0");
}
String acc = mAccuracyEdit.getText().toString().trim();
if (acc.length() == 0) {
mAccuracyEdit.setText("0");
}
long expiration = System.currentTimeMillis() + geolocExpirationTime;
Geoloc geoloc = new Geoloc(mLocationEdit.getText().toString(), Double.parseDouble(lat),
Double.parseDouble(lon), expiration, Float.parseFloat(acc));
Intent in = new Intent();
in.putExtra(EXTRA_GEOLOC, (Parcelable) geoloc);
setResult(-1, in);
finish();
}
};
/**
* Select geolocation button listener
*/
private OnClickListener btnSelectListener = new OnClickListener() {
public void onClick(View v) {
// Start a new activity to send a geolocation
Intent geolocSelectIntent = new Intent(EditGeoloc.this, SelectGeoloc.class);
startActivityForResult(geolocSelectIntent, REQUEST_CODE_SELECT_GEOLOC);
}
};
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case REQUEST_CODE_SELECT_GEOLOC:
Geoloc geoloc = data.getParcelableExtra(EXTRA_GEOLOC);
mLatitudeEdit.setText(String.valueOf(geoloc.getLatitude()));
mLongitudeEdit.setText(String.valueOf(geoloc.getLongitude()));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/geoloc/SelectGeoloc.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.geoloc;
import com.gsma.rcs.ri.R;
import com.gsma.services.rcs.Geoloc;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
/**
* Select a geoloc from a map
*/
public class SelectGeoloc extends FragmentActivity implements OnMapReadyCallback,
OnMapClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geoloc_display);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap map) {
map.setOnMapClickListener(this);
}
@Override
public void onMapClick(LatLng point) {
Geoloc geoloc = new Geoloc(null, point.latitude, point.longitude, 0, 0);
Intent intent = getIntent();
intent.putExtra(DisplayGeoloc.EXTRA_GEOLOC, (Parcelable) geoloc);
setResult(RESULT_OK, intent);
finish();
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/messaging/geoloc/ShowGeoloc.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.messaging.geoloc;
import com.gsma.rcs.ri.R;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.contact.ContactId;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/**
* Activity to show Geoloc
*/
public class ShowGeoloc extends Activity {
/**
* Intent parameters
*/
public final static String EXTRA_GEOLOC = "geoloc";
public final static String EXTRA_CONTACT = "contact";
private Geoloc mGeoloc;
private ContactId mContact;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geoloc_show);
mGeoloc = getIntent().getParcelableExtra(EXTRA_GEOLOC);
mContact = getIntent().getParcelableExtra(EXTRA_CONTACT);
TextView contactText = (TextView) findViewById(R.id.contact);
contactText.setText(mContact.toString());
TextView locationText = (TextView) findViewById(R.id.location);
locationText.setText(mGeoloc.getLabel());
TextView latitudeText = (TextView) findViewById(R.id.latitude);
latitudeText.setText(Double.toString(mGeoloc.getLatitude()));
TextView longitudeText = (TextView) findViewById(R.id.longitude);
longitudeText.setText(Double.toString(mGeoloc.getLongitude()));
TextView accuracyText = (TextView) findViewById(R.id.accuracy);
accuracyText.setText(Float.toString(mGeoloc.getAccuracy()));
Button displayBtn = (Button) findViewById(R.id.display_geoloc_btn);
displayBtn.setOnClickListener(mBtnDisplayListener);
}
private OnClickListener mBtnDisplayListener = new OnClickListener() {
public void onClick(View v) {
DisplayGeoloc.showContactOnMap(ShowGeoloc.this, mContact, mGeoloc);
}
};
public static void ShowGeolocForContact(Context ctx, ContactId contact, Geoloc geoloc) {
Intent intent = new Intent(ctx, ShowGeoloc.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_GEOLOC, (Parcelable) geoloc);
intent.putExtra(EXTRA_CONTACT, (Parcelable) contact);
ctx.startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/permissions/PermissionsActivity.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package com.gsma.rcs.ri.permissions;
import com.gsma.rcs.api.connection.utils.RcsListActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RI;
import com.gsma.rcs.ri.utils.LogUtils;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.ArrayAdapter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* A class to request permissions
*
* @author Philippe LEMORDANT
*/
public class PermissionsActivity extends RcsListActivity {
/**
* List of permissions needed for service. Just need to ask one permission per dangerous group.
* READ_SMS is not asked since not required after Lollipop.
*/
// @formatter:off
private static final Set sAllPermissionsList = new HashSet<>(Arrays.asList(
Manifest.permission.READ_CONTACTS,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CAMERA,
Manifest.permission.CALL_PHONE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.Manifest.permission.RECORD_AUDIO));
// @formatter:on
private static final int MY_PERMISSION_REQUEST_ALL = 5428;
private static final String LOGTAG = LogUtils.getTag(PermissionsActivity.class.getSimpleName());
private static final String PERMISSION_ASKED = "permission_asked";
private Set notGrantedPermissions;
private boolean mAskingPermission;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ask_permissions);
notGrantedPermissions = getNotGrantedPermissions();
if (savedInstanceState != null) {
mAskingPermission = savedInstanceState.getBoolean(PERMISSION_ASKED);
if (!notGrantedPermissions.isEmpty()) {
if (!mAskingPermission) {
mAskingPermission = true;
ActivityCompat
.requestPermissions(this, notGrantedPermissions
.toArray(new String[notGrantedPermissions.size()]),
MY_PERMISSION_REQUEST_ALL);
}
displayUngrantedPermissions();
} else {
startActivity(new Intent(this, RI.class));
finish();
}
} else {
if (notGrantedPermissions.isEmpty()) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "All permissions are granted");
}
startActivity(new Intent(this, RI.class));
finish();
} else {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Permission to be asked: " + notGrantedPermissions);
askPermissions();
}
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// Make sure to call the super method so that the variables of our views are saved
super.onSaveInstanceState(outState);
// Save our own variable now
outState.putBoolean(PERMISSION_ASKED, mAskingPermission);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (MY_PERMISSION_REQUEST_ALL == requestCode) {
mAskingPermission = false;
notGrantedPermissions = new HashSet<>();
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
String unGrantedPermission = permissions[i];
if (LogUtils.isActive) {
Log.d(LOGTAG, "Permission Denied: " + unGrantedPermission);
}
notGrantedPermissions.add(unGrantedPermission);
}
}
if (!notGrantedPermissions.isEmpty()) {
displayUngrantedPermissions();
} else {
startActivity(new Intent(this, RI.class));
finish();
}
}
}
private void askPermissions() {
if (notGrantedPermissions.size() > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// only ask permissions once.
if (!mAskingPermission) {
mAskingPermission = true;
displayUngrantedPermissions();
requestPermissions(
notGrantedPermissions.toArray(new String[notGrantedPermissions.size()]),
MY_PERMISSION_REQUEST_ALL);
}
} else {
startActivity(new Intent(this, RI.class));
finish();
}
}
private Set getNotGrantedPermissions() {
Set permissionsToAsk = new HashSet<>();
for (String permission : PermissionsActivity.sAllPermissionsList) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this,
permission)) {
permissionsToAsk.add(permission);
}
}
return permissionsToAsk;
}
private void displayUngrantedPermissions() {
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
notGrantedPermissions.toArray(new String[notGrantedPermissions.size()])));
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/service/RegistrationStatus.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.service;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceRegistration;
import com.gsma.services.rcs.RcsServiceRegistrationListener;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;
/**
* Display and monitor the registration status
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class RegistrationStatus extends RcsActivity {
private final Handler mHandler = new Handler();
private MyRegistrationListener registrationListener = new MyRegistrationListener();
private static final String LOGTAG = LogUtils.getTag(RegistrationStatus.class.getSimpleName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.service_registration);
// Display registration status by default
displayRegistrationStatus(false);
// Register to API connection manager
if (!isServiceConnected(RcsServiceName.CAPABILITY)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.CAPABILITY);
try {
getCapabilityApi().addEventListener(registrationListener);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (isServiceConnected(RcsServiceName.CAPABILITY)) {
try {
getCapabilityApi().removeEventListener(registrationListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
@Override
protected void onResume() {
super.onResume();
if (isExiting()) {
return;
}
try {
displayRegistrationStatus(getCapabilityApi().isServiceRegistered());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private class MyRegistrationListener extends RcsServiceRegistrationListener {
// Service is registered to the network platform
public void onServiceRegistered() {
mHandler.post(new Runnable() {
public void run() {
displayRegistrationStatus(true);
}
});
}
// Service is unregistered from the network platform
public void onServiceUnregistered(RcsServiceRegistration.ReasonCode reason) {
mHandler.post(new Runnable() {
public void run() {
displayRegistrationStatus(false);
}
});
}
}
private void displayRegistrationStatus(boolean status) {
TextView statusTxt = (TextView) findViewById(R.id.registration_status);
statusTxt.setText(String.valueOf(status));
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/service/ServiceConfigurationActivity.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.service;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.CommonServiceConfiguration;
import com.gsma.services.rcs.CommonServiceConfiguration.MessagingMethod;
import com.gsma.services.rcs.CommonServiceConfiguration.MinimumBatteryLevel;
import com.gsma.services.rcs.RcsServiceControl;
import com.gsma.services.rcs.RcsServiceException;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Map;
/**
* Service configuration
*
* @author LEMORDANT Philippe
*/
public class ServiceConfigurationActivity extends RcsActivity {
private static final String LOGTAG = LogUtils.getTag(ServiceConfigurationActivity.class
.getSimpleName());
private Spinner mSpinnerDefMessaginMethod;
private Spinner mSpinnerMinBatteryLevel;
private TextView mTextEditDisplayName;
private CheckBox mCheckBoxIsConfigValid;
private TextView mTextEditMessagingUX;
private TextView mTextEditContactId;
private TextView mTextRcsServiceActivation;
private CommonServiceConfiguration mConfiguration;
private String mIntialDisplayName;
private RcsServiceControl mRcsServiceControl;
private static SparseArray sPosToMinimumBatteryLevel = new SparseArray<>();
private static Map sMinimumBatteryLevelToPos = new HashMap<>();
static {
int order = 0;
for (MinimumBatteryLevel entry : MinimumBatteryLevel.values()) {
sPosToMinimumBatteryLevel.put(order, entry);
sMinimumBatteryLevelToPos.put(entry, order++);
}
}
private MinimumBatteryLevel getMinimumBatteryLevelFromSpinnerPosition(int position) {
return sPosToMinimumBatteryLevel.get(position);
}
private int getSpinnerPositionFromMinimumBatteryLevel(MinimumBatteryLevel level) {
return sMinimumBatteryLevelToPos.get(level);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRcsServiceControl = RiApplication.getRcsServiceControl();
/* Set layout */
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.service_configuration);
mCheckBoxIsConfigValid = (CheckBox) findViewById(R.id.label_service_configuration_valid);
mTextEditMessagingUX = (TextView) findViewById(R.id.label_messaging_mode);
mTextEditContactId = (TextView) findViewById(R.id.label_my_contact_id);
mTextEditDisplayName = (TextView) findViewById(R.id.text_my_display_name);
mTextRcsServiceActivation = (TextView) findViewById(R.id.text_service_activation);
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.CONTACT);
try {
mConfiguration = getContactApi().getCommonConfiguration();
mIntialDisplayName = mConfiguration.getMyDisplayName();
if (mIntialDisplayName == null) {
mIntialDisplayName = "";
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
return;
}
String[] messagingMethods = getResources().getStringArray(R.array.messaging_method);
mSpinnerDefMessaginMethod = (Spinner) findViewById(R.id.spinner_default_messaging_method);
ArrayAdapter adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, messagingMethods);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinnerDefMessaginMethod.setAdapter(adapter);
mSpinnerDefMessaginMethod.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parentView, View selectedItemView,
int position, long id) {
MessagingMethod method = MessagingMethod.valueOf(mSpinnerDefMessaginMethod
.getSelectedItemPosition());
try {
MessagingMethod oldMethod = mConfiguration.getDefaultMessagingMethod();
if (!oldMethod.equals(method)) {
mConfiguration.setDefaultMessagingMethod(method);
if (LogUtils.isActive) {
Log.d(LOGTAG,
"onClick DefaultMessagingMethod ".concat(method.toString()));
}
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onNothingSelected(AdapterView> parentView) {
}
});
String[] batteryLevels = getResources().getStringArray(R.array.minimum_battery_level);
mSpinnerMinBatteryLevel = (Spinner) findViewById(R.id.spinner_label_min_battery_level);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, batteryLevels);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinnerMinBatteryLevel.setAdapter(adapter);
mSpinnerMinBatteryLevel.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parentView, View selectedItemView,
int position, long id) {
MinimumBatteryLevel level = getMinimumBatteryLevelFromSpinnerPosition(mSpinnerMinBatteryLevel
.getSelectedItemPosition());
try {
MinimumBatteryLevel oldLevel = mConfiguration.getMinimumBatteryLevel();
if (!oldLevel.equals(level)) {
mConfiguration.setMinimumBatteryLevel(level);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onClick MinimumBatteryLevel ".concat(level.toString()));
}
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onNothingSelected(AdapterView> parentView) {
}
});
startMonitorServices(RcsServiceName.CONTACT);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate");
}
}
@Override
public void onDestroy() {
super.onDestroy();
String newDisplayName = mTextEditDisplayName.getText().toString();
if (mIntialDisplayName != null && !mIntialDisplayName.equals(newDisplayName)) {
setDisplayName(newDisplayName);
}
}
@Override
protected void onResume() {
super.onResume();
if (isExiting()) {
return;
}
displayServiceConfiguration();
}
private void displayServiceConfiguration() {
try {
mCheckBoxIsConfigValid.setChecked(mConfiguration.isConfigValid());
mSpinnerDefMessaginMethod.setSelection(mConfiguration.getDefaultMessagingMethod()
.toInt());
mSpinnerMinBatteryLevel
.setSelection(getSpinnerPositionFromMinimumBatteryLevel(mConfiguration
.getMinimumBatteryLevel()));
mTextEditMessagingUX.setText(mConfiguration.getMessagingUX().name());
mTextEditDisplayName.setText(mConfiguration.getMyDisplayName());
mTextEditContactId.setText(mConfiguration.getMyContactId().toString());
boolean rcsServiceActivationchangeable = mRcsServiceControl
.isActivationModeChangeable();
if (rcsServiceActivationchangeable) {
mTextRcsServiceActivation
.setText(getString(R.string.label_service_activate_changeable));
} else {
mTextRcsServiceActivation
.setText(getString(R.string.label_service_activate_unchangeable));
}
} catch (RcsServiceException e) {
showException(e);
}
}
private void setDisplayName(String newDisplayName) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Refresh display name to ".concat(newDisplayName));
}
try {
mConfiguration.setMyDisplayName(newDisplayName);
} catch (RcsServiceException e) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Exception occurred", e);
}
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/service/ServiceStatus.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.service;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsPermissionDeniedException;
import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.RcsServiceControl;
import com.gsma.services.rcs.RcsServiceListener;
import com.gsma.services.rcs.capability.CapabilityService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/**
* Display and monitor the service status
*
* @author Jean-Marc AUFFRET
*/
public class ServiceStatus extends RcsActivity implements RcsServiceListener {
private RcsService mApi;
private RcsServiceControl mRcsServiceControl;
private TextView mServiceBound;
private TextView mServiceActivated;
private TextView mServiceStarted;
private static final String LOGTAG = LogUtils.getTag(ServiceStatus.class.getSimpleName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRcsServiceControl = RiApplication.getRcsServiceControl();
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.service_status);
mServiceBound = (TextView) findViewById(R.id.service_bound);
mServiceActivated = (TextView) findViewById(R.id.service_activated);
mServiceStarted = (TextView) findViewById(R.id.service_started);
Button serviceActivationRefresh = (Button) findViewById(R.id.service_refresh_all);
serviceActivationRefresh.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
displayServiceActivation();
displayServiceStarted();
}
});
// Display service status by default
displayServiceBinding(false);
displayServiceActivation();
displayServiceStarted();
// Register service up event listener
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(RcsService.ACTION_SERVICE_UP);
registerReceiver(serviceUpListener, intentFilter);
// Instantiate API
mApi = new CapabilityService(getApplicationContext(), this);
// Connect API
try {
mApi.connect();
} catch (RcsPermissionDeniedException e) {
mApi = null;
showMessageThenExit(R.string.label_api_not_compatible);
}
}
private void displayServiceActivation() {
try {
mServiceActivated.setText(Boolean.toString(mRcsServiceControl.isActivated()));
} catch (RcsGenericException e) {
Log.e(LOGTAG, "Failed to read service activation status", e);
mServiceActivated.setText(getString(R.string.error_service_activated));
}
}
private void displayServiceStarted() {
try {
mServiceStarted.setText(Boolean.toString(mRcsServiceControl.isServiceStarted()));
} catch (RcsGenericException e) {
Log.e(LOGTAG, "Failed to read service started", e);
mServiceActivated.setText(getString(R.string.error_service_started));
}
}
@Override
public void onDestroy() {
super.onDestroy();
// Unregister service up event listener
try {
unregisterReceiver(serviceUpListener);
} catch (IllegalArgumentException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
if (mApi != null) {
// Disconnect API
mApi.disconnect();
}
}
/**
* Callback called when service is connected. This method is called when the service is well
* connected to the RCS service (binding procedure successful): this means the methods of the
* API may be used.
*/
public void onServiceConnected() {
// Display service binding status
displayServiceBinding(true);
}
/**
* Callback called when service has been disconnected. This method is called when the service is
* disconnected from the RCS service (e.g. service deactivated).
*
* @param error Error
*/
public void onServiceDisconnected(ReasonCode error) {
// Display service binding status
displayServiceBinding(false);
}
/**
* Display service status
*
* @param status Status
*/
private void displayServiceBinding(boolean status) {
mServiceBound.setText(String.valueOf(status));
}
/**
* RCS service up event listener
*/
private BroadcastReceiver serviceUpListener = new BroadcastReceiver() {
@Override
public void onReceive(Context context, final Intent intent) {
// Retry a connection to the service
try {
mApi.connect();
} catch (RcsPermissionDeniedException e) {
mApi = null;
showMessageThenExit(R.string.label_api_not_compatible);
}
}
};
}
================================================
FILE: RI/src/com/gsma/rcs/ri/service/TestServiceApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.service;
import com.gsma.rcs.ri.R;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* Service API
*
* @author Jean-Marc AUFFRET
*/
public class TestServiceApi extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Set items
String[] items = {
getString(R.string.menu_service_status),
getString(R.string.menu_registration_status),
getString(R.string.menu_service_configuration)
};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, ServiceStatus.class));
break;
case 1:
startActivity(new Intent(this, RegistrationStatus.class));
break;
case 2:
startActivity(new Intent(this, ServiceConfigurationActivity.class));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/SharingListView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsFragmentActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.sharing.geoloc.GeolocSharingLogView;
import com.gsma.rcs.ri.sharing.image.ImageSharingLogView;
import com.gsma.rcs.ri.sharing.video.VideoSharingLogView;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.history.HistoryLog;
import com.gsma.services.rcs.history.HistoryUriBuilder;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharing;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingListener;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingLog;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingService;
import com.gsma.services.rcs.sharing.image.ImageSharing.ReasonCode;
import com.gsma.services.rcs.sharing.image.ImageSharing.State;
import com.gsma.services.rcs.sharing.image.ImageSharingListener;
import com.gsma.services.rcs.sharing.image.ImageSharingLog;
import com.gsma.services.rcs.sharing.image.ImageSharingService;
import com.gsma.services.rcs.sharing.video.VideoSharing;
import com.gsma.services.rcs.sharing.video.VideoSharingListener;
import com.gsma.services.rcs.sharing.video.VideoSharingLog;
import com.gsma.services.rcs.sharing.video.VideoSharingService;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
/**
* A class to view the sharing logs
*/
public class SharingListView extends RcsFragmentActivity implements
LoaderManager.LoaderCallbacks {
/**
* The loader's unique ID. Loader IDs are specific to the Activity in which they reside.
*/
private static final int LOADER_ID = 1;
// @formatter:off
private static final String[] PROJECTION = {
HistoryLog.BASECOLUMN_ID,
HistoryLog.ID,
HistoryLog.PROVIDER_ID,
HistoryLog.CONTACT,
HistoryLog.TIMESTAMP,
HistoryLog.DIRECTION,
HistoryLog.STATUS,
HistoryLog.FILENAME,
HistoryLog.DURATION,
HistoryLog.CONTENT,
HistoryLog.MIME_TYPE,
HistoryLog.TRANSFERRED,
HistoryLog.FILESIZE
};
// @formatter:on
private static final String LOGTAG = LogUtils.getTag(SharingListView.class.getName());
private static final int MAX_LENGTH_DESCRIPTION = 30;
private static final String SORT_BY = "timestamp DESC";
private static final String WHERE_CLAUSE_WITH_CONTACT = "contact=?";
/**
* Associate the providers name menu with providerIds defined in HistoryLog
*/
private static final TreeMap sProviders = new TreeMap<>();
private boolean[] mCheckedProviders;
private AlertDialog mFilterAlertDialog;
private List mFilterMenuItems;
private GeolocSharingListener mGeolocSharingListener;
private boolean mGeolocSharingListenerSet;
private GeolocSharingService mGeolocSharingService;
private final Handler mHandler = new Handler();
private ImageSharingListener mImageSharingListener;
private boolean mImageSharingListenerSet;
private ImageSharingService mImageSharingService;
private List mProviderIds;
private SharingLogAdapter mAdapter;
private Spinner mSpinner;
private VideoSharingListener mVideoSharingListener;
private boolean mVideoSharingListenerSet;
private VideoSharingService mVideoSharingService;
private class ViewHolder {
private final int mColumnContactIdx;
private final int mColumnTimestampIdx;
private final int mColumnStatusIdx;
private final int mColumnDirectionIdx;
private final TextView mTypeView;
private final TextView mContactView;
private final TextView mDescriptionView;
private final TextView mDateView;
private final ImageView mDirectionIconImageView;
private final int mColumnProviderIdIdx;
private final int mColumnFilenameIdx;
private final int mColumnDurationIdx;
private final int mColumnContentIdx;
public ViewHolder(View view, Cursor cursor) {
mColumnProviderIdIdx = cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID);
mColumnContactIdx = cursor.getColumnIndexOrThrow(HistoryLog.CONTACT);
mColumnTimestampIdx = cursor.getColumnIndexOrThrow(HistoryLog.TIMESTAMP);
mColumnDirectionIdx = cursor.getColumnIndexOrThrow(HistoryLog.DIRECTION);
mColumnStatusIdx = cursor.getColumnIndexOrThrow(HistoryLog.STATUS);
mColumnFilenameIdx = cursor.getColumnIndexOrThrow(HistoryLog.FILENAME);
mColumnDurationIdx = cursor.getColumnIndexOrThrow(HistoryLog.DURATION);
mColumnContentIdx = cursor.getColumnIndexOrThrow(HistoryLog.CONTENT);
mTypeView = (TextView) view.findViewById(R.id.conversation_type);
mContactView = (TextView) view.findViewById(R.id.contact_label);
mDescriptionView = (TextView) view.findViewById(R.id.description);
mDateView = (TextView) view.findViewById(R.id.date);
mDirectionIconImageView = (ImageView) view.findViewById(R.id.call_type_icon);
}
public int getColumnProviderIdIdx() {
return mColumnProviderIdIdx;
}
public int getColumnContactIdx() {
return mColumnContactIdx;
}
public int getColumnTimestampIdx() {
return mColumnTimestampIdx;
}
public int getColumnStatusIdx() {
return mColumnStatusIdx;
}
public int getColumnDirectionIdx() {
return mColumnDirectionIdx;
}
public TextView getTypeView() {
return mTypeView;
}
public TextView getContactView() {
return mContactView;
}
public TextView getDescriptionView() {
return mDescriptionView;
}
public TextView getDateView() {
return mDateView;
}
public ImageView getDirectionIconImageView() {
return mDirectionIconImageView;
}
public int getColumnFilenameIdx() {
return mColumnFilenameIdx;
}
public int getColumnDurationIdx() {
return mColumnDurationIdx;
}
public int getColumnContentIdx() {
return mColumnContentIdx;
}
}
private class SharingLogAdapter extends CursorAdapter {
private final LayoutInflater mInflater;
private final RcsContactUtil mRcsContactUtil;
private Drawable mDrawableIncoming;
private Drawable mDrawableIncomingFailed;
private Drawable mDrawableOutgoing;
private Drawable mDrawableOutgoingFailed;
public SharingLogAdapter(Activity activity) {
super(activity, null, 0);
mRcsContactUtil = RcsContactUtil.getInstance(activity);
mInflater = LayoutInflater.from(SharingListView.this);
Resources resources = activity.getResources();
mDrawableIncomingFailed = resources.getDrawable(R.drawable.ri_incoming_call_failed);
mDrawableOutgoingFailed = resources.getDrawable(R.drawable.ri_outgoing_call_failed);
mDrawableIncoming = resources.getDrawable(R.drawable.ri_incoming_call);
mDrawableOutgoing = resources.getDrawable(R.drawable.ri_outgoing_call);
}
@Override
public int getItemViewType(int position) {
return 1;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.sharing_log_list, parent, false);
view.setTag(new ViewHolder(view, cursor));
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
// Set contact number
String phone = cursor.getString(holder.getColumnContactIdx());
ContactId contact = ContactUtil.formatContact(phone);
holder.getContactView().setText(mRcsContactUtil.getDisplayName(contact));
// Set the date/time field by mixing relative and absolute times
long date = cursor.getLong(holder.getColumnTimestampIdx());
holder.getDateView().setText(
DateUtils.getRelativeTimeSpanString(date, System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE));
// Set the status text and destination icon
int status = cursor.getInt(holder.getColumnStatusIdx());
Direction dir = Direction.valueOf(cursor.getInt(holder.getColumnDirectionIdx()));
switch (dir) {
case INCOMING:
if (status != State.FAILED.toInt()
&& status != VideoSharing.State.FAILED.toInt()
&& status != GeolocSharing.State.FAILED.toInt()) {
holder.getDirectionIconImageView().setImageDrawable(mDrawableIncoming);
} else {
holder.getDirectionIconImageView()
.setImageDrawable(mDrawableIncomingFailed);
}
break;
case OUTGOING:
if (status != State.FAILED.toInt()
&& status != VideoSharing.State.FAILED.toInt()
&& status != GeolocSharing.State.FAILED.toInt()) {
holder.getDirectionIconImageView().setImageDrawable(mDrawableOutgoing);
} else {
holder.getDirectionIconImageView()
.setImageDrawable(mDrawableOutgoingFailed);
}
break;
}
int providerId = cursor.getInt(holder.getColumnProviderIdIdx());
switch (providerId) {
case ImageSharingLog.HISTORYLOG_MEMBER_ID:
holder.getTypeView().setText(R.string.label_sharing_log_menu_ish);
String filename = cursor.getString(holder.getColumnFilenameIdx());
holder.getDescriptionView().setText(
TextUtils.isEmpty(filename) ? "" : truncateString(filename,
MAX_LENGTH_DESCRIPTION));
break;
case VideoSharingLog.HISTORYLOG_MEMBER_ID:
holder.getTypeView().setText(R.string.label_sharing_log_menu_vsh);
long duration = cursor.getLong(holder.getColumnDurationIdx());
holder.getDescriptionView().setText(
getString(R.string.value_log_duration,
DateUtils.formatElapsedTime(duration / 1000L)));
break;
case GeolocSharingLog.HISTORYLOG_MEMBER_ID:
holder.getTypeView().setText(R.string.label_sharing_log_menu_gsh);
String content = cursor.getString(holder.getColumnContentIdx());
holder.getDescriptionView().setText(
TextUtils.isEmpty(content) ? "" : truncateString(content,
MAX_LENGTH_DESCRIPTION));
default:
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.history_log_sharing);
initialize();
}
@Override
protected void onResume() {
super.onResume();
queryHistoryLogAndRefreshView();
}
private void initialize() {
final Runnable updateUi = new Runnable() {
@Override
public void run() {
queryHistoryLogAndRefreshView();
}
};
mImageSharingService = getImageSharingApi();
mImageSharingListener = new ImageSharingListener() {
@Override
public void onStateChanged(ContactId contact, String sharingId, State state,
ReasonCode reasonCode) {
}
@Override
public void onProgressUpdate(ContactId contact, String sharingId, long currentSize,
long totalSize) {
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
Log.d(SharingListView.LOGTAG, "onDeleted contact=" + contact + " for sharing IDs="
+ Arrays.toString(sharingIds.toArray()));
mHandler.post(updateUi);
}
};
mVideoSharingService = getVideoSharingApi();
mVideoSharingListener = new VideoSharingListener() {
@Override
public void onStateChanged(ContactId contact, String sharingId,
VideoSharing.State state, VideoSharing.ReasonCode reasonCode) {
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
Log.d(SharingListView.LOGTAG, "onDeleted contact=" + contact + " for sharing IDs="
+ Arrays.toString(sharingIds.toArray()));
mHandler.post(updateUi);
}
};
mGeolocSharingService = getGeolocSharingApi();
mGeolocSharingListener = new GeolocSharingListener() {
@Override
public void onStateChanged(ContactId contact, String sharingId,
GeolocSharing.State state, GeolocSharing.ReasonCode reasonCode) {
}
@Override
public void onProgressUpdate(ContactId contact, String sharingId, long currentSize,
long totalSize) {
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
Log.d(SharingListView.LOGTAG, "onDeleted contact=" + contact + " for sharing IDs="
+ Arrays.toString(sharingIds.toArray()));
mHandler.post(updateUi);
}
};
mAdapter = new SharingLogAdapter(this);
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setEmptyView(findViewById(android.R.id.empty));
listView.setAdapter(mAdapter);
registerForContextMenu(listView);
mSpinner = (Spinner) findViewById(R.id.contact);
mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this,
getString(R.string.label_sharing_log_contact_spinner_default_value)));
mSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View v, int position, long id) {
/* Call when an item is selected so also at the start of the activity to initialize */
queryHistoryLogAndRefreshView();
}
@Override
public void onNothingSelected(AdapterView> parent) {
}
});
listView.setOnItemClickListener(getOnItemClickListener());
sProviders.put(ImageSharingLog.HISTORYLOG_MEMBER_ID,
getString(R.string.label_sharing_log_menu_ish));
sProviders.put(VideoSharingLog.HISTORYLOG_MEMBER_ID,
getString(R.string.label_sharing_log_menu_vsh));
sProviders.put(GeolocSharingLog.HISTORYLOG_MEMBER_ID,
getString(R.string.label_sharing_log_menu_gsh));
setProviders(sProviders);
setCursorLoader(true);
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
if (mImageSharingService != null && mImageSharingListenerSet) {
mImageSharingService.removeEventListener(mImageSharingListener);
}
if (mVideoSharingService != null && mVideoSharingListenerSet) {
mVideoSharingService.removeEventListener(mVideoSharingListener);
}
if (mGeolocSharingService != null && mGeolocSharingListenerSet) {
mGeolocSharingService.removeEventListener(mGeolocSharingListener);
}
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
new MenuInflater(getApplicationContext()).inflate(R.menu.menu_historylog, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_filter:
Builder builder = new Builder(this);
builder.setTitle(R.string.title_sharing_log_dialog_filter_logs);
builder.setMultiChoiceItems(
mFilterMenuItems.toArray(new CharSequence[mFilterMenuItems.size()]),
mCheckedProviders, new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
mCheckedProviders[which] = isChecked;
}
});
builder.setPositiveButton(R.string.label_ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mFilterAlertDialog.dismiss();
queryHistoryLogAndRefreshView();
}
});
builder.setNegativeButton(R.string.label_cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mFilterAlertDialog.dismiss();
}
});
mFilterAlertDialog = builder.show();
registerDialog(mFilterAlertDialog);
break;
case R.id.menu_delete:
if (!isServiceConnected(RcsServiceName.IMAGE_SHARING, RcsServiceName.VIDEO_SHARING,
RcsServiceName.GEOLOC_SHARING)) {
showMessage(R.string.label_service_not_available);
break;
}
Log.d(LOGTAG, "delete all image sharing sessions");
try {
if (!mImageSharingListenerSet) {
mImageSharingService.addEventListener(mImageSharingListener);
mImageSharingListenerSet = true;
}
mImageSharingService.deleteImageSharings();
if (!mVideoSharingListenerSet) {
mVideoSharingService.addEventListener(mVideoSharingListener);
mVideoSharingListenerSet = true;
}
mVideoSharingService.deleteVideoSharings();
if (!mGeolocSharingListenerSet) {
mGeolocSharingService.addEventListener(mGeolocSharingListener);
mGeolocSharingListenerSet = true;
}
mGeolocSharingService.deleteGeolocSharings();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
break;
}
return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.menu_log_sharing_item, menu);
menu.findItem(R.id.menu_sharing_display).setVisible(false);
// Get the list item position
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
Cursor cursor = (Cursor) mAdapter.getItem(info.position);
Direction dir = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(HistoryLog.DIRECTION)));
String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.MIME_TYPE));
if (mimeType != null && Utils.isImageType(mimeType)) {
if (Direction.INCOMING == dir) {
Long transferred = cursor.getLong(cursor
.getColumnIndexOrThrow(HistoryLog.TRANSFERRED));
Long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(HistoryLog.FILESIZE));
if (fileSize.equals(transferred)) {
menu.findItem(R.id.menu_sharing_display).setVisible(true);
return;
}
return;
}
menu.findItem(R.id.menu_sharing_display).setVisible(true);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
Cursor cursor = (Cursor) mAdapter.getItem(info.position);
int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID));
String sharingId = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID));
if (LogUtils.isActive) {
Log.d(LOGTAG, "onContextItemSelected sharing ID=".concat(sharingId));
}
try {
switch (item.getItemId()) {
case R.id.menu_sharing_delete:
Log.d(LOGTAG, "Delete sharing ID=".concat(sharingId));
switch (providerId) {
case ImageSharingLog.HISTORYLOG_MEMBER_ID:
if (!mImageSharingListenerSet) {
mImageSharingService.addEventListener(mImageSharingListener);
mImageSharingListenerSet = true;
}
mImageSharingService.deleteImageSharing(sharingId);
return true;
case VideoSharingLog.HISTORYLOG_MEMBER_ID:
if (!mVideoSharingListenerSet) {
mVideoSharingService.addEventListener(mVideoSharingListener);
mVideoSharingListenerSet = true;
}
mVideoSharingService.deleteVideoSharing(sharingId);
return true;
case GeolocSharingLog.HISTORYLOG_MEMBER_ID:
if (!mGeolocSharingListenerSet) {
mGeolocSharingService.addEventListener(mGeolocSharingListener);
mGeolocSharingListenerSet = true;
}
mGeolocSharingService.deleteGeolocSharing(sharingId);
return true;
default:
return true;
}
case R.id.menu_sharing_display:
Utils.showPicture(this, Uri.parse(cursor.getString(cursor
.getColumnIndexOrThrow(HistoryLog.CONTENT))));
return true;
default:
return super.onContextItemSelected(item);
}
} catch (RcsServiceNotAvailableException e) {
showMessage(R.string.label_service_not_available);
return true;
} catch (RcsGenericException e2) {
showExceptionThenExit(e2);
return true;
}
}
private OnItemClickListener getOnItemClickListener() {
return new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View v, int pos, long id) {
Cursor cursor = (Cursor) mAdapter.getItem(pos);
int providerId = cursor
.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID));
String sharingId = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID));
switch (providerId) {
case ImageSharingLog.HISTORYLOG_MEMBER_ID:
ImageSharingLogView.startActivity(SharingListView.this, sharingId);
break;
case VideoSharingLog.HISTORYLOG_MEMBER_ID:
VideoSharingLogView.startActivity(SharingListView.this, sharingId);
break;
case GeolocSharingLog.HISTORYLOG_MEMBER_ID:
GeolocSharingLogView.startActivity(SharingListView.this, sharingId);
break;
default:
}
}
};
}
private String getSelectedContact() {
MatrixCursor cursor = (MatrixCursor) mSpinner.getSelectedItem();
String contact = cursor.getString(1);
if (getString(R.string.label_history_log_contact_spinner_default_value).equals(contact)) {
return contact;
}
return ContactUtil.formatContact(contact).toString();
}
/**
* A method called to query the history log and refresh view
*/
private void queryHistoryLogAndRefreshView() {
Cursor cursor = null;
List selectedProviderIds = getSelectedProviderIds();
String contact = getSelectedContact();
if (!selectedProviderIds.isEmpty()) {
if (getString(R.string.label_sharing_log_contact_spinner_default_value).equals(contact)) {
/*
* No contact is selected
*/
Uri uri = createSharingLogUri(selectedProviderIds);
cursor = getContentResolver().query(uri, PROJECTION, null, null, SORT_BY);
} else {
Uri uri = createSharingLogUri(selectedProviderIds);
cursor = getContentResolver().query(uri, PROJECTION, WHERE_CLAUSE_WITH_CONTACT,
new String[] {
contact
}, SORT_BY);
}
}
mAdapter.changeCursor(cursor);
}
/**
* Truncate a string
*
* @param in string to truncate
* @param maxLength maximum length
* @return truncated string
*/
private String truncateString(String in, int maxLength) {
if (in.length() > maxLength) {
in = in.substring(0, maxLength).concat("...");
}
return "\"" + in + "\"";
}
/**
* Sets the providers
*
* @param providers ordered map of providers IDs associated with their name
*/
private void setProviders(TreeMap providers) {
mProviderIds = new ArrayList<>();
mFilterMenuItems = new ArrayList<>();
for (Entry entry : providers.entrySet()) {
mFilterMenuItems.add(entry.getValue());
mProviderIds.add(entry.getKey());
}
/* Upon setting, all providers are checked True */
mCheckedProviders = new boolean[providers.size()];
for (int i = 0; i < mCheckedProviders.length; i++) {
mCheckedProviders[i] = true;
}
}
/**
* Gets the list of selected providers IDs
*
* @return the list of selected providers IDs
*/
private List getSelectedProviderIds() {
List providers = new ArrayList<>();
for (int i = 0; i < mCheckedProviders.length; i++) {
if (mCheckedProviders[i]) {
providers.add(mProviderIds.get(i));
}
}
return providers;
}
/**
* Create History URI from list of provider IDs
*
* @param providerIds list of provider IDs
* @return Uri
*/
private Uri createSharingLogUri(List providerIds) {
HistoryUriBuilder uriBuilder = new HistoryUriBuilder(HistoryLog.CONTENT_URI);
for (Integer providerId : providerIds) {
uriBuilder.appendProvider(providerId);
}
return uriBuilder.build();
}
private void setCursorLoader(boolean firstLoad) {
if (firstLoad) {
// Initialize the Loader with id '1' and callbacks 'mCallbacks'.
getSupportLoaderManager().initLoader(LOADER_ID, null, this);
} else {
// We switched from one contact to another: reload history since.
getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreateLoader " + id);
}
List selectedProviderIds = getSelectedProviderIds();
String contact = getSelectedContact();
if (!selectedProviderIds.isEmpty()) {
Uri uri = createSharingLogUri(selectedProviderIds);
// Create a new CursorLoader with the following query parameters.
if (getString(R.string.label_sharing_log_contact_spinner_default_value).equals(contact)) {
// No contact is selected
return new CursorLoader(this, uri, PROJECTION, null, null, SORT_BY);
}
return new CursorLoader(this, uri, PROJECTION, WHERE_CLAUSE_WITH_CONTACT, new String[] {
contact
}, SORT_BY);
}
return null;
}
@Override
public void onLoadFinished(Loader loader, Cursor cursor) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onLoadFinished " + loader.getId());
}
// A switch-case is useful when dealing with multiple Loaders/IDs
switch (loader.getId()) {
case LOADER_ID:
// The asynchronous load is complete and the data
// is now available for use. Only now can we associate
// the queried Cursor with the CursorAdapter.
mAdapter.swapCursor(cursor);
break;
}
// The listview now displays the queried data.
}
@Override
public void onLoaderReset(Loader loader) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onLoaderReset " + loader.getId());
}
// For whatever reason, the Loader's data is now unavailable.
// Remove any references to the old data by replacing it with a null
// Cursor.
mAdapter.swapCursor(null);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/TestSharingApi.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.sharing.geoloc.InitiateGeolocSharing;
import com.gsma.rcs.ri.sharing.image.InitiateImageSharing;
import com.gsma.rcs.ri.sharing.video.OutgoingVideoSharing;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* Sharing API
*
* @author Jean-Marc AUFFRET
*/
public class TestSharingApi extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Set items
// @formatter:off
String[] items = {
getString(R.string.menu_initiate_image_sharing),
getString(R.string.menu_initiate_video_sharing),
getString(R.string.menu_initiate_geoloc_sharing),
getString(R.string.menu_log_sharing)
};
// @formatter:on
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startActivity(new Intent(this, InitiateImageSharing.class));
break;
case 1:
startActivity(new Intent(this, OutgoingVideoSharing.class));
break;
case 2:
startActivity(new Intent(this, InitiateGeolocSharing.class));
break;
case 3:
startActivity(new Intent(this, SharingListView.class));
break;
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/geoloc/GeolocSharingDAO.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.geoloc;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharing;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharing.ReasonCode;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharing.State;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingLog;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
/**
* Geolocation sharing Data Object
*
* @author Philippe LEMORDANT
*/
public class GeolocSharingDAO {
private final String mSharingId;
private final ContactId mContact;
private final State mState;
private final ReasonCode mReasonCode;
private final Direction mDirection;
private final String mMimeType;
private final String mContent;
private final long mTimestamp;
private static ContentResolver sContentResolver;
private GeolocSharingDAO(String sharingId, ContactId contact, State state,
ReasonCode reasonCode, Direction dir, String mimeType, String content, long timestamp) {
mSharingId = sharingId;
mContact = contact;
mState = state;
mReasonCode = reasonCode;
mDirection = dir;
mMimeType = mimeType;
mContent = content;
mTimestamp = timestamp;
}
public String getSharingId() {
return mSharingId;
}
public GeolocSharing.State getState() {
return mState;
}
public String getContent() {
return mContent;
}
public ContactId getContact() {
return mContact;
}
public String getMimeType() {
return mMimeType;
}
public Direction getDirection() {
return mDirection;
}
public long getTimestamp() {
return mTimestamp;
}
public ReasonCode getReasonCode() {
return mReasonCode;
}
/**
* Gets instance of chat message from RCS provider
*
* @param ctx the context
* @param sharingId the sharing ID
* @return instance or null if entry not found
*/
public static GeolocSharingDAO getGeolocSharing(Context ctx, String sharingId) {
if (sContentResolver == null) {
sContentResolver = ctx.getContentResolver();
}
Cursor cursor = null;
try {
cursor = sContentResolver.query(
Uri.withAppendedPath(GeolocSharingLog.CONTENT_URI, sharingId), null, null,
null, null);
if (cursor == null) {
throw new SQLException("Query failed!");
}
if (!cursor.moveToFirst()) {
return null;
}
String contact = cursor.getString(cursor
.getColumnIndexOrThrow(GeolocSharingLog.CONTACT));
ContactId contactId = null;
if (contact != null) {
contactId = ContactUtil.formatContact(contact);
}
String mimeType = cursor.getString(cursor
.getColumnIndexOrThrow(GeolocSharingLog.MIME_TYPE));
String content = cursor.getString(cursor
.getColumnIndexOrThrow(GeolocSharingLog.CONTENT));
State state = State.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(GeolocSharingLog.STATE)));
Direction dir = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(GeolocSharingLog.DIRECTION)));
long timestamp = cursor.getLong(cursor
.getColumnIndexOrThrow(GeolocSharingLog.TIMESTAMP));
ReasonCode reasonCode = ReasonCode.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(GeolocSharingLog.REASON_CODE)));
return new GeolocSharingDAO(sharingId, contactId, state, reasonCode, dir, mimeType,
content, timestamp);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/geoloc/GeolocSharingIntentService.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.geoloc;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingIntent;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* Geoloc sharing intent service
*
* @author YPLO6403
*/
public class GeolocSharingIntentService extends IntentService {
private static final String LOGTAG = LogUtils.getTag(GeolocSharingIntentService.class
.getSimpleName());
static final String BUNDLE_GSH_ID = "bundle_gsh";
/**
* Creates an IntentService.
*/
public GeolocSharingIntentService() {
super("GeolocSharingIntentService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
/*
* We want this service to stop running if forced stop so return not sticky.
*/
return START_NOT_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
String action;
if ((action = intent.getAction()) == null) {
return;
}
/* Check action from incoming intent */
if (!GeolocSharingIntent.ACTION_NEW_INVITATION.equals(action)) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Unknown action ".concat(action));
}
return;
}
/* Gets data from the incoming Intent */
String sharingId = intent.getStringExtra(GeolocSharingIntent.EXTRA_SHARING_ID);
if (sharingId == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read sharing ID");
}
return;
}
GeolocSharingDAO gshSharing = GeolocSharingDAO.getGeolocSharing(this, sharingId);
if (gshSharing == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot get geoloc sharing for ".concat(sharingId));
}
return;
}
ContactId contact = gshSharing.getContact();
if (contact == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot get contact sharing for ".concat(sharingId));
}
return;
}
/* Save contact into intent */
Bundle bundle = new Bundle();
bundle.putParcelable(BUNDLE_GSH_ID, contact);
intent.putExtras(bundle);
/* Display invitation notification */
addGeolocSharingInvitationNotification(intent, contact);
}
/**
* Add geoloc share notification
*
* @param invitation intent
*/
private void addGeolocSharingInvitationNotification(Intent invitation, ContactId contact) {
/* Create pending intent */
Intent intent = new Intent(invitation);
intent.setClass(this, ReceiveGeolocSharing.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*
* If the PendingIntent has the same operation, action, data, categories, components, and
* flags it will be replaced. Invitation should be notified individually so we use a random
* generator to provide a unique request code and reuse it for the notification.
*/
int uniqueId = Utils.getUniqueIdForPendingIntent();
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_ONE_SHOT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
String title = getString(R.string.title_recv_geoloc_sharing);
/* Create notification */
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(contentIntent);
notif.setSmallIcon(R.drawable.ri_notif_csh_icon);
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(true);
notif.setOnlyAlertOnce(true);
notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
notif.setDefaults(Notification.DEFAULT_VIBRATE);
notif.setContentTitle(title);
notif.setContentText(getString(R.string.label_from_args, displayName));
/* Send notification */
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(uniqueId, notif.build());
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/geoloc/GeolocSharingInvitationReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.geoloc;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Geoloc sharing invitation receiver
*
* @author vfml3370
*/
public class GeolocSharingInvitationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, GeolocSharingIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/geoloc/GeolocSharingLogView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.geoloc;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* A class to view the persisted information for geolocation sharing.
* Created by Philippe LEMORDANT.
*/
public class GeolocSharingLogView extends RcsActivity {
private static final String EXTRA_SHARING_ID = "id";
private String mSharingId;
private TextView mTxtViewContact;
private TextView mTxtViewContent;
private TextView mTxtViewDate;
private TextView mTxtViewDir;
private TextView mTxtViewMime;
private TextView mTxtViewReason;
private TextView mTxtViewState;
private SimpleDateFormat sDateFormat;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sharing_log_geoloc_item);
mSharingId = getIntent().getStringExtra(EXTRA_SHARING_ID);
initialize();
}
private void initialize() {
mTxtViewContact = (TextView) findViewById(R.id.history_log_item_contact);
mTxtViewState = (TextView) findViewById(R.id.history_log_item_state);
mTxtViewReason = (TextView) findViewById(R.id.history_log_item_reason);
mTxtViewDir = (TextView) findViewById(R.id.history_log_item_direction);
mTxtViewDate = (TextView) findViewById(R.id.history_log_item_date);
mTxtViewMime = (TextView) findViewById(R.id.history_log_item_mime);
mTxtViewContent = (TextView) findViewById(R.id.history_log_item_content);
}
private String getDateFromDb(long timestamp) {
if (0 == timestamp) {
return "";
}
if (sDateFormat == null) {
sDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
}
return sDateFormat.format(new Date(timestamp));
}
@Override
protected void onResume() {
super.onResume();
GeolocSharingDAO dao = GeolocSharingDAO.getGeolocSharing(this, mSharingId);
if (dao == null) {
showMessageThenExit(R.string.error_item_not_found);
return;
}
mTxtViewContact.setText(dao.getContact().toString());
mTxtViewState.setText(RiApplication.sGeolocSharingStates[dao.getState().toInt()]);
mTxtViewReason.setText(RiApplication.sGeolocReasonCodes[dao.getReasonCode().toInt()]);
mTxtViewDir.setText(RiApplication.getDirection(dao.getDirection()));
mTxtViewDate.setText(getDateFromDb(dao.getTimestamp()));
mTxtViewMime.setText(dao.getMimeType());
mTxtViewContent.setText(String.valueOf(dao.getContent()));
}
/**
* Start activity to view details of geolocation sharing record
*
* @param context the context
* @param sharingId the sharing ID
*/
public static void startActivity(Context context, String sharingId) {
Intent intent = new Intent(context, GeolocSharingLogView.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EXTRA_SHARING_ID, sharingId);
context.startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/geoloc/InitiateGeolocSharing.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.geoloc;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.messaging.geoloc.EditGeoloc;
import com.gsma.rcs.ri.messaging.geoloc.ShowGeoloc;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharing;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingListener;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingService;
import android.Manifest;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.Set;
/**
* Initiate geoloc sharing
*
* @author vfml3370
* @author Philippe LEMORDANT
*/
public class InitiateGeolocSharing extends RcsActivity {
/**
* Activity result constants
*/
private final static int SELECT_GEOLOCATION = 0;
/**
* UI handler
*/
private final Handler mHandler = new Handler();
private Geoloc mGeoloc;
private GeolocSharing mGeolocSharing;
private String mSharingId;
private static final String LOGTAG = LogUtils.getTag(InitiateGeolocSharing.class.getName());
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
private GeolocSharingListener mListener;
private GeolocSharingService mGeolocSharingService;
private Button mInviteBtn;
private ProgressBar mProgressBar;
private Button mSelectBtn;
private TextView mStatusView;
private Button mDialBtn;
private TextView mPositionView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.geoloc_sharing_initiate);
initialize();
if (!isServiceConnected(RcsServiceName.GEOLOC_SHARING)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.GEOLOC_SHARING);
try {
mGeolocSharingService.addEventListener(mListener);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mGeolocSharingService != null && isServiceConnected(RcsServiceName.GEOLOC_SHARING)) {
try {
mGeolocSharingService.removeEventListener(mListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case SELECT_GEOLOCATION:
/* Get selected geoloc */
mGeoloc = data.getParcelableExtra(EditGeoloc.EXTRA_GEOLOC);
mPositionView.setText(mGeoloc.toString());
/* Enable invite button */
mInviteBtn.setEnabled(true);
break;
}
}
private void updateProgressBar(long currentSize, long totalSize) {
double position = 0.0d;
if (totalSize != 0) {
mStatusView.setText(Utils.getProgressLabel(currentSize, totalSize));
position = (((double) currentSize) / ((double) totalSize)) * 100.0d;
} else {
mStatusView.setText("");
}
mProgressBar.setProgress((int) position);
}
private void quitSession() {
try {
if (mGeolocSharing != null && GeolocSharing.State.STARTED == mGeolocSharing.getState()) {
mGeolocSharing.abortSharing();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mGeolocSharing = null;
/* Exit activity */
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
quitSession();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_geoloc_sharing, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void initialize() {
mGeolocSharingService = getGeolocSharingApi();
mSpinner = (Spinner) findViewById(R.id.contact);
ContactListAdapter adapter = ContactListAdapter.createRcsContactListAdapter(this);
mSpinner.setAdapter(adapter);
OnClickListener btnInviteListener = new OnClickListener() {
public void onClick(View v) {
// Check if the service is available
try {
if (!mGeolocSharingService.isServiceRegistered()) {
showMessage(R.string.error_not_registered);
return;
}
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
ContactId contact = ContactUtil.formatContact(phoneNumber);
if (LogUtils.isActive) {
Log.d(LOGTAG, "share geoloc=" + mGeoloc + " contact=" + contact);
}
mGeolocSharing = mGeolocSharingService.shareGeoloc(contact, mGeoloc);
mSharingId = mGeolocSharing.getSharingId();
mSpinner.setEnabled(false);
mInviteBtn.setVisibility(View.INVISIBLE);
mSelectBtn.setVisibility(View.INVISIBLE);
mDialBtn.setVisibility(View.INVISIBLE);
} catch (RcsServiceNotAvailableException e) {
showMessage(R.string.label_service_not_available);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mInviteBtn = (Button) findViewById(R.id.invite_btn);
mInviteBtn.setOnClickListener(btnInviteListener);
mInviteBtn.setEnabled(false);
OnClickListener btnSelectListener = new OnClickListener() {
public void onClick(View v) {
// Start a new activity to send a geolocation
startActivityForResult(new Intent(InitiateGeolocSharing.this, EditGeoloc.class),
SELECT_GEOLOCATION);
}
};
mSelectBtn = (Button) findViewById(R.id.select_btn);
mSelectBtn.setOnClickListener(btnSelectListener);
mSelectBtn.setEnabled(false);
OnClickListener btnDialListener = new OnClickListener() {
public void onClick(View v) {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
// Initiate a GSM call before to be able to share content
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:".concat(phoneNumber)));
if (ActivityCompat.checkSelfPermission(InitiateGeolocSharing.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
startActivity(intent);
}
};
mDialBtn = (Button) findViewById(R.id.dial_btn);
mDialBtn.setOnClickListener(btnDialListener);
mDialBtn.setEnabled(false);
mStatusView = (TextView) findViewById(R.id.progress_status);
mPositionView = (TextView) findViewById(R.id.position);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
updateProgressBar(0, 0);
mPositionView.setText("");
if (adapter == null || adapter.getCount() != 0) {
mDialBtn.setEnabled(true);
mSelectBtn.setEnabled(true);
}
mListener = new GeolocSharingListener() {
@Override
public void onProgressUpdate(ContactId contact, String sharingId,
final long currentSize, final long totalSize) {
/* Discard event if not for current sharingId */
if (InitiateGeolocSharing.this.mSharingId == null
|| !InitiateGeolocSharing.this.mSharingId.equals(sharingId)) {
return;
}
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(final ContactId contact, String sharingId,
final GeolocSharing.State state, GeolocSharing.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " sharingId=" + sharingId
+ " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current sharingId */
if (InitiateGeolocSharing.this.mSharingId == null
|| !InitiateGeolocSharing.this.mSharingId.equals(sharingId)) {
return;
}
final String _state = RiApplication.sGeolocSharingStates[state.toInt()];
final String _reasonCode = RiApplication.sGeolocReasonCodes[reasonCode.toInt()];
mHandler.post(new Runnable() {
public void run() {
TextView statusView = (TextView) findViewById(R.id.progress_status);
switch (state) {
case STARTED:
// Display session status
statusView.setText(_state);
break;
case ABORTED:
showMessageThenExit(getString(R.string.label_sharing_aborted,
_reasonCode));
break;
case REJECTED:
showMessageThenExit(getString(R.string.label_sharing_rejected,
_reasonCode));
break;
case FAILED:
showMessageThenExit(getString(R.string.label_sharing_failed,
_reasonCode));
break;
case TRANSFERRED:
/* Display transfer progress */
statusView.setText(_state);
/* Make sure progress bar is at the end */
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setProgress(progressBar.getMax());
ShowGeoloc.ShowGeolocForContact(InitiateGeolocSharing.this,
contact, mGeoloc);
break;
default:
statusView.setText(getString(R.string.label_gsh_state_changed,
_state, _reasonCode));
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " sharingIds=" + sharingIds);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/geoloc/ReceiveGeolocSharing.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.geoloc;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.messaging.geoloc.ShowGeoloc;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.Geoloc;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharing;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingIntent;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingListener;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingService;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.Set;
/**
* Receive geoloc sharing
*
* @author vfml3370
* @author yplo6403
*/
public class ReceiveGeolocSharing extends RcsActivity {
/**
* UI handler
*/
private final Handler mHandler = new Handler();
private String mSharingId;
private ContactId mRemoteContact;
/**
* Geoloc sharing session
*/
private GeolocSharing mGeolocSharing;
private Geoloc mGeoloc;
private static final String LOGTAG = LogUtils.getTag(ReceiveGeolocSharing.class.getName());
private GeolocSharingListener mListener;
private OnClickListener mAcceptBtnListener;
private OnClickListener mDeclineBtnListener;
private ProgressBar mProgressBar;
private boolean mSessionListenerSet;
private TextView mStatusView;
private TextView mPositionView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.geoloc_sharing_receive);
initialize();
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.GEOLOC_SHARING, RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.GEOLOC_SHARING, RcsServiceName.CONTACT);
processIntent(getIntent());
}
@Override
public void onDestroy() {
super.onDestroy();
if (!mSessionListenerSet || !isServiceConnected(RcsServiceName.GEOLOC_SHARING)) {
return;
}
try {
getGeolocSharingApi().removeEventListener(mListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
processIntent(intent);
}
private void processIntent(Intent invitation) {
mSharingId = invitation.getStringExtra(GeolocSharingIntent.EXTRA_SHARING_ID);
mRemoteContact = invitation.getParcelableExtra(GeolocSharingIntentService.BUNDLE_GSH_ID);
initiateGeolocSharing();
}
private void initiateGeolocSharing() {
GeolocSharingService gshApi = getGeolocSharingApi();
try {
// Get the geoloc sharing
mGeolocSharing = gshApi.getGeolocSharing(mSharingId);
if (mGeolocSharing == null) {
// Session not found or expired
showMessageThenExit(R.string.label_session_not_found);
return;
}
mSessionListenerSet = true;
gshApi.addEventListener(mListener);
/* Display sharing infos */
String from = RcsContactUtil.getInstance(this).getDisplayName(mRemoteContact);
TextView fromTextView = (TextView) findViewById(R.id.contact);
fromTextView.setText(from);
mPositionView = (TextView) findViewById(R.id.position);
mProgressBar.setProgress(0);
mPositionView.setText("");
/* Display accept/reject dialog */
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_geoloc_sharing);
builder.setMessage(getString(R.string.label_from_args, from));
builder.setCancelable(false);
builder.setIcon(R.drawable.ri_notif_gsh_icon);
builder.setPositiveButton(R.string.label_accept, mAcceptBtnListener);
builder.setNegativeButton(R.string.label_decline, mDeclineBtnListener);
registerDialog(builder.show());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void acceptInvitation() {
try {
mGeolocSharing.acceptInvitation();
} catch (RcsGenericException e) {
showExceptionThenExit(e);
}
}
private void rejectInvitation() {
try {
mGeolocSharing.rejectInvitation();
} catch (RcsGenericException e) {
showExceptionThenExit(e);
}
}
private void updateProgressBar(long currentSize, long totalSize) {
mStatusView.setText(Utils.getProgressLabel(currentSize, totalSize));
double position = ((double) currentSize / (double) totalSize) * 100.0d;
mProgressBar.setProgress((int) position);
}
private void quitSession() {
try {
if (mGeolocSharing != null && GeolocSharing.State.STARTED == mGeolocSharing.getState()) {
mGeolocSharing.abortSharing();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mGeolocSharing = null;
/* Exit activity */
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
quitSession();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_image_sharing, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void initialize() {
mStatusView = (TextView) findViewById(R.id.progress_status);
mListener = new GeolocSharingListener() {
@Override
public void onProgressUpdate(ContactId contact, String sharingId,
final long currentSize, final long totalSize) {
/* Discard event if not for current sharingId */
if (mSharingId == null || !mSharingId.equals(sharingId)) {
return;
}
mHandler.post(new Runnable() {
public void run() {
updateProgressBar(currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(final ContactId contact, final String sharingId,
final GeolocSharing.State state, GeolocSharing.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact.toString() + " sharingId="
+ sharingId + " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current sharingId */
if (mSharingId == null || !mSharingId.equals(sharingId)) {
return;
}
final String _reasonCode = RiApplication.sGeolocReasonCodes[reasonCode.toInt()];
final String _state = RiApplication.sGeolocSharingStates[state.toInt()];
mHandler.post(new Runnable() {
public void run() {
switch (state) {
case ABORTED:
String msg = getString(R.string.label_sharing_aborted, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case FAILED:
msg = getString(R.string.label_sharing_failed, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case TRANSFERRED:
/* Display transfer progress */
mStatusView.setText(_state);
/* Make sure progress bar is at the end */
mProgressBar.setProgress(mProgressBar.getMax());
/* Show the shared geoloc */
try {
mGeoloc = mGeolocSharing.getGeoloc();
mPositionView.setText(mGeoloc.toString());
ShowGeoloc.ShowGeolocForContact(ReceiveGeolocSharing.this,
contact, mGeoloc);
} catch (RcsServiceException e) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged failed to get geoloc for "
.concat(sharingId));
}
}
break;
default:
mStatusView.setText(_state);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged ".concat(getString(
R.string.label_gsh_state_changed, _state, _reasonCode)));
}
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " sharingIds=" + sharingIds);
}
}
};
mAcceptBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
acceptInvitation();
}
};
mDeclineBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
rejectInvitation();
finish();
}
};
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/image/ImageSharingDAO.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.image;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.image.ImageSharing;
import com.gsma.services.rcs.sharing.image.ImageSharingLog;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Image Sharing Data Object
*
* @author YPLO6403
*/
public class ImageSharingDAO implements Parcelable {
private final String mSharingId;
private final ContactId mContact;
private final Uri mFile;
private final String mFilename;
private final String mMimeType;
private final ImageSharing.State mState;
private final Direction mDirection;
private final long mTimestamp;
private final long mSizeTransferred;
private final long mSize;
private final ImageSharing.ReasonCode mReasonCode;
private static ContentResolver sContentResolver;
private ImageSharingDAO(String sharingId, ContactId contact, Uri file, String filename,
String mimeType, ImageSharing.State state, Direction direction, long timestamp,
long sizeTransferred, long size, ImageSharing.ReasonCode reasonCode) {
mSharingId = sharingId;
mContact = contact;
mFile = file;
mFilename = filename;
mMimeType = mimeType;
mState = state;
mDirection = direction;
mTimestamp = timestamp;
mSizeTransferred = sizeTransferred;
mSize = size;
mReasonCode = reasonCode;
}
/**
* Constructor
*
* @param source Parcelable source
*/
public ImageSharingDAO(Parcel source) {
mSharingId = source.readString();
boolean containsContactId = source.readInt() != 0;
if (containsContactId) {
mContact = ContactId.CREATOR.createFromParcel(source);
} else {
mContact = null;
}
boolean containsFile = source.readInt() != 0;
if (containsFile) {
mFile = Uri.parse(source.readString());
} else {
mFile = null;
}
mFilename = source.readString();
mMimeType = source.readString();
mState = ImageSharing.State.valueOf(source.readInt());
mDirection = Direction.valueOf(source.readInt());
mTimestamp = source.readLong();
mSizeTransferred = source.readLong();
mSize = source.readLong();
mReasonCode = ImageSharing.ReasonCode.valueOf(source.readInt());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mSharingId);
if (mContact != null) {
dest.writeInt(1);
mContact.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
if (mFile != null) {
dest.writeInt(1);
dest.writeString(mFile.toString());
} else {
dest.writeInt(0);
}
dest.writeString(mFilename);
dest.writeString(mMimeType);
dest.writeInt(mState.toInt());
dest.writeInt(mDirection.toInt());
dest.writeLong(mTimestamp);
dest.writeLong(mSizeTransferred);
dest.writeLong(mSize);
dest.writeInt(mReasonCode.toInt());
}
/**
* Gets state
*
* @return state
*/
public ImageSharing.State getState() {
return mState;
}
/**
* Gets transferred size
*
* @return size
*/
public long getSizeTransferred() {
return mSizeTransferred;
}
/**
* Gets sharing ID
*
* @return sharingId
*/
public String getSharingId() {
return mSharingId;
}
/**
* Gets remote contact
*
* @return contact
*/
public ContactId getContact() {
return mContact;
}
/**
* Gets file URI
*
* @return file URI
*/
public Uri getFile() {
return mFile;
}
/**
* Gets file name
*
* @return file name
*/
public String getFilename() {
return mFilename;
}
/**
* Gets mime type
*
* @return mime type
*/
public String getMimeType() {
return mMimeType;
}
public Direction getDirection() {
return mDirection;
}
/**
* Gets date of the sharing
*
* @return time stamp
*/
public long getTimestamp() {
return mTimestamp;
}
/**
* Gets size
*
* @return size
*/
public long getSize() {
return mSize;
}
/**
* Gets reason code
*
* @return reason code
*/
public ImageSharing.ReasonCode getReasonCode() {
return mReasonCode;
}
/**
* Gets instance of Image sharing from RCS provider
*
* @param ctx the context
* @param sharingId the sharing ID
* @return instance or null if entry not found
*/
public static ImageSharingDAO getImageSharingDAO(Context ctx, String sharingId) {
if (sContentResolver == null) {
sContentResolver = ctx.getContentResolver();
}
Cursor cursor = null;
try {
cursor = sContentResolver.query(
Uri.withAppendedPath(ImageSharingLog.CONTENT_URI, sharingId), null, null, null,
null);
if (cursor == null) {
throw new SQLException("Query failed!");
}
if (!cursor.moveToFirst()) {
throw new SQLException("Failed to find Image Sharing with ID: ".concat(sharingId));
}
String number = cursor.getString(cursor.getColumnIndexOrThrow(ImageSharingLog.CONTACT));
ContactId contact = ContactUtil.formatContact(number);
Uri file = Uri.parse(cursor.getString(cursor
.getColumnIndexOrThrow(ImageSharingLog.FILE)));
String filename = cursor.getString(cursor
.getColumnIndexOrThrow(ImageSharingLog.FILENAME));
String mimeType = cursor.getString(cursor
.getColumnIndexOrThrow(ImageSharingLog.MIME_TYPE));
ImageSharing.State state = ImageSharing.State.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ImageSharingLog.STATE)));
Direction dir = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ImageSharingLog.DIRECTION)));
long timestamp = cursor
.getLong(cursor.getColumnIndexOrThrow(ImageSharingLog.TIMESTAMP));
long sizeTransferred = cursor.getLong(cursor
.getColumnIndexOrThrow(ImageSharingLog.TRANSFERRED));
long size = cursor.getLong(cursor.getColumnIndexOrThrow(ImageSharingLog.FILESIZE));
ImageSharing.ReasonCode reason = ImageSharing.ReasonCode.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(ImageSharingLog.REASON_CODE)));
return new ImageSharingDAO(sharingId, contact, file, filename, mimeType, state, dir,
timestamp, sizeTransferred, size, reason);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
@Override
public String toString() {
return "ImageSharingDAO [sharingId=" + mSharingId + ", contact=" + mContact + ", file="
+ mFile + ", filename=" + mFilename + ", mimeType=" + mMimeType + ", state="
+ mState + ", size=" + mSize + "]";
}
@Override
public int describeContents() {
return 0;
}
/**
* public CREATOR field that generates instances of Parcelable class from a ImageSharingDAO.
*/
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public ImageSharingDAO createFromParcel(Parcel in) {
return new ImageSharingDAO(in);
}
@Override
public ImageSharingDAO[] newArray(int size) {
return new ImageSharingDAO[size];
}
};
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/image/ImageSharingIntentService.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.image;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.image.ImageSharing;
import com.gsma.services.rcs.sharing.image.ImageSharingIntent;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* Image sharing intent service
*
* @author YPLO6403
*/
public class ImageSharingIntentService extends IntentService {
private static final String LOGTAG = LogUtils.getTag(ImageSharingIntentService.class
.getSimpleName());
static final String BUNDLE_ISHDAO_ID = "ishdao";
/**
* Creates an IntentService.
*/
public ImageSharingIntentService() {
super("ImageSharingIntentService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// We want this service to stop running if forced stop
// so return not sticky.
return START_NOT_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
String action;
if ((action = intent.getAction()) == null) {
return;
}
// Check action from incoming intent
if (!ImageSharingIntent.ACTION_NEW_INVITATION.equals(action)) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Unknown action ".concat(action));
}
return;
}
// Gets data from the incoming Intent
String sharingId = intent.getStringExtra(ImageSharingIntent.EXTRA_SHARING_ID);
if (sharingId == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read sharing ID");
}
return;
}
// Get Image sharing from provider
ImageSharingDAO ishDao = ImageSharingDAO.getImageSharingDAO(this, sharingId);
if (ishDao == null) {
return;
}
// Save ImageSharingDAO into intent
Bundle bundle = new Bundle();
bundle.putParcelable(BUNDLE_ISHDAO_ID, ishDao);
intent.putExtras(bundle);
if (LogUtils.isActive) {
Log.d(LOGTAG, "ISH invitation ".concat(ishDao.toString()));
}
if (ImageSharing.State.INVITED == ishDao.getState()) {
addImageSharingInvitationNotification(intent, ishDao);
}
}
/**
* Add image share notification
*
* @param invitation Intent invitation
* @param ishDao the image sharing data object
*/
private void addImageSharingInvitationNotification(Intent invitation, ImageSharingDAO ishDao) {
ContactId contact = ishDao.getContact();
if (contact == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "addImageSharingInvitationNotification failed: cannot parse contact");
}
return;
}
/* Create pending intent */
Intent intent = new Intent(invitation);
intent.setClass(this, ReceiveImageSharing.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*
* If the PendingIntent has the same operation, action, data, categories, components, and
* flags it will be replaced. Invitation should be notified individually so we use a random
* generator to provide a unique request code and reuse it for the notification.
*/
int uniqueId = Utils.getUniqueIdForPendingIntent();
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_ONE_SHOT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
String title = getString(R.string.title_recv_image_sharing);
/* Create notification */
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(contentIntent);
notif.setSmallIcon(R.drawable.ri_notif_csh_icon);
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(true);
notif.setOnlyAlertOnce(true);
notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
notif.setDefaults(Notification.DEFAULT_VIBRATE);
notif.setContentTitle(title);
notif.setContentText(getString(R.string.label_from_args, displayName));
/* Send notification */
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(uniqueId, notif.build());
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/image/ImageSharingInvitationReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.image;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Image sharing invitation receiver
*
* @author YPLO6403
*/
public class ImageSharingInvitationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, ImageSharingIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/image/ImageSharingLogView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.image;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* A class to view the persisted information for image sharing
* Created by Philippe LEMORDANT.
*/
public class ImageSharingLogView extends RcsActivity {
private static final String EXTRA_SHARING_ID = "id";
private String mSharingId;
private TextView mTxtViewContact;
private TextView mTxtViewDate;
private TextView mTxtViewDir;
private TextView mTxtViewFileSize;
private TextView mTxtViewFilename;
private TextView mTxtViewMime;
private TextView mTxtViewReason;
private TextView mTxtViewState;
private TextView mTxtViewTransferred;
private TextView mTxtViewUri;
private SimpleDateFormat sDateFormat;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sharing_log_image_item);
mSharingId = getIntent().getStringExtra(EXTRA_SHARING_ID);
initialize();
}
private void initialize() {
mTxtViewContact = (TextView) findViewById(R.id.history_log_item_contact);
mTxtViewState = (TextView) findViewById(R.id.history_log_item_state);
mTxtViewReason = (TextView) findViewById(R.id.history_log_item_reason);
mTxtViewDir = (TextView) findViewById(R.id.history_log_item_direction);
mTxtViewDate = (TextView) findViewById(R.id.history_log_item_date);
mTxtViewMime = (TextView) findViewById(R.id.history_log_item_mime);
mTxtViewFilename = (TextView) findViewById(R.id.history_log_item_filename);
mTxtViewFileSize = (TextView) findViewById(R.id.history_log_item_size);
mTxtViewUri = (TextView) findViewById(R.id.history_log_item_uri);
mTxtViewTransferred = (TextView) findViewById(R.id.history_log_item_transferred);
}
private String getDateFromDb(long timestamp) {
if (0 == timestamp) {
return "";
}
if (sDateFormat == null) {
sDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
}
return sDateFormat.format(new Date(timestamp));
}
@Override
protected void onResume() {
super.onResume();
ImageSharingDAO dao = ImageSharingDAO.getImageSharingDAO(this, mSharingId);
if (dao == null) {
showMessageThenExit(R.string.error_item_not_found);
return;
}
mTxtViewContact.setText(dao.getContact().toString());
mTxtViewState.setText(RiApplication.sImageSharingStates[dao.getState().toInt()]);
mTxtViewReason.setText(RiApplication.sImageSharingReasonCodes[dao.getReasonCode().toInt()]);
mTxtViewDir.setText(RiApplication.getDirection(dao.getDirection()));
mTxtViewDate.setText(getDateFromDb(dao.getTimestamp()));
mTxtViewMime.setText(dao.getMimeType());
mTxtViewFilename.setText(dao.getFilename());
mTxtViewFileSize.setText(String.valueOf(dao.getSize()));
mTxtViewUri.setText(dao.getFile().toString());
mTxtViewTransferred.setText(String.valueOf(dao.getSizeTransferred()));
}
/**
* Start activity to view details of image sharing record
*
* @param context the context
* @param sharingId the sharing ID
*/
public static void startActivity(Context context, String sharingId) {
Intent intent = new Intent(context, ImageSharingLogView.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EXTRA_SHARING_ID, sharingId);
context.startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/image/InitiateImageSharing.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.image;
import static com.gsma.rcs.ri.utils.FileUtils.takePersistableContentUriPermission;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceNotAvailableException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.image.ImageSharing;
import com.gsma.services.rcs.sharing.image.ImageSharingListener;
import com.gsma.services.rcs.sharing.image.ImageSharingService;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.Set;
/**
* Initiate image sharing
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class InitiateImageSharing extends RcsActivity {
private static final String LOGTAG = LogUtils.getTag(InitiateImageSharing.class.getName());
/**
* Activity result constants
*/
private final static int SELECT_IMAGE = 0;
/**
* UI handler
*/
private final Handler mHandler = new Handler();
private String mFilename;
private Uri mFile;
private long mFilesize = -1;
private ImageSharing mImageSharing;
private String mSharingId;
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
private ImageSharingListener mIshListener;
private Button mDialBtn;
private Button mInviteBtn;
private ProgressBar mProgressBar;
private Button mSelectBtn;
private TextView mStatusView;
private TextView mFilenameView;
private ImageSharingService mImageSharingService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.image_sharing_initiate);
initialize();
// Register to API connection manager
if (!isServiceConnected(RcsServiceName.IMAGE_SHARING)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.IMAGE_SHARING);
try {
mImageSharingService.addEventListener(mIshListener);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (!isServiceConnected(RcsServiceName.IMAGE_SHARING)) {
return;
}
try {
getImageSharingApi().removeEventListener(mIshListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (RESULT_OK != resultCode || SELECT_IMAGE != requestCode) {
return;
}
if (data != null && data.getData() != null) {
// Get selected photo URI
mFile = data.getData();
// Display the selected filename attribute
mFilename = FileUtils.getFileName(this, mFile);
mFilesize = FileUtils.getFileSize(this, mFile);
mFilenameView.setText(mFilename);
TextView sizeView = (TextView) findViewById(R.id.size);
sizeView.setText(FileUtils.humanReadableByteCount(mFilesize, true));
mInviteBtn.setEnabled(true);
}
}
private void updateProgressBar(long currentSize, long totalSize) {
double position = 0.0d;
if (totalSize != 0) {
mStatusView.setText(Utils.getProgressLabel(currentSize, totalSize));
position = (((double) currentSize) / ((double) totalSize)) * 100.0d;
} else {
mStatusView.setText("");
}
mProgressBar.setProgress((int) position);
}
private void quitSession() {
try {
if (mImageSharing != null
&& RcsSessionUtil.isAllowedToAbortImageSharingSession(mImageSharing)) {
mImageSharing.abortSharing();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mImageSharing = null;
// Exit activity
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
try {
if (mImageSharing == null
|| !RcsSessionUtil.isAllowedToAbortImageSharingSession(mImageSharing)) {
finish();
return true;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setCancelable(true);
registerDialog(builder.show());
return true;
} catch (RcsServiceException e) {
showException(e);
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_image_sharing, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void initialize() {
mImageSharingService = getImageSharingApi();
mSpinner = (Spinner) findViewById(R.id.contact);
ContactListAdapter adapter = ContactListAdapter.createRcsContactListAdapter(this);
mSpinner.setAdapter(adapter);
OnClickListener btnInviteListener = new OnClickListener() {
public void onClick(View v) {
// Check if the service is available
try {
if (!mImageSharingService.isServiceRegistered()) {
showMessage(R.string.error_not_registered);
return;
}
// Get the remote contact
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
final ContactId remote = ContactUtil.formatContact(phoneNumber);
if (LogUtils.isActive) {
Log.d(LOGTAG, "shareImage image=" + mFilename + " size=" + mFilesize);
}
/* Only take persistable permission for content Uris */
takePersistableContentUriPermission(InitiateImageSharing.this, mFile);
// Initiate sharing
mImageSharing = mImageSharingService.shareImage(remote, mFile);
mSharingId = mImageSharing.getSharingId();
// Disable UI
mSpinner.setEnabled(false);
// Hide buttons
mInviteBtn.setVisibility(View.INVISIBLE);
mSelectBtn.setVisibility(View.INVISIBLE);
mDialBtn.setVisibility(View.INVISIBLE);
} catch (RcsServiceNotAvailableException e) {
showMessage(R.string.label_service_not_available);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
};
mInviteBtn = (Button) findViewById(R.id.invite_btn);
mInviteBtn.setOnClickListener(btnInviteListener);
mInviteBtn.setEnabled(false);
OnClickListener btnSelectListener = new OnClickListener() {
public void onClick(View v) {
FileUtils.openFile(InitiateImageSharing.this, "image/*", SELECT_IMAGE);
}
};
mSelectBtn = (Button) findViewById(R.id.select_btn);
mSelectBtn.setOnClickListener(btnSelectListener);
mSelectBtn.setEnabled(false);
OnClickListener btnDialListener = new OnClickListener() {
public void onClick(View v) {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
// Initiate a GSM call before to be able to share content
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:".concat(phoneNumber)));
if (ActivityCompat.checkSelfPermission(InitiateImageSharing.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
startActivity(intent);
}
};
mDialBtn = (Button) findViewById(R.id.dial_btn);
mDialBtn.setOnClickListener(btnDialListener);
mDialBtn.setEnabled(false);
mStatusView = (TextView) findViewById(R.id.progress_status);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mFilenameView = (TextView) findViewById(R.id.uri);
updateProgressBar(0, 0);
mFilenameView.setText("");
if (adapter == null || adapter.getCount() != 0) {
mDialBtn.setEnabled(true);
mSelectBtn.setEnabled(true);
}
mIshListener = new ImageSharingListener() {
@Override
public void onProgressUpdate(ContactId contact, String sharingId,
final long currentSize, final long totalSize) {
// Discard event if not for current sharingId
if (mSharingId == null || !mSharingId.equals(sharingId)) {
return;
}
mHandler.post(new Runnable() {
public void run() {
// Display sharing progress
updateProgressBar(currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(ContactId contact, String sharingId,
final ImageSharing.State state, ImageSharing.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " sharingId=" + sharingId
+ " state=" + state + " reason=" + reasonCode);
}
// Discard event if not for current sharingId
if (mSharingId == null || !mSharingId.equals(sharingId)) {
return;
}
final String _reasonCode = RiApplication.sImageSharingReasonCodes[reasonCode
.toInt()];
final String _state = RiApplication.sImageSharingStates[state.toInt()];
mHandler.post(new Runnable() {
public void run() {
TextView statusView = (TextView) findViewById(R.id.progress_status);
switch (state) {
case STARTED:
statusView.setText(_state);
break;
case ABORTED:
String msg = getString(R.string.label_sharing_aborted, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case REJECTED:
msg = getString(R.string.label_sharing_rejected, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case FAILED:
msg = getString(R.string.label_sharing_failed, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case TRANSFERRED:
statusView.setText(_state);
break;
default:
statusView.setText(_state);
if (LogUtils.isActive) {
Log.d(LOGTAG,
"onStateChanged "
+ getString(R.string.label_ish_state_changed,
_state, _reasonCode));
}
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " sharingIds=" + sharingIds);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/image/ReceiveImageSharing.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.image;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.utils.FileUtils;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.image.ImageSharing;
import com.gsma.services.rcs.sharing.image.ImageSharingIntent;
import com.gsma.services.rcs.sharing.image.ImageSharingListener;
import com.gsma.services.rcs.sharing.image.ImageSharingService;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.Set;
/**
* Receive image sharing
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class ReceiveImageSharing extends RcsActivity {
/**
* UI handler
*/
private final Handler handler = new Handler();
private ImageSharing mImageSharing;
/**
* The Image Sharing Data Object
*/
private ImageSharingDAO mIshDao;
private static final String LOGTAG = LogUtils.getTag(ReceiveImageSharing.class.getName());
private ImageSharingListener mListener;
private OnClickListener mAcceptBtnListener;
private OnClickListener mDeclineBtnListener;
private ProgressBar mProgressBar;
private boolean mSessionListenerSet;
private TextView mStatusView;
private String mSharingId;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.image_sharing_receive);
intitialize();
/* Register to API connection manager */
if (!isServiceConnected(RcsServiceName.IMAGE_SHARING, RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.IMAGE_SHARING, RcsServiceName.CONTACT);
processIntent(getIntent());
}
@Override
public void onDestroy() {
super.onDestroy();
if (!mSessionListenerSet || !isServiceConnected(RcsServiceName.IMAGE_SHARING)) {
return;
}
try {
getImageSharingApi().removeEventListener(mListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
processIntent(intent);
}
private void processIntent(Intent invitation) {
mSharingId = invitation.getStringExtra(ImageSharingIntent.EXTRA_SHARING_ID);
mIshDao = invitation.getParcelableExtra(ImageSharingIntentService.BUNDLE_ISHDAO_ID);
initiateImageSharing();
}
private void initiateImageSharing() {
ImageSharingService ishApi = getImageSharingApi();
try {
/* Get the image sharing */
mImageSharing = ishApi.getImageSharing(mSharingId);
if (mImageSharing == null) {
// Session not found or expired
showMessageThenExit(R.string.label_session_not_found);
return;
}
mSessionListenerSet = true;
ishApi.addEventListener(mListener);
/* Display sharing infos */
String from = RcsContactUtil.getInstance(this).getDisplayName(mIshDao.getContact());
TextView fromTextView = (TextView) findViewById(R.id.contact);
fromTextView.setText(from);
String size = FileUtils.humanReadableByteCount(mIshDao.getSize(), true);
TextView sizeTxt = (TextView) findViewById(R.id.size);
sizeTxt.setText(size);
updateProgressBar(0, mIshDao.getSize());
/* Display accept/reject dialog */
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_image_sharing);
builder.setMessage(getString(R.string.label_ft_from_size, from, size));
builder.setCancelable(false);
builder.setIcon(R.drawable.ri_notif_csh_icon);
builder.setPositiveButton(R.string.label_accept, mAcceptBtnListener);
builder.setNegativeButton(R.string.label_decline, mDeclineBtnListener);
registerDialog(builder.show());
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void acceptInvitation() {
try {
mImageSharing.acceptInvitation();
} catch (RcsGenericException e) {
showExceptionThenExit(e);
}
}
private void rejectInvitation() {
try {
mImageSharing.rejectInvitation();
} catch (RcsGenericException e) {
showExceptionThenExit(e);
}
}
private void updateProgressBar(long currentSize, long totalSize) {
mStatusView.setText(Utils.getProgressLabel(currentSize, totalSize));
double position = ((double) currentSize / (double) totalSize) * 100.0d;
mProgressBar.setProgress((int) position);
}
private void quitSession() {
try {
if (mImageSharing != null && ImageSharing.State.STARTED == mImageSharing.getState()) {
mImageSharing.abortSharing();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mImageSharing = null;
/* Exit activity */
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
try {
if (mImageSharing == null
|| !RcsSessionUtil.isAllowedToAbortImageSharingSession(mImageSharing)) {
finish();
return true;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setCancelable(true);
registerDialog(builder.show());
return true;
} catch (RcsServiceException e) {
showException(e);
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_image_sharing, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void intitialize() {
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mStatusView = (TextView) findViewById(R.id.progress_status);
mListener = new ImageSharingListener() {
@Override
public void onProgressUpdate(ContactId contact, String sharingId,
final long currentSize, final long totalSize) {
/* Discard event if not for current sharingId */
if (mSharingId == null || !mSharingId.equals(sharingId)) {
return;
}
handler.post(new Runnable() {
public void run() {
/* Display sharing progress */
updateProgressBar(currentSize, totalSize);
}
});
}
@Override
public void onStateChanged(ContactId contact, String sharingId,
final ImageSharing.State state, ImageSharing.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact.toString() + " sharingId="
+ sharingId + " state=" + state + " reason=" + reasonCode);
}
/* Discard event if not for current sharingId */
if (mSharingId == null || !mSharingId.equals(sharingId)) {
return;
}
final String _reasonCode = RiApplication.sImageSharingReasonCodes[reasonCode
.toInt()];
final String _state = RiApplication.sImageSharingStates[state.toInt()];
handler.post(new Runnable() {
public void run() {
switch (state) {
case ABORTED:
String msg = getString(R.string.label_sharing_aborted, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case FAILED:
msg = getString(R.string.label_sharing_failed, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case REJECTED:
msg = getString(R.string.label_sharing_rejected, _reasonCode);
mStatusView.setText(msg);
showMessageThenExit(msg);
break;
case TRANSFERRED:
// Display transfer progress
mStatusView.setText(_state);
// Make sure progress bar is at the end
mProgressBar.setProgress(mProgressBar.getMax());
// Show the shared image
Utils.showPicture(ReceiveImageSharing.this, mIshDao.getFile());
break;
default:
// Display session status
mStatusView.setText(_state);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged ".concat(getString(
R.string.label_ish_state_changed, _state, _reasonCode)));
}
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " sharingIds=" + sharingIds);
}
}
};
mAcceptBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
acceptInvitation();
}
};
mDeclineBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
rejectInvitation();
finish();
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/video/IncomingVideoSharing.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License ats
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.video;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.sharing.video.media.TerminatingVideoPlayer;
import com.gsma.rcs.ri.sharing.video.media.VideoPlayerListener;
import com.gsma.rcs.ri.sharing.video.media.VideoSurfaceView;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.video.VideoDescriptor;
import com.gsma.services.rcs.sharing.video.VideoSharing;
import com.gsma.services.rcs.sharing.video.VideoSharingListener;
import com.gsma.services.rcs.sharing.video.VideoSharingService;
import com.orangelabs.rcs.core.ims.protocol.rtp.codec.video.h264.H264Config;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.Set;
/**
* Receive video sharing
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class IncomingVideoSharing extends RcsActivity implements VideoPlayerListener {
/**
* UI handler
*/
private final Handler handler = new Handler();
private VideoSharing mVideoSharing;
/**
* The Video Sharing Data Object
*/
private VideoSharingDAO mVshDao;
/**
* Video renderer
* Note: this field is intentionally static
*/
private static TerminatingVideoPlayer mVideoRenderer;
/**
* Video width
*/
private int mVideoWidth = H264Config.QCIF_WIDTH;
/**
* Video height
*/
private int mVideoHeight = H264Config.QCIF_HEIGHT;
/**
* Live video preview
*/
private VideoSurfaceView mVideoView;
private static final String SAVE_VIDEO_SHARING_DAO = "videoSharingDao";
private static final String SAVE_WAIT_USER_ACCEPT = "waitUserAccept";
private boolean mWaitForUseAcceptance = true;
private AlertDialog mAcceptDeclineDialog;
private OnClickListener mAcceptBtnListener;
private OnClickListener mDeclineBtnListener;
private VideoSharingListener mVshListener;
private static final String LOGTAG = LogUtils
.getTag(IncomingVideoSharing.class.getSimpleName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setContentView(R.layout.video_sharing_incoming);
initialize();
// Always on window
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
// Saved datas
if (savedInstanceState == null) {
// Get invitation info
mVshDao = getIntent().getExtras().getParcelable(
VideoSharingIntentService.BUNDLE_VSHDAO_ID);
} else {
mVshDao = savedInstanceState.getParcelable(SAVE_VIDEO_SHARING_DAO);
mWaitForUseAcceptance = savedInstanceState.getBoolean(SAVE_WAIT_USER_ACCEPT);
}
if (mVshDao == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "onCreate cannot read Video Sharing invitation");
}
finish();
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate ".concat(mVshDao.toString()));
}
// Create the live video view
mVideoView = (VideoSurfaceView) findViewById(R.id.video_view);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
mVideoView.setAspectRatio(mVideoWidth, mVideoHeight);
} else {
mVideoView.setAspectRatio(mVideoHeight, mVideoWidth);
}
SurfaceHolder surface = mVideoView.getHolder();
surface.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surface.setKeepScreenOn(true);
if (mVideoRenderer == null) {
// Instantiate the renderer
mVideoRenderer = new TerminatingVideoPlayer(mVideoView, this);
} else {
mVideoRenderer.setSurface(mVideoView);
}
// Register to API connection manager
if (!isServiceConnected(RcsServiceName.VIDEO_SHARING, RcsServiceName.CONTACT)) {
showMessageThenExit(R.string.label_service_not_available);
} else {
startMonitorServices(RcsServiceName.VIDEO_SHARING, RcsServiceName.CONTACT);
startOrRestartVideoSharing();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mAcceptDeclineDialog != null) {
mAcceptDeclineDialog.cancel();
mAcceptDeclineDialog = null;
}
if (isFinishing()) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDestroy reset video renderer");
}
mVideoRenderer = null;
}
if (isServiceConnected(RcsServiceName.VIDEO_SHARING)) {
// Remove video sharing listener
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onDestroy Remove listener");
}
getVideoSharingApi().removeEventListener(mVshListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(SAVE_VIDEO_SHARING_DAO, mVshDao);
outState.putBoolean(SAVE_WAIT_USER_ACCEPT, mWaitForUseAcceptance);
}
private void startOrRestartVideoSharing() {
VideoSharingService vshApi = getVideoSharingApi();
try {
mVideoSharing = vshApi.getVideoSharing(mVshDao.getSharingId());
if (mVideoSharing == null) {
// Session not found or expired
showMessageThenExit(R.string.label_session_not_found);
return;
}
vshApi.addEventListener(mVshListener);
ContactId remote = mVshDao.getContact();
// Display sharing information
String from = RcsContactUtil.getInstance(this).getDisplayName(remote);
TextView fromTextView = (TextView) findViewById(R.id.contact);
fromTextView.setText(from);
if (mWaitForUseAcceptance) {
showReceiveNotification(from);
} else {
displayVideoFormat();
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void showReceiveNotification(String from) {
// User alert
// Display accept/reject dialog
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_video_sharing);
builder.setMessage(getString(R.string.label_from_args, from));
builder.setCancelable(false);
builder.setIcon(R.drawable.ri_notif_csh_icon);
builder.setPositiveButton(R.string.label_accept, mAcceptBtnListener);
builder.setNegativeButton(R.string.label_decline, mDeclineBtnListener);
mAcceptDeclineDialog = builder.show();
registerDialog(mAcceptDeclineDialog);
}
private void acceptInvitation() {
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "acceptInvitation");
}
mVideoSharing.acceptInvitation(mVideoRenderer);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void rejectInvitation() {
try {
if (LogUtils.isActive) {
Log.d(LOGTAG, "rejectInvitation");
}
mVideoSharing.rejectInvitation();
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
private void quitSession() {
try {
if (mVideoSharing != null && VideoSharing.State.STARTED == mVideoSharing.getState()) {
mVideoSharing.abortSharing();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mVideoSharing = null;
// Exit activity
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
try {
if (mVideoSharing == null
|| !RcsSessionUtil.isAllowedToAbortVideoSharingSession(mVideoSharing)) {
finish();
return true;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setCancelable(true);
registerDialog(builder.show());
return true;
} catch (RcsServiceException e) {
showException(e);
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_video_sharing, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
private void displayVideoFormat() {
try {
VideoDescriptor videoDescriptor = mVideoSharing.getVideoDescriptor();
String format = mVideoSharing.getVideoEncoding() + " " + videoDescriptor.getWidth()
+ "x" + videoDescriptor.getHeight();
TextView fmtView = (TextView) findViewById(R.id.video_format);
fmtView.setVisibility(View.VISIBLE);
fmtView.setText(format);
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
/*-------------------------- Video player callbacks ------------------*/
@Override
public void onPlayerOpened() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerOpened");
}
}
@Override
public void onPlayerStarted() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerStarted");
}
}
@Override
public void onPlayerStopped() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerStopped");
}
}
@Override
public void onPlayerClosed() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerClosed");
}
}
@Override
public void onPlayerError() {
// TODO
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerError");
}
}
@Override
public void onPlayerResized(int width, int height) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerResized " + width + "x" + height);
}
mVideoView.setAspectRatio(width, height);
LinearLayout l = (LinearLayout) mVideoView.getParent();
l.setLayoutParams(new FrameLayout.LayoutParams(width, height));
}
private void initialize() {
mAcceptBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mAcceptDeclineDialog = null;
mWaitForUseAcceptance = false;
acceptInvitation();
}
};
mDeclineBtnListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mAcceptDeclineDialog = null;
mWaitForUseAcceptance = false;
rejectInvitation();
// Exit activity
finish();
}
};
mVshListener = new VideoSharingListener() {
@Override
public void onStateChanged(ContactId contact, String sharingId,
final VideoSharing.State state, VideoSharing.ReasonCode reasonCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " sharingId=" + sharingId
+ " state=" + state + " reason=" + reasonCode);
}
// Discard event if not for current sharingId
if (mVshDao == null || !mVshDao.getSharingId().equals(sharingId)) {
return;
}
final String _reasonCode = RiApplication.sVideoReasonCodes[reasonCode.toInt()];
handler.post(new Runnable() {
public void run() {
switch (state) {
case STARTED:
displayVideoFormat();
// Start the renderer
mVideoRenderer.open();
mVideoRenderer.start();
break;
case ABORTED:
// Stop the renderer
mVideoRenderer.stop();
mVideoRenderer.close();
// Display session status
showMessageThenExit(getString(R.string.label_sharing_aborted,
_reasonCode));
break;
case FAILED:
// Stop the renderer
mVideoRenderer.stop();
mVideoRenderer.close();
// Session is failed: exit
showMessageThenExit(getString(R.string.label_sharing_failed,
_reasonCode));
break;
case REJECTED:
// Stop the renderer
mVideoRenderer.stop();
mVideoRenderer.close();
// Session is rejected: exit
showMessageThenExit(getString(R.string.label_sharing_rejected,
_reasonCode));
break;
default:
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged ".concat(getString(
R.string.label_vsh_state_changed,
RiApplication.sVideoSharingStates[state.toInt()],
_reasonCode)));
}
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " sharingIds=" + sharingIds);
}
}
};
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/video/OutgoingVideoSharing.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.video;
import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.sharing.video.media.OriginatingVideoPlayer;
import com.gsma.rcs.ri.sharing.video.media.VideoPlayerListener;
import com.gsma.rcs.ri.sharing.video.media.VideoSurfaceView;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsPersistentStorageException;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.video.VideoDescriptor;
import com.gsma.services.rcs.sharing.video.VideoSharing;
import com.gsma.services.rcs.sharing.video.VideoSharingListener;
import com.gsma.services.rcs.sharing.video.VideoSharingService;
import com.orangelabs.rcs.core.ims.protocol.rtp.codec.video.h264.H264Config;
import com.orangelabs.rcs.core.ims.protocol.rtp.format.video.CameraOptions;
import com.orangelabs.rcs.core.ims.protocol.rtp.format.video.Orientation;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
/**
* Initiate video sharing.
*
* @author Jean-Marc AUFFRET
* @author Philippe LEMORDANT
*/
public class OutgoingVideoSharing extends RcsActivity implements VideoPlayerListener,
SurfaceHolder.Callback {
/**
* UI handler
*/
private final Handler handler = new Handler();
/**
* Video sharing
*/
private VideoSharing mVideoSharing;
/**
* Video sharing Id
*/
private String mSharingId;
/**
* Video player
* Note: this field is intentionally static
*/
private static OriginatingVideoPlayer mVideoPlayer;
/**
* Camera of the device
*/
private Camera mCamera;
/**
* Opened camera id
*/
private CameraOptions mOpenedCameraId = CameraOptions.FRONT;
/**
* Camera preview started flag
*/
private boolean mCameraPreviewRunning = false;
/**
* Video width
*/
private int mVideoWidth = H264Config.QCIF_WIDTH;
/**
* Video height
*/
private int mVideoHeight = H264Config.QCIF_HEIGHT;
/**
* Number of cameras
*/
private int mNbfCameras = 1;
/**
* Live video preview
*/
private VideoSurfaceView mVideoView;
/**
* Video surface holder
*/
private SurfaceHolder mSurface;
/**
* Spinner for contact selection
*/
private Spinner mSpinner;
private Button mInviteBtn;
private Button mDialBtn;
private Button mSwitchCamBtn;
private ContactId mContact;
/**
* Session is started and video format is then negotiated.
*/
private boolean mStarted = false;
/**
* Preview surface view is created
*/
private boolean mIsSurfaceCreated;
private static final String LOGTAG = LogUtils
.getTag(OutgoingVideoSharing.class.getSimpleName());
private static final String SAVE_SHARING_ID = "sharingId";
private static final String SAVE_VIDEO_HEIGHT = "videoHeight";
private static final String SAVE_VIDEO_WIDTH = "videoWidth";
private static final String SAVE_NB_OF_CAMERAS = "numberOfCameras";
private static final String SAVE_OPENED_CAMERA_ID = "openedCameraId";
/**
* We save the remote contact into the activity bundle.
* This information could also be retrieved from session instance.
*/
private static final String SAVE_REMOTE_CONTACT = "remoteContact";
/**
* We save this information into the activity bundle.
* This information could also be retrieved from session instance state.
*/
private static final String SAVE_STARTED = "started";
/*
* (non-Javadoc)
* @see android.app.Activity#onCreate(android.os.Bundle)
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Always on window
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
// Set layout
setContentView(R.layout.video_sharing_outgoing);
// Set the contact selector
mSpinner = (Spinner) findViewById(R.id.contact);
ContactListAdapter adapter = ContactListAdapter.createRcsContactListAdapter(this);
mSpinner.setAdapter(adapter);
// Saved datas
if (savedInstanceState == null) {
mNbfCameras = getNumberOfCameras();
} else {
mSharingId = savedInstanceState.getString(SAVE_SHARING_ID);
mNbfCameras = savedInstanceState.getInt(SAVE_NB_OF_CAMERAS);
mVideoHeight = savedInstanceState.getInt(SAVE_VIDEO_HEIGHT);
mVideoWidth = savedInstanceState.getInt(SAVE_VIDEO_WIDTH);
mOpenedCameraId = CameraOptions.convert(savedInstanceState
.getInt(SAVE_OPENED_CAMERA_ID));
mContact = savedInstanceState.getParcelable(SAVE_REMOTE_CONTACT);
mStarted = savedInstanceState.getBoolean(SAVE_STARTED);
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Sharing ID " + mSharingId + " Nb of cameras=" + mNbfCameras
+ " active camera=" + mOpenedCameraId);
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Resolution: " + mVideoWidth + "x" + mVideoHeight);
}
// Set button callback
mInviteBtn = (Button) findViewById(R.id.invite_btn);
mInviteBtn.setOnClickListener(btnInviteListener);
mDialBtn = (Button) findViewById(R.id.dial_btn);
mDialBtn.setOnClickListener(btnDialListener);
mSwitchCamBtn = (Button) findViewById(R.id.switch_cam_btn);
// Disable button if no contact available
if (adapter == null || adapter.getCount() == 0) {
mDialBtn.setEnabled(false);
mInviteBtn.setEnabled(false);
}
// Get camera info
if (mNbfCameras > 1) {
boolean backAvailable = checkCameraSize(CameraOptions.BACK);
boolean frontAvailable = checkCameraSize(CameraOptions.FRONT);
if (frontAvailable && backAvailable) {
mSwitchCamBtn.setOnClickListener(btnSwitchCamListener);
} else if (frontAvailable) {
mOpenedCameraId = CameraOptions.FRONT;
mSwitchCamBtn.setVisibility(View.INVISIBLE);
} else if (backAvailable) {
mOpenedCameraId = CameraOptions.BACK;
mSwitchCamBtn.setVisibility(View.INVISIBLE);
} else {
if (LogUtils.isActive) {
Log.d(LOGTAG, "No camera available for encoding");
}
}
} else {
if (checkCameraSize(CameraOptions.FRONT)) {
mSwitchCamBtn.setVisibility(View.INVISIBLE);
} else {
if (LogUtils.isActive) {
Log.d(LOGTAG, "No camera available for encoding");
}
}
}
// Create the live video view
mVideoView = (VideoSurfaceView) findViewById(R.id.video_preview);
mVideoView.setAspectRatio(mVideoWidth, mVideoHeight);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
mVideoView.setAspectRatio(mVideoWidth, mVideoHeight);
} else {
mVideoView.setAspectRatio(mVideoHeight, mVideoWidth);
}
mSurface = mVideoView.getHolder();
mSurface.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurface.setKeepScreenOn(true);
mSurface.addCallback(this);
// Check if session in progress
if (mSharingId != null) {
// Sharing in progress
mDialBtn.setVisibility(View.GONE);
mInviteBtn.setVisibility(View.GONE);
mSpinner.setVisibility(View.GONE);
mSwitchCamBtn.setEnabled((mNbfCameras > 1));
handler.post(continueOutgoingSessionRunnable);
displayRemoteContact();
} else {
// Sharing not yet initiated
mDialBtn.setVisibility(View.VISIBLE);
mInviteBtn.setVisibility(View.VISIBLE);
mSwitchCamBtn.setEnabled(false);
boolean canInitiate = true;
if (adapter == null || adapter.getCount() == 0) {
canInitiate = false;
}
mDialBtn.setEnabled(canInitiate);
mInviteBtn.setEnabled(canInitiate);
}
// Register to API connection manager
if (!isServiceConnected(RcsServiceName.VIDEO_SHARING)) {
showMessageThenExit(R.string.label_service_not_available);
return;
}
startMonitorServices(RcsServiceName.VIDEO_SHARING);
// Add service listener
try {
VideoSharingService vshService = getVideoSharingApi();
if (mSharingId != null) {
// Sharing is in progress: get sharing session
mVideoSharing = vshService.getVideoSharing(mSharingId);
if (mStarted) {
displayVideoFormat();
}
}
vshService.addEventListener(vshListener);
if (LogUtils.isActive) {
Log.d(LOGTAG, "onCreate video sharing");
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(SAVE_SHARING_ID, mSharingId);
outState.putInt(SAVE_VIDEO_HEIGHT, mVideoHeight);
outState.putInt(SAVE_VIDEO_WIDTH, mVideoWidth);
outState.putInt(SAVE_NB_OF_CAMERAS, mNbfCameras);
outState.putInt(SAVE_OPENED_CAMERA_ID, mOpenedCameraId.getValue());
outState.putParcelable(SAVE_REMOTE_CONTACT, mContact);
outState.putBoolean(SAVE_STARTED, mStarted);
}
@Override
public void onDestroy() {
super.onDestroy();
if (!isServiceConnected(RcsServiceName.VIDEO_SHARING)) {
return;
}
try {
getVideoSharingApi().removeEventListener(vshListener);
} catch (RcsServiceException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
closeCamera();
}
/**
* Dial button listener
*/
// TODO initialize
private OnClickListener btnDialListener = new OnClickListener() {
public void onClick(View v) {
// get selected phone number
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
// Initiate a GSM call before to be able to share content
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:".concat(phoneNumber)));
if (ActivityCompat.checkSelfPermission(OutgoingVideoSharing.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
startActivity(intent);
}
};
/**
* Invite button listener
*/
private OnClickListener btnInviteListener = new OnClickListener() {
public void onClick(View v) {
// Check if the service is available
try {
if (!getVideoSharingApi().isServiceRegistered()) {
showMessage(R.string.label_service_not_available);
return;
}
} catch (RcsServiceException e) {
showExceptionThenExit(e);
}
// Get the remote contact
ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());
mContact = ContactUtil.formatContact(phoneNumber);
new Thread() {
public void run() {
try {
// Create the video player
mVideoPlayer = new OriginatingVideoPlayer(OutgoingVideoSharing.this);
// Open the camera
openCamera();
// Initiate sharing
mVideoSharing = getVideoSharingApi().shareVideo(mContact, mVideoPlayer);
mSharingId = mVideoSharing.getSharingId();
} catch (final RcsServiceException e) {
// Free the camera
closeCamera();
handler.post(new Runnable() {
public void run() {
showExceptionThenExit(e);
}
});
}
}
}.start();
mSwitchCamBtn.setEnabled(true);
// Hide buttons
mInviteBtn.setVisibility(View.GONE);
mDialBtn.setVisibility(View.GONE);
mSpinner.setVisibility(View.GONE);
displayRemoteContact();
}
};
/**
* Switch camera button listener
*/
private View.OnClickListener btnSwitchCamListener = new View.OnClickListener() {
public void onClick(View v) {
// Switch camera
switchCamera();
}
};
private void quitSession() {
try {
if (mVideoSharing != null && VideoSharing.State.STARTED == mVideoSharing.getState()) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Abort sharing");
}
mVideoSharing.abortSharing();
}
} catch (RcsServiceException e) {
showException(e);
} finally {
mVideoSharing = null;
// Exit activity
finish();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Back key pressed");
}
try {
if (mVideoSharing == null
|| !RcsSessionUtil.isAllowedToAbortVideoSharingSession(mVideoSharing)) {
finish();
return true;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.label_confirm_close);
builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
quitSession();
}
});
builder.setNegativeButton(R.string.label_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setCancelable(true);
registerDialog(builder.show());
return true;
} catch (RcsServiceException e) {
showException(e);
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(getApplicationContext());
inflater.inflate(R.menu.menu_video_sharing, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_close_session:
quitSession();
break;
}
return true;
}
/*-------------------------- Camera methods ------------------*/
/**
* Open the camera
*/
private synchronized void openCamera() {
if (mCamera != null) {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Already opened camera");
}
return;
}
openCamera(mOpenedCameraId);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
mVideoView.setAspectRatio(mVideoWidth, mVideoHeight);
} else {
mVideoView.setAspectRatio(mVideoHeight, mVideoWidth);
}
// Start camera
mCamera.setPreviewCallback(mVideoPlayer);
startCameraPreview();
}
/**
* Close the camera
*/
private synchronized void closeCamera() {
if (mCamera == null) {
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Close camera");
}
mCamera.setPreviewCallback(null);
if (mCameraPreviewRunning) {
mCameraPreviewRunning = false;
mCamera.stopPreview();
}
mCamera.release();
mCamera = null;
}
/**
* Switch the camera
*/
private synchronized void switchCamera() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "Switch camera");
}
closeCamera();
// Open the other camera
if (mOpenedCameraId.getValue() == CameraOptions.BACK.getValue()) {
mOpenedCameraId = CameraOptions.FRONT;
} else {
mOpenedCameraId = CameraOptions.BACK;
}
openCamera();
}
/**
* Check if good camera sizes are available for encoder. Must be used only before open camera.
*
* @param cameraId the camera ID
* @return false if the camera don't have the good preview size for the encoder
*/
boolean checkCameraSize(CameraOptions cameraId) {
boolean sizeAvailable = false;
Camera camera = null;
Method method = getCameraOpenMethod();
if (method != null) {
try {
camera = (Camera) method.invoke(camera, new Object[] {
cameraId.getValue()
});
} catch (Exception e) {
camera = Camera.open();
}
} else {
camera = Camera.open();
}
if (camera == null) {
return false;
}
// Check common sizes
Parameters param = camera.getParameters();
List sizes = param.getSupportedPreviewSizes();
for (Camera.Size size : sizes) {
if ((size.width == H264Config.QVGA_WIDTH && size.height == H264Config.QVGA_HEIGHT)
|| (size.width == H264Config.CIF_WIDTH && size.height == H264Config.CIF_HEIGHT)
|| (size.width == H264Config.VGA_WIDTH && size.height == H264Config.VGA_HEIGHT)) {
sizeAvailable = true;
break;
}
}
// Release camera
camera.release();
return sizeAvailable;
}
/**
* Start the camera preview
*/
private void startCameraPreview() {
if (mCamera == null) {
return;
}
// Camera settings
Camera.Parameters p = mCamera.getParameters();
p.setPreviewFormat(PixelFormat.YCbCr_420_SP);
// Orientation
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
switch (display.getRotation()) {
case Surface.ROTATION_0:
if (LogUtils.isActive) {
Log.d(LOGTAG, "ROTATION_0");
}
if (mOpenedCameraId == CameraOptions.FRONT) {
mVideoPlayer.setOrientation(Orientation.ROTATE_90_CCW);
} else {
mVideoPlayer.setOrientation(Orientation.ROTATE_90_CW);
}
mCamera.setDisplayOrientation(90);
break;
case Surface.ROTATION_90:
if (LogUtils.isActive) {
Log.d(LOGTAG, "ROTATION_90");
}
mVideoPlayer.setOrientation(Orientation.NONE);
break;
case Surface.ROTATION_180:
if (LogUtils.isActive) {
Log.d(LOGTAG, "ROTATION_180");
}
if (mOpenedCameraId == CameraOptions.FRONT) {
mVideoPlayer.setOrientation(Orientation.ROTATE_90_CW);
} else {
mVideoPlayer.setOrientation(Orientation.ROTATE_90_CCW);
}
mCamera.setDisplayOrientation(270);
break;
case Surface.ROTATION_270:
if (LogUtils.isActive) {
Log.d(LOGTAG, "ROTATION_270");
}
if (mOpenedCameraId == CameraOptions.FRONT) {
mVideoPlayer.setOrientation(Orientation.ROTATE_180);
} else {
mVideoPlayer.setOrientation(Orientation.ROTATE_180);
}
mCamera.setDisplayOrientation(180);
break;
}
// Check if preview size is supported
if (isPreviewSizeSupported(p, mVideoWidth, mVideoHeight)) {
// Use the existing size without resizing
p.setPreviewSize(mVideoWidth, mVideoHeight);
// TODO videoPlayer.activateResizing(videoWidth, videoHeight); //
// same size = no
// resizing
if (LogUtils.isActive) {
Log.d(LOGTAG, "Camera preview initialized with size " + mVideoWidth + "x"
+ mVideoHeight);
}
} else {
// Check if can use a other known size (QVGA, CIF or VGA)
int w = 0;
int h = 0;
for (Camera.Size size : p.getSupportedPreviewSizes()) {
w = size.width;
h = size.height;
if ((w == H264Config.QVGA_WIDTH && h == H264Config.QVGA_HEIGHT)
|| (w == H264Config.CIF_WIDTH && h == H264Config.CIF_HEIGHT)
|| (w == H264Config.VGA_WIDTH && h == H264Config.VGA_HEIGHT)) {
break;
}
}
if (w != 0) {
p.setPreviewSize(w, h);
// TODO does not work if default sizes are not supported like
// for Samsung S5 mini
// mVideoPlayer.activateResizing(w, h);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Camera preview initialized with size " + w + "x" + h
+ " with a resizing to " + mVideoWidth + "x" + mVideoHeight);
}
} else {
// The camera don't have known size, we can't use it
if (LogUtils.isActive) {
Log.d(LOGTAG, "Camera preview can't be initialized with size " + mVideoWidth
+ "x" + mVideoHeight);
}
Toast.makeText(this,
getString(R.string.label_session_failed, "Camera is not compatible"),
Toast.LENGTH_SHORT).show();
quitSession();
return;
}
}
// Set camera parameters
mCamera.setParameters(p);
try {
mCamera.setPreviewDisplay(mVideoView.getHolder());
mCamera.startPreview();
mCameraPreviewRunning = true;
} catch (Exception e) {
mCamera = null;
}
}
/**
* Get Camera "open" Method
*
* @return Method
*/
private Method getCameraOpenMethod() {
ClassLoader classLoader = OutgoingVideoSharing.class.getClassLoader();
try {
Class> cameraClass = classLoader.loadClass("android.hardware.Camera");
try {
return cameraClass.getMethod("open", new Class[] {
int.class
});
} catch (NoSuchMethodException ignored) {
}
} catch (ClassNotFoundException ignored) {
}
return null;
}
/**
* Open the camera
*
* @param cameraId Camera ID
*/
private void openCamera(CameraOptions cameraId) {
Method method = getCameraOpenMethod();
if (mNbfCameras > 1 && method != null) {
try {
int hCamId = 0;
if (cameraId == CameraOptions.FRONT) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int id = 0; id < mNbfCameras; id++) {
Camera.getCameraInfo(id, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
hCamId = id;
break;
}
}
}
mCamera = (Camera) method.invoke(mCamera, new Object[] {
hCamId
});
mOpenedCameraId = cameraId;
} catch (Exception e) {
mCamera = Camera.open();
mOpenedCameraId = CameraOptions.BACK;
}
} else {
mCamera = Camera.open();
mOpenedCameraId = CameraOptions.BACK;
}
if (mVideoPlayer != null) {
mVideoPlayer.setCameraId(mOpenedCameraId.getValue());
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "Open camera ".concat(mOpenedCameraId.toString()));
}
}
/**
* Get Camera "numberOfCameras" Method
*
* @return Method
*/
private Method getCameraNumberOfCamerasMethod() {
ClassLoader classLoader = OutgoingVideoSharing.class.getClassLoader();
try {
Class> cameraClass = classLoader.loadClass("android.hardware.Camera");
try {
return cameraClass.getMethod("getNumberOfCameras", (Class[]) null);
} catch (NoSuchMethodException ignored) {
}
} catch (ClassNotFoundException ignored) {
}
return null;
}
/**
* Get number of cameras
*
* @return number of cameras
*/
private int getNumberOfCameras() {
Method method = getCameraNumberOfCamerasMethod();
if (method != null) {
try {
return (Integer) method.invoke(null, (Object[]) null);
} catch (Exception e) {
return 1;
}
} else {
return 1;
}
}
/*-------------------------- Session callbacks ------------------*/
/**
* Video sharing listener
*/
private VideoSharingListener vshListener = new VideoSharingListener() {
@Override
public void onStateChanged(ContactId contact, String sharingId,
final VideoSharing.State state, VideoSharing.ReasonCode reasonCode) {
// Discard event if not for current sharingId
if (mSharingId == null || !mSharingId.equals(sharingId)) {
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged contact=" + contact + " sharingId=" + sharingId
+ " state=" + state + " reason=" + reasonCode);
}
final String _reasonCode = RiApplication.sVideoReasonCodes[reasonCode.toInt()];
handler.post(new Runnable() {
public void run() {
switch (state) {
case STARTED:
mStarted = true;
displayVideoFormat();
// Start the player
mVideoPlayer.open();
mVideoPlayer.start();
// Update camera button
Button switchCamBtn = (Button) findViewById(R.id.switch_cam_btn);
switchCamBtn.setEnabled(true);
// Session is established : hide progress dialog
break;
case ABORTED:
// Stop the player
mVideoPlayer.stop();
mVideoPlayer.close();
// Release the camera
closeCamera();
// Display message info and exit
showMessageThenExit(getString(R.string.label_sharing_aborted,
_reasonCode));
break;
case REJECTED:
// Release the camera
closeCamera();
showMessageThenExit(getString(R.string.label_sharing_rejected,
_reasonCode));
break;
case FAILED:
// Stop the player
mVideoPlayer.stop();
mVideoPlayer.close();
// Release the camera
closeCamera();
// Display error info and exit
showMessageThenExit(getString(R.string.label_sharing_failed,
_reasonCode));
break;
default:
if (LogUtils.isActive) {
Log.d(LOGTAG, "onStateChanged ".concat(getString(
R.string.label_vsh_state_changed,
RiApplication.sVideoSharingStates[state.toInt()],
_reasonCode)));
}
}
}
});
}
@Override
public void onDeleted(ContactId contact, Set sharingIds) {
if (LogUtils.isActive) {
Log.w(LOGTAG, "onDeleted contact=" + contact + " sharingIds=" + sharingIds);
}
}
};
/*-------------------------- Video player callbacks ------------------*/
/**
* Callback called when the player is opened
*/
public void onPlayerOpened() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerOpened");
}
}
/**
* Callback called when the player is started
*/
public void onPlayerStarted() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerStarted");
}
}
/**
* Callback called when the player is stopped
*/
public void onPlayerStopped() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerStopped");
}
}
/**
* Callback called when the player is closed
*/
public void onPlayerClosed() {
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerClosed");
}
}
/**
* Callback called when the player has failed
*/
public void onPlayerError() {
// TODO
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerError");
}
}
/**
* Callback called when the player has been resized
*/
public void onPlayerResized(int width, int height) {
// TODO
if (LogUtils.isActive) {
Log.d(LOGTAG, "onPlayerResized");
}
}
/**
* Check if preview size is supported
*
* @param parameters Camera parameters
* @param width width
* @param height height
* @return True if supported
*/
private boolean isPreviewSizeSupported(Parameters parameters, int width, int height) {
List sizes = parameters.getSupportedPreviewSizes();
for (Size size : sizes) {
if (size.width == width && size.height == height) {
return true;
}
}
return false;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsSurfaceCreated = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mIsSurfaceCreated = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsSurfaceCreated = false;
}
/**
* Runnable to continue outgoing session.
* Note: the surface view be created to display preview.
*/
private Runnable continueOutgoingSessionRunnable = new Runnable() {
private int delay = 0;
@Override
public void run() {
if (mIsSurfaceCreated) {
// Open camera only once surface is created
openCamera();
} else {
delay += 200;
handler.removeCallbacks(this);
if (delay < 2000) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Delaying continue Outgoing");
}
handler.postDelayed(this, delay);
}
}
}
};
/**
* Display video format
*/
private void displayVideoFormat() {
try {
VideoDescriptor videoDescriptor = mVideoSharing.getVideoDescriptor();
String format = mVideoSharing.getVideoEncoding() + " " + videoDescriptor.getWidth()
+ "x" + videoDescriptor.getHeight();
TextView fmtView = (TextView) findViewById(R.id.video_format);
fmtView.setText(format);
} catch (RcsPersistentStorageException | RcsGenericException e) {
Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
}
}
/**
* Display remote contact
*/
private void displayRemoteContact() {
TextView fromTextView = (TextView) findViewById(R.id.remote);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(mContact);
fromTextView.setText(displayName);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/video/VideoSharingDAO.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.video;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.services.rcs.RcsService.Direction;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.video.VideoSharing;
import com.gsma.services.rcs.sharing.video.VideoSharingLog;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.format.DateUtils;
/**
* Video Sharing Data Object
*
* @author YPLO6403
*/
public class VideoSharingDAO implements Parcelable {
private final String mSharingId;
private final ContactId mContact;
private final VideoSharing.State mState;
private final VideoSharing.ReasonCode mReasonCode;
private final Direction mDirection;
private final long mTimestamp;
private final long mDuration;
private final int mHeight;
private final int mWidth;
private final String mVideoEncoding;
private static ContentResolver sContentResolver;
private VideoSharingDAO(String sharingId, ContactId contact, VideoSharing.State state,
VideoSharing.ReasonCode reasonCode, Direction direction, long timestamp, long duration,
int height, int width, String videoEncoding) {
mSharingId = sharingId;
mContact = contact;
mState = state;
mReasonCode = reasonCode;
mDirection = direction;
mTimestamp = timestamp;
mDuration = duration;
mHeight = height;
mWidth = width;
mVideoEncoding = videoEncoding;
}
/**
* Constructor
*
* @param source Parcelable source
*/
public VideoSharingDAO(Parcel source) {
mSharingId = source.readString();
boolean containsContactId = source.readInt() != 0;
if (containsContactId) {
mContact = ContactId.CREATOR.createFromParcel(source);
} else {
mContact = null;
}
mState = VideoSharing.State.valueOf(source.readInt());
mDirection = Direction.valueOf(source.readInt());
mTimestamp = source.readLong();
mDuration = source.readLong();
mHeight = source.readInt();
mWidth = source.readInt();
mVideoEncoding = source.readString();
mReasonCode = VideoSharing.ReasonCode.valueOf(source.readInt());
}
/**
* Gets state
*
* @return state
*/
public VideoSharing.State getState() {
return mState;
}
/**
* Gets reason code
*
* @return reason code
*/
public VideoSharing.ReasonCode getReasonCode() {
return mReasonCode;
}
/**
* Gets sharing ID
*
* @return sharingId
*/
public String getSharingId() {
return mSharingId;
}
/**
* Gets remote contact
*
* @return contact
*/
public ContactId getContact() {
return mContact;
}
public Direction getDirection() {
return mDirection;
}
/**
* Gets date of the sharing
*
* @return time stamp
*/
public long getTimestamp() {
return mTimestamp;
}
/**
* Gets duration
*
* @return duration
*/
public long getDuration() {
return mDuration;
}
/**
* Gets height
*
* @return height
*/
public int getHeight() {
return mHeight;
}
/**
* Gets width
*
* @return width
*/
public int getWidth() {
return mWidth;
}
/**
* Gets video encoding name (e.g. H264).
*
* @return video encoding
*/
public String getVideoEncoding() {
return mVideoEncoding;
}
@Override
public String toString() {
return "VideoSharingDAO [sharingId=" + mSharingId + ", contact=" + mContact + ", state="
+ mState + ", duration=" + DateUtils.formatElapsedTime(mDuration / 1000) + "]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mSharingId);
if (mContact != null) {
dest.writeInt(1);
mContact.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
dest.writeInt(mState.toInt());
dest.writeInt(mDirection.toInt());
dest.writeLong(mTimestamp);
dest.writeLong(mDuration);
dest.writeInt(mHeight);
dest.writeInt(mWidth);
dest.writeString(mVideoEncoding);
dest.writeInt(mReasonCode.toInt());
}
/**
* public CREATOR field that generates instances of Parcelable class from a VideoSharingDAO.
*/
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public VideoSharingDAO createFromParcel(Parcel in) {
return new VideoSharingDAO(in);
}
@Override
public VideoSharingDAO[] newArray(int size) {
return new VideoSharingDAO[size];
}
};
/**
* Gets instance of Video sharing from RCS provider
*
* @param context the context
* @param sharingId the sharing ID
* @return instance or null if entry not found
*/
public static VideoSharingDAO getVideoSharingDAO(Context context, String sharingId) {
if (sContentResolver == null) {
sContentResolver = context.getContentResolver();
}
Cursor cursor = null;
try {
cursor = sContentResolver.query(
Uri.withAppendedPath(VideoSharingLog.CONTENT_URI, sharingId), null, null, null,
null);
if (cursor == null) {
throw new SQLException("Query failed!");
}
if (!cursor.moveToFirst()) {
throw new SQLException("Failed to find Video Sharing with ID: ".concat(sharingId));
}
String number = cursor.getString(cursor.getColumnIndexOrThrow(VideoSharingLog.CONTACT));
ContactId contact = ContactUtil.formatContact(number);
VideoSharing.State state = VideoSharing.State.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(VideoSharingLog.STATE)));
Direction dir = Direction.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(VideoSharingLog.DIRECTION)));
long timestamp = cursor
.getLong(cursor.getColumnIndexOrThrow(VideoSharingLog.TIMESTAMP));
long duration = cursor.getLong(cursor.getColumnIndexOrThrow(VideoSharingLog.DURATION));
int height = cursor.getInt(cursor.getColumnIndexOrThrow(VideoSharingLog.HEIGHT));
int width = cursor.getInt(cursor.getColumnIndexOrThrow(VideoSharingLog.WIDTH));
String videoEncoding = cursor.getString(cursor
.getColumnIndexOrThrow(VideoSharingLog.VIDEO_ENCODING));
VideoSharing.ReasonCode reason = VideoSharing.ReasonCode.valueOf(cursor.getInt(cursor
.getColumnIndexOrThrow(VideoSharingLog.REASON_CODE)));
return new VideoSharingDAO(sharingId, contact, state, reason, dir, timestamp, duration,
height, width, videoEncoding);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/video/VideoSharingIntentService.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.video;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.Utils;
import com.gsma.services.rcs.sharing.video.VideoSharing;
import com.gsma.services.rcs.sharing.video.VideoSharingIntent;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* Video sharing intent service
*
* @author YPLO6403
*/
public class VideoSharingIntentService extends IntentService {
private static final String LOGTAG = LogUtils.getTag(VideoSharingIntentService.class
.getSimpleName());
static final String BUNDLE_VSHDAO_ID = "vshdao";
/**
* Constructor
*/
public VideoSharingIntentService() {
super("VideoSharingIntentService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// We want this service to stop running if forced stop
// so return not sticky.
return START_NOT_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
String action;
if ((action = intent.getAction()) == null) {
return;
}
// Check action from incoming intent
if (!VideoSharingIntent.ACTION_NEW_INVITATION.equals(action)) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Unknown action ".concat(action));
}
return;
}
// Gets data from the incoming Intent
String sharingId = intent.getStringExtra(VideoSharingIntent.EXTRA_SHARING_ID);
if (sharingId == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "Cannot read sharing ID");
}
return;
}
if (LogUtils.isActive) {
Log.d(LOGTAG, "onHandleIntent video sharing with ID ".concat(sharingId));
}
// Get Video Sharing from provider
VideoSharingDAO vshDao = VideoSharingDAO.getVideoSharingDAO(this, sharingId);
if (vshDao == null) {
return;
}
// Save VideoSharingDAO into intent
Bundle bundle = new Bundle();
bundle.putParcelable(BUNDLE_VSHDAO_ID, vshDao);
intent.putExtras(bundle);
if (LogUtils.isActive) {
Log.d(LOGTAG, "Video sharing invitation ".concat(vshDao.toString()));
}
if (VideoSharing.State.INVITED == vshDao.getState()) {
addVideoSharingInvitationNotification(intent, vshDao);
}
}
/**
* Add video share notification
*
* @param invitation Intent invitation
* @param vshDao the video sharing data object
*/
public void addVideoSharingInvitationNotification(Intent invitation, VideoSharingDAO vshDao) {
if (vshDao.getContact() == null) {
if (LogUtils.isActive) {
Log.e(LOGTAG, "VideoSharingInvitationReceiver failed: cannot parse contact");
}
return;
}
/* Create pending intent */
Intent intent = new Intent(invitation);
intent.setClass(this, IncomingVideoSharing.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*
* If the PendingIntent has the same operation, action, data, categories, components, and
* flags it will be replaced. Invitation should be notified individually so we use a random
* generator to provide a unique request code and reuse it for the notification.
*/
int uniqueId = Utils.getUniqueIdForPendingIntent();
PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent,
PendingIntent.FLAG_ONE_SHOT);
String displayName = RcsContactUtil.getInstance(this).getDisplayName(vshDao.getContact());
String notifTitle = getString(R.string.title_recv_video_sharing);
/* Create notification */
NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
notif.setContentIntent(contentIntent);
notif.setSmallIcon(R.drawable.ri_notif_csh_icon);
notif.setWhen(System.currentTimeMillis());
notif.setAutoCancel(true);
notif.setOnlyAlertOnce(true);
notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
notif.setDefaults(Notification.DEFAULT_VIBRATE);
notif.setContentTitle(notifTitle);
notif.setContentText(getString(R.string.label_from_args, displayName));
/* Send notification */
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(uniqueId, notif.build());
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/video/VideoSharingInvitationReceiver.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.video;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Video sharing invitation receiver
*
* @author YPLO6403
*/
public class VideoSharingInvitationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, VideoSharingIntentService.class);
context.startService(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/video/VideoSharingLogView.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.video;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* A class to view the persisted information for video sharing
* Created by Philippe LEMORDANT.
*/
public class VideoSharingLogView extends RcsActivity {
private static final String EXTRA_SHARING_ID = "id";
private String mSharingId;
private TextView mTxtViewContact;
private TextView mTxtViewDate;
private TextView mTxtViewDir;
private TextView mTxtViewDuration;
private TextView mTxtViewEncoding;
private TextView mTxtViewHeight;
private TextView mTxtViewReason;
private TextView mTxtViewState;
private TextView mTxtViewWidth;
private SimpleDateFormat sDateFormat;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sharing_log_video_item);
initialize();
mSharingId = getIntent().getStringExtra(EXTRA_SHARING_ID);
}
private void initialize() {
mTxtViewContact = (TextView) findViewById(R.id.history_log_item_contact);
mTxtViewState = (TextView) findViewById(R.id.history_log_item_state);
mTxtViewReason = (TextView) findViewById(R.id.history_log_item_reason);
mTxtViewDir = (TextView) findViewById(R.id.history_log_item_direction);
mTxtViewDate = (TextView) findViewById(R.id.history_log_item_date);
mTxtViewDuration = (TextView) findViewById(R.id.history_log_item_duration);
mTxtViewHeight = (TextView) findViewById(R.id.history_log_item_height);
mTxtViewWidth = (TextView) findViewById(R.id.history_log_item_width);
mTxtViewEncoding = (TextView) findViewById(R.id.history_log_item_encoding);
}
private String getDateFromDb(long timestamp) {
if (0 == timestamp) {
return "";
}
if (sDateFormat == null) {
sDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
}
return sDateFormat.format(new Date(timestamp));
}
@Override
protected void onResume() {
super.onResume();
VideoSharingDAO dao = VideoSharingDAO.getVideoSharingDAO(this, mSharingId);
if (dao == null) {
showMessageThenExit(R.string.error_item_not_found);
return;
}
mTxtViewContact.setText(dao.getContact().toString());
mTxtViewState.setText(RiApplication.sVideoSharingStates[dao.getState().toInt()]);
mTxtViewReason.setText(RiApplication.sVideoReasonCodes[dao.getReasonCode().toInt()]);
mTxtViewDir.setText(RiApplication.getDirection(dao.getDirection()));
mTxtViewDate.setText(getDateFromDb(dao.getTimestamp()));
mTxtViewDuration.setText(DateUtils.formatElapsedTime(dao.getDuration() / 1000));
mTxtViewHeight.setText(String.valueOf(dao.getHeight()));
mTxtViewWidth.setText(String.valueOf(dao.getWidth()));
mTxtViewEncoding.setText(dao.getVideoEncoding());
}
/**
* Start activity to view details of video sharing record
*
* @param context the context
* @param sharingId the sharing ID
*/
public static void startActivity(Context context, String sharingId) {
Intent intent = new Intent(context, VideoSharingLogView.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EXTRA_SHARING_ID, sharingId);
context.startActivity(intent);
}
}
================================================
FILE: RI/src/com/gsma/rcs/ri/sharing/video/media/FifoBuffer.java
================================================
/*******************************************************************************
* Software Name : RCS IMS Stack
*
* Copyright (C) 2010-2016 Orange.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.gsma.rcs.ri.sharing.video.media;
import java.util.Vector;
/**
* FIFO buffer
*
* @author Jean-Marc AUFFRET
*/
public class FifoBuffer {
/**
* Number of objects in the buffer
*/
private int nbObjects = 0;
/**
* Buffer of objects
*/
private Vector