diff --git a/build.gradle b/build.gradle index b1fb63fcd..bcf64f88e 100644 --- a/build.gradle +++ b/build.gradle @@ -35,14 +35,14 @@ ext { lintAbortOnError = false // QuickBlox SDK version - qbSdkVersion = '3.7.0' + qbChatAdapterVersion = '2.0' + qbSdkVersion = '3.8.0' - versionCode = 370 - versionName = '3.7.0' + versionCode = 380 + versionName = '3.8.0' testRunnerVersion = "0.4.1" - // Dependency versions playServicesVersion = '11.4.2' supportLibVersion = '26.1.0' @@ -53,14 +53,12 @@ ext { designVersion = supportLibVersion cardViewVersion = supportLibVersion - uilVersion = '1.9.0' glideVersion = '3.6.1' pullToRefreshVersion = '3.2.3' - stickyListHeaders = '2.7.0' robotoTextViewVersion = '4.0.0' - stickersVersion = '0.7.3' crashlyticsVersion = '2.8.0' fabricToolsVersion = '1.25.1' + stickylistheadersVersion = '0.4.2' swipyVersion = '1.2.3' dimensionDefault = 'default' diff --git a/sample-chat/build.gradle b/sample-chat/build.gradle index a8031b656..46e91faae 100644 --- a/sample-chat/build.gradle +++ b/sample-chat/build.gradle @@ -74,14 +74,14 @@ android { dependencies { - implementation (project(":sample-core")) + implementation(project(":sample-core")) - implementation ("com.quickblox:quickblox-android-sdk-chat:$rootProject.qbSdkVersion") - implementation("com.quickblox:quickblox-android-sdk-content:$rootProject.qbSdkVersion") + implementation "com.quickblox:quickblox-android-sdk-chat:$rootProject.qbSdkVersion" + implementation "com.quickblox:quickblox-android-sdk-content:$rootProject.qbSdkVersion" + implementation "com.quickblox:chat-message-adapter:$rootProject.qbChatAdapterVersion" + implementation "com.timehop.stickyheadersrecyclerview:library:$rootProject.stickylistheadersVersion@aar" implementation "com.github.orangegangsters:swipy:$rootProject.swipyVersion@aar" - implementation "com.github.bumptech.glide:glide:${rootProject.glideVersion}" - implementation (name: 'sticky-list-headers', ext: 'aar') } apply from: "../artifacts.gradle" diff --git a/sample-chat/libs/sticky-list-headers.aar b/sample-chat/libs/sticky-list-headers.aar deleted file mode 100644 index b034d584f..000000000 Binary files a/sample-chat/libs/sticky-list-headers.aar and /dev/null differ diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/ChatActivity.java b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/ChatActivity.java index a7f786cf0..3440cb120 100644 --- a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/ChatActivity.java +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/ChatActivity.java @@ -6,6 +6,10 @@ import android.os.PersistableBundle; import android.support.design.widget.Snackbar; import android.support.v7.app.ActionBar; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.LinearSmoothScroller; +import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Log; import android.view.Menu; @@ -17,8 +21,8 @@ import com.quickblox.chat.QBChatService; import com.quickblox.chat.model.QBAttachment; -import com.quickblox.chat.model.QBChatMessage; import com.quickblox.chat.model.QBChatDialog; +import com.quickblox.chat.model.QBChatMessage; import com.quickblox.chat.model.QBDialogType; import com.quickblox.core.QBEntityCallback; import com.quickblox.core.exception.QBResponseException; @@ -36,7 +40,9 @@ import com.quickblox.sample.core.utils.Toaster; import com.quickblox.sample.core.utils.imagepick.ImagePickHelper; import com.quickblox.sample.core.utils.imagepick.OnImagePickedListener; +import com.quickblox.ui.kit.chatmessage.adapter.listeners.QBChatAttachClickListener; import com.quickblox.users.model.QBUser; +import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.SmackException; @@ -48,32 +54,31 @@ import java.util.Collections; import java.util.List; -import se.emilsjolander.stickylistheaders.StickyListHeadersListView; - public class ChatActivity extends BaseActivity implements OnImagePickedListener { private static final String TAG = ChatActivity.class.getSimpleName(); private static final int REQUEST_CODE_ATTACHMENT = 721; private static final int REQUEST_CODE_SELECT_PEOPLE = 752; - private static final String PROPERTY_SAVE_TO_HISTORY = "save_to_history"; - public static final String EXTRA_DIALOG_ID = "dialogId"; private ProgressBar progressBar; - private StickyListHeadersListView messagesListView; private EditText messageEditText; private LinearLayout attachmentPreviewContainerLayout; private Snackbar snackbar; private ChatAdapter chatAdapter; + private RecyclerView chatMessagesRecyclerView; + protected List messagesList; private AttachmentPreviewAdapter attachmentPreviewAdapter; private ConnectionListener chatConnectionListener; + private ImageAttachClickListener imageAttachClickListener; private QBChatDialog qbChatDialog; private ArrayList unShownMessages; private int skipPagination = 0; private ChatMessageListener chatMessageListener; + private boolean checkAdapterInit; public static void startForResult(Activity activity, int code, QBChatDialog dialogId) { Intent intent = new Intent(activity, ChatActivity.class); @@ -93,13 +98,15 @@ public void onCreate(Bundle savedInstanceState) { Log.v(TAG, "deserialized dialog = " + qbChatDialog); qbChatDialog.initForChat(QBChatService.getInstance()); + initViews(); + initMessagesRecyclerView(); + chatMessageListener = new ChatMessageListener(); qbChatDialog.addMessageListener(chatMessageListener); initChatConnectionListener(); - initViews(); initChat(); } @@ -122,12 +129,14 @@ protected void onRestoreInstanceState(Bundle savedInstanceState) { @Override protected void onResume() { super.onResume(); + addChatMessagesAdapterListeners(); ChatHelper.getInstance().addConnectionListener(chatConnectionListener); } @Override protected void onPause() { super.onPause(); + removeChatMessagesAdapterListeners(); ChatHelper.getInstance().removeConnectionListener(chatConnectionListener); } @@ -276,21 +285,28 @@ public void onAttachmentsClick(View view) { } public void showMessage(QBChatMessage message) { - if (chatAdapter != null) { + if (isAdapterConnected()) { chatAdapter.add(message); scrollMessageListDown(); } else { - if (unShownMessages == null) { - unShownMessages = new ArrayList<>(); - } - unShownMessages.add(message); + delayShowMessage(message); } } + private boolean isAdapterConnected() { + return checkAdapterInit; + } + + private void delayShowMessage(QBChatMessage message) { + if (unShownMessages == null) { + unShownMessages = new ArrayList<>(); + } + unShownMessages.add(message); + } + private void initViews() { actionBar.setDisplayHomeAsUpEnabled(true); - messagesListView = _findViewById(R.id.list_chat_messages); messageEditText = _findViewById(R.id.edit_chat_message); progressBar = _findViewById(R.id.progress_chat); attachmentPreviewContainerLayout = _findViewById(R.id.layout_attachment_preview_container); @@ -317,6 +333,23 @@ public void onClick(View v) { previewAdapterView.setAdapter(attachmentPreviewAdapter); } + private void initMessagesRecyclerView() { + chatMessagesRecyclerView = findViewById(R.id.list_chat_messages); + + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + layoutManager.setStackFromEnd(true); + chatMessagesRecyclerView.setLayoutManager(layoutManager); + + messagesList = new ArrayList<>(); + chatAdapter = new ChatAdapter(this, qbChatDialog, messagesList); + chatAdapter.setPaginationHistoryListener(new PaginationListener()); + chatMessagesRecyclerView.addItemDecoration( + new StickyRecyclerHeadersDecoration(chatAdapter)); + + chatMessagesRecyclerView.setAdapter(chatAdapter); + imageAttachClickListener = new ImageAttachClickListener(); + } + private void sendChatMessage(String text, QBAttachment attachment) { QBChatMessage chatMessage = new QBChatMessage(); if (attachment != null) { @@ -324,11 +357,11 @@ private void sendChatMessage(String text, QBAttachment attachment) { } else { chatMessage.setBody(text); } - chatMessage.setProperty(PROPERTY_SAVE_TO_HISTORY, "1"); + chatMessage.setSaveToHistory(true); chatMessage.setDateSent(System.currentTimeMillis() / 1000); chatMessage.setMarkable(true); - if (!QBDialogType.PRIVATE.equals(qbChatDialog.getType()) && !qbChatDialog.isJoined()){ + if (!QBDialogType.PRIVATE.equals(qbChatDialog.getType()) && !qbChatDialog.isJoined()) { Toaster.shortToast("You're still joining a group chat, please wait a bit"); return; } @@ -402,6 +435,7 @@ private void releaseChat() { leaveGroupDialog(); } } + private void updateDialog(final ArrayList selectedUsers) { ChatHelper.getInstance().updateDialogUsers(qbChatDialog, selectedUsers, new QBEntityCallback() { @@ -463,48 +497,12 @@ public void onSuccess(ArrayList messages, Bundle args) { // The newest messages should be in the end of list, // so we need to reverse list to show messages in the right order Collections.reverse(messages); - if (chatAdapter == null) { - chatAdapter = new ChatAdapter(ChatActivity.this, qbChatDialog, messages); - chatAdapter.setPaginationHistoryListener(new PaginationHistoryListener() { - @Override - public void downloadMore() { - loadChatHistory(); - } - }); - chatAdapter.setOnItemInfoExpandedListener(new ChatAdapter.OnItemInfoExpandedListener() { - @Override - public void onItemInfoExpanded(final int position) { - if (isLastItem(position)) { - // HACK need to allow info textview visibility change so posting it via handler - runOnUiThread(new Runnable() { - @Override - public void run() { - messagesListView.setSelection(position); - } - }); - } else { - messagesListView.smoothScrollToPosition(position); - } - } - - private boolean isLastItem(int position) { - return position == chatAdapter.getCount() - 1; - } - }); - if (unShownMessages != null && !unShownMessages.isEmpty()) { - List chatList = chatAdapter.getList(); - for (QBChatMessage message : unShownMessages) { - if (!chatList.contains(message)) { - chatAdapter.add(message); - } - } - } - messagesListView.setAdapter(chatAdapter); - messagesListView.setAreHeadersSticky(false); - messagesListView.setDivider(null); - } else { + if (!checkAdapterInit) { + checkAdapterInit = true; chatAdapter.addList(messages); - messagesListView.setSelection(messages.size()); + addDelayedMessagesToAdapter(); + } else { + chatAdapter.addToList(messages); } progressBar.setVisibility(View.GONE); } @@ -519,8 +517,19 @@ public void onError(QBResponseException e) { skipPagination += ChatHelper.CHAT_HISTORY_ITEMS_PER_PAGE; } + private void addDelayedMessagesToAdapter() { + if (unShownMessages != null && !unShownMessages.isEmpty()) { + List chatList = chatAdapter.getList(); + for (QBChatMessage message : unShownMessages) { + if (!chatList.contains(message)) { + chatAdapter.add(message); + } + } + } + } + private void scrollMessageListDown() { - messagesListView.setSelection(messagesListView.getCount() - 1); + chatMessagesRecyclerView.scrollToPosition(messagesList.size() - 1); } private void deleteChat() { @@ -552,7 +561,7 @@ public void reconnectionSuccessful() { skipPagination = 0; switch (qbChatDialog.getType()) { case GROUP: - chatAdapter = null; + checkAdapterInit = false; // Join active room if we're in Group Chat runOnUiThread(new Runnable() { @Override @@ -566,10 +575,35 @@ public void run() { }; } - public class ChatMessageListener extends QbChatDialogMessageListenerImp { + private void addChatMessagesAdapterListeners() { + chatAdapter.setAttachImageClickListener(imageAttachClickListener); + } + + private void removeChatMessagesAdapterListeners() { + chatAdapter.removeAttachImageClickListener(imageAttachClickListener); + } + + private class ChatMessageListener extends QbChatDialogMessageListenerImp { @Override public void processMessage(String s, QBChatMessage qbChatMessage, Integer integer) { showMessage(qbChatMessage); } } + + private class ImageAttachClickListener implements QBChatAttachClickListener { + + @Override + public void onLinkClicked(QBAttachment qbAttachment, int position) { + AttachmentImageActivity.start(ChatActivity.this, qbAttachment.getUrl()); + } + } + + private class PaginationListener implements PaginationHistoryListener { + + @Override + public void downloadMore() { + Log.w(TAG, "downloadMore"); + loadChatHistory(); + } + } } diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/DialogsActivity.java b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/DialogsActivity.java index cfab43959..4b4af32a8 100644 --- a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/DialogsActivity.java +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/DialogsActivity.java @@ -33,6 +33,7 @@ import com.quickblox.core.QBEntityCallback; import com.quickblox.core.exception.QBResponseException; import com.quickblox.core.request.QBRequestGetBuilder; +import com.quickblox.messages.services.QBPushManager; import com.quickblox.messages.services.SubscribeService; import com.quickblox.sample.chat.R; import com.quickblox.sample.chat.managers.DialogsManager; @@ -40,11 +41,13 @@ import com.quickblox.sample.chat.utils.chat.ChatHelper; import com.quickblox.sample.chat.utils.qb.QbChatDialogMessageListenerImp; import com.quickblox.sample.chat.utils.qb.QbDialogHolder; +import com.quickblox.sample.chat.utils.qb.callback.QBPushSubscribeListenerImpl; import com.quickblox.sample.chat.utils.qb.callback.QbEntityCallbackImpl; import com.quickblox.sample.core.gcm.GooglePlayServicesHelper; import com.quickblox.sample.core.ui.dialog.ProgressDialogFragment; import com.quickblox.sample.core.utils.SharedPrefsHelper; import com.quickblox.sample.core.utils.constant.GcmConsts; +import com.quickblox.users.QBUsers; import com.quickblox.users.model.QBUser; import java.util.ArrayList; @@ -223,7 +226,7 @@ public ActionMode startSupportActionMode(ActionMode.Callback callback) { private void userLogout() { ChatHelper.getInstance().destroy(); - SubscribeService.unSubscribeFromPushes(DialogsActivity.this); + logout(); SharedPrefsHelper.getInstance().removeQbUser(); LoginActivity.start(DialogsActivity.this); QbDialogHolder.getInstance().clear(); @@ -231,6 +234,25 @@ private void userLogout() { finish(); } + private void logout() { + if (QBPushManager.getInstance().isSubscribedToPushes()) { + QBPushManager.getInstance().addListener(new QBPushSubscribeListenerImpl() { + @Override + public void onSubscriptionDeleted(boolean success) { + logoutREST(); + QBPushManager.getInstance().removeListener(this); + } + }); + SubscribeService.unSubscribeFromPushes(DialogsActivity.this); + } else { + logoutREST(); + } + } + + private void logoutREST() { + QBUsers.signOut().performAsync(null); + } + private void updateDialogsList() { requestBuilder.setSkip(skipRecords = 0); loadDialogsFromQb(true, true); diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/SelectUsersActivity.java b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/SelectUsersActivity.java index 6417b1017..276592fd3 100644 --- a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/SelectUsersActivity.java +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/activity/SelectUsersActivity.java @@ -102,7 +102,7 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_select_people_action_done: if (usersAdapter != null) { - List users = usersAdapter.getSelectedUsers(); + List users = new ArrayList<>(usersAdapter.getSelectedUsers()); if (users.size() >= MINIMUM_CHAT_OCCUPANTS_SIZE) { passResultToCallerActivity(); } else { diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/ChatAdapter.java b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/ChatAdapter.java index 69cf9391c..0e5a667d8 100644 --- a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/ChatAdapter.java +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/ChatAdapter.java @@ -1,50 +1,37 @@ package com.quickblox.sample.chat.ui.adapter; -import android.annotation.SuppressLint; import android.content.Context; +import android.support.v7.widget.RecyclerView; import android.util.Log; -import android.view.Gravity; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; import android.widget.TextView; -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.target.Target; import com.quickblox.chat.model.QBAttachment; import com.quickblox.chat.model.QBChatDialog; import com.quickblox.chat.model.QBChatMessage; import com.quickblox.core.helper.CollectionsUtil; import com.quickblox.sample.chat.R; -import com.quickblox.sample.chat.ui.activity.AttachmentImageActivity; -import com.quickblox.sample.chat.ui.widget.MaskedImageView; -import com.quickblox.sample.chat.utils.Consts; import com.quickblox.sample.chat.utils.TimeUtils; import com.quickblox.sample.chat.utils.chat.ChatHelper; import com.quickblox.sample.chat.utils.qb.PaginationHistoryListener; import com.quickblox.sample.chat.utils.qb.QbUsersHolder; -import com.quickblox.sample.core.ui.adapter.BaseListAdapter; import com.quickblox.sample.core.utils.ResourceUtils; +import com.quickblox.sample.core.utils.UiUtils; +import com.quickblox.ui.kit.chatmessage.adapter.QBMessagesAdapter; import com.quickblox.users.model.QBUser; +import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; -import java.util.Collection; import java.util.List; -import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; - -public class ChatAdapter extends BaseListAdapter implements StickyListHeadersAdapter { +public class ChatAdapter extends QBMessagesAdapter implements StickyRecyclerHeadersAdapter { private static final String TAG = ChatAdapter.class.getSimpleName(); private final QBChatDialog chatDialog; - private OnItemInfoExpandedListener onItemInfoExpandedListener; private PaginationHistoryListener paginationListener; private int previousGetCount = 0; @@ -53,118 +40,85 @@ public ChatAdapter(Context context, QBChatDialog chatDialog, List this.chatDialog = chatDialog; } - public void setOnItemInfoExpandedListener(OnItemInfoExpandedListener onItemInfoExpandedListener) { - this.onItemInfoExpandedListener = onItemInfoExpandedListener; + public void addToList(List items) { + chatMessages.addAll(0, items); + notifyItemRangeInserted(0, items.size()); } @Override - public View getView(final int position, View convertView, ViewGroup parent) { - final ViewHolder holder; - if (convertView == null) { - holder = new ViewHolder(); - convertView = inflater.inflate(R.layout.list_item_chat_message, parent, false); - - holder.messageBodyTextView = (TextView) convertView.findViewById(R.id.text_image_message); - holder.messageAuthorTextView = (TextView) convertView.findViewById(R.id.text_message_author); - holder.messageContainerLayout = (LinearLayout) convertView.findViewById(R.id.layout_chat_message_container); - holder.messageBodyContainerLayout = (RelativeLayout) convertView.findViewById(R.id.layout_message_content_container); - holder.messageInfoTextView = (TextView) convertView.findViewById(R.id.text_message_info); - holder.attachmentImageView = (MaskedImageView) convertView.findViewById(R.id.image_message_attachment); - holder.attachmentProgressBar = (ProgressBar) convertView.findViewById(R.id.progress_message_attachment); + public void add(QBChatMessage item) { + this.chatMessages.add(item); + this.notifyItemInserted(chatMessages.size() - 1); + } - convertView.setTag(holder); - } else { - holder = (ViewHolder) convertView.getTag(); + @Override + public void onBindViewHolder(QBMessageViewHolder holder, int position) { + downloadMore(position); + QBChatMessage chatMessage = getItem(position); + if (isIncoming(chatMessage) && !isRead(chatMessage)) { + readMessage(chatMessage); } + super.onBindViewHolder(holder, position); + } - final QBChatMessage chatMessage = getItem(position); + @Override + public String getImageUrl(int position) { + QBAttachment attachment = getQBAttach(position); + return attachment.getUrl(); + } - setIncomingOrOutgoingMessageAttributes(holder, chatMessage); - setMessageBody(holder, chatMessage); - setMessageInfo(chatMessage, holder); - setMessageAuthor(holder, chatMessage); + @Override + protected void onBindViewMsgLeftHolder(TextMessageHolder holder, QBChatMessage chatMessage, int position) { + holder.timeTextMessageTextView.setVisibility(View.GONE); + TextView opponentNameTextView = holder.itemView.findViewById(R.id.opponent_name_text_view); + opponentNameTextView.setTextColor(UiUtils.getRandomTextColorById(chatMessage.getSenderId())); + opponentNameTextView.setText(getSenderName(chatMessage)); - holder.messageContainerLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (hasAttachments(chatMessage)) { - Collection attachments = chatMessage.getAttachments(); - QBAttachment attachment = attachments.iterator().next(); - AttachmentImageActivity.start(context, attachment.getUrl()); - } else { - toggleItemInfo(holder, position); - } - } - }); - holder.messageContainerLayout.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (hasAttachments(chatMessage)) { - toggleItemInfo(holder, position); - return true; - } + TextView customMessageTimeTextView = holder.itemView.findViewById(R.id.custom_msg_text_time_message); + customMessageTimeTextView.setText(getDate(chatMessage.getDateSent())); - return false; - } - }); - holder.messageInfoTextView.setVisibility(View.GONE); + super.onBindViewMsgLeftHolder(holder, chatMessage, position); + } - if (isIncoming(chatMessage) && !isRead(chatMessage)){ - readMessage(chatMessage); - } + @Override + protected void onBindViewAttachLeftHolder(ImageAttachHolder holder, QBChatMessage chatMessage, int position) { + TextView opponentNameTextView = holder.itemView.findViewById(R.id.opponent_name_attach_view); + opponentNameTextView.setTextColor(UiUtils.getRandomTextColorById(chatMessage.getSenderId())); + opponentNameTextView.setText(getSenderName(chatMessage)); - downloadMore(position); + super.onBindViewAttachLeftHolder(holder, chatMessage, position); + } - return convertView; + private String getSenderName(QBChatMessage chatMessage) { + QBUser sender = QbUsersHolder.getInstance().getUserById(chatMessage.getSenderId()); + return sender.getFullName(); } - private void downloadMore(int position) { - if (position == 0) { - if (getCount() != previousGetCount) { - paginationListener.downloadMore(); - previousGetCount = getCount(); - } + private void readMessage(QBChatMessage chatMessage) { + try { + chatDialog.readMessage(chatMessage); + } catch (XMPPException | SmackException.NotConnectedException e) { + Log.w(TAG, e); } } - public void setPaginationHistoryListener(PaginationHistoryListener paginationListener) { - this.paginationListener = paginationListener; + private boolean isRead(QBChatMessage chatMessage) { + Integer currentUserId = ChatHelper.getCurrentUser().getId(); + return !CollectionsUtil.isEmpty(chatMessage.getReadIds()) && chatMessage.getReadIds().contains(currentUserId); } - private void toggleItemInfo(ViewHolder holder, int position) { - boolean isMessageInfoVisible = holder.messageInfoTextView.getVisibility() == View.VISIBLE; - holder.messageInfoTextView.setVisibility(isMessageInfoVisible ? View.GONE : View.VISIBLE); - - if (onItemInfoExpandedListener != null) { - onItemInfoExpandedListener.onItemInfoExpanded(position); - } + public void setPaginationHistoryListener(PaginationHistoryListener paginationListener) { + this.paginationListener = paginationListener; } - @Override - public View getHeaderView(int position, View convertView, ViewGroup parent) { - HeaderViewHolder holder; - if (convertView == null) { - holder = new HeaderViewHolder(); - convertView = inflater.inflate(R.layout.view_chat_message_header, parent, false); - holder.dateTextView = (TextView) convertView.findViewById(R.id.header_date_textview); - convertView.setTag(holder); - } else { - holder = (HeaderViewHolder) convertView.getTag(); - } - - QBChatMessage chatMessage = getItem(position); - holder.dateTextView.setText(TimeUtils.getDate(chatMessage.getDateSent() * 1000)); - - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) holder.dateTextView.getLayoutParams(); + private void downloadMore(int position) { if (position == 0) { - lp.topMargin = ResourceUtils.getDimen(R.dimen.chat_date_header_top_margin); - } else { - lp.topMargin = 0; + if (getItemCount() != previousGetCount) { + paginationListener.downloadMore(); + previousGetCount = getItemCount(); + } } - holder.dateTextView.setLayoutParams(lp); - - return convertView; } @Override @@ -173,141 +127,27 @@ public long getHeaderId(int position) { return TimeUtils.getDateAsHeaderId(chatMessage.getDateSent() * 1000); } - private void setMessageBody(final ViewHolder holder, QBChatMessage chatMessage) { - if (hasAttachments(chatMessage)) { - Collection attachments = chatMessage.getAttachments(); - QBAttachment attachment = attachments.iterator().next(); - - holder.messageBodyTextView.setVisibility(View.GONE); - holder.attachmentImageView.setVisibility(View.VISIBLE); - holder.attachmentProgressBar.setVisibility(View.VISIBLE); - Glide.with(context) - .load(attachment.getUrl()) - .listener(new RequestListener() { - @Override - public boolean onException(Exception e, String model, - Target target, boolean isFirstResource) { - e.printStackTrace(); - holder.attachmentImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - holder.attachmentProgressBar.setVisibility(View.GONE); - return false; - } - - @Override - public boolean onResourceReady(GlideDrawable resource, String model, - Target target, - boolean isFromMemoryCache, boolean isFirstResource) { - holder.attachmentImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); - holder.attachmentProgressBar.setVisibility(View.GONE); - return false; - } - }) - .override(Consts.PREFERRED_IMAGE_SIZE_PREVIEW, Consts.PREFERRED_IMAGE_SIZE_PREVIEW) - .dontTransform() - .error(R.drawable.ic_error) - .into(holder.attachmentImageView); - } else { - holder.messageBodyTextView.setText(chatMessage.getBody()); - holder.messageBodyTextView.setVisibility(View.VISIBLE); - holder.attachmentImageView.setVisibility(View.GONE); - holder.attachmentProgressBar.setVisibility(View.GONE); - } - } - - private void setMessageAuthor(ViewHolder holder, QBChatMessage chatMessage) { - if (isIncoming(chatMessage)) { - QBUser sender = QbUsersHolder.getInstance().getUserById(chatMessage.getSenderId()); - holder.messageAuthorTextView.setText(sender.getFullName()); - holder.messageAuthorTextView.setVisibility(View.VISIBLE); - - if (hasAttachments(chatMessage)) { - holder.messageAuthorTextView.setBackgroundResource(R.drawable.shape_rectangle_semi_transparent); - holder.messageAuthorTextView.setTextColor(ResourceUtils.getColor(R.color.text_color_white)); - } else { - holder.messageAuthorTextView.setBackgroundResource(0); - holder.messageAuthorTextView.setTextColor(ResourceUtils.getColor(R.color.text_color_dark_grey)); - } - } else { - holder.messageAuthorTextView.setVisibility(View.GONE); - } + @Override + public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent) { + View view = inflater.inflate(R.layout.view_chat_message_header, parent, false); + return new RecyclerView.ViewHolder(view) { + }; } - private void setMessageInfo(QBChatMessage chatMessage, ViewHolder holder) { - holder.messageInfoTextView.setText(TimeUtils.getTime(chatMessage.getDateSent() * 1000)); - } + @Override + public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder, int position) { + View view = holder.itemView; + TextView dateTextView = view.findViewById(R.id.header_date_textview); - @SuppressLint("RtlHardcoded") - private void setIncomingOrOutgoingMessageAttributes(ViewHolder holder, QBChatMessage chatMessage) { - boolean isIncoming = isIncoming(chatMessage); - int gravity = isIncoming ? Gravity.LEFT : Gravity.RIGHT; - holder.messageContainerLayout.setGravity(gravity); - holder.messageInfoTextView.setGravity(gravity); + QBChatMessage chatMessage = getItem(position); + dateTextView.setText(TimeUtils.getDate(chatMessage.getDateSent() * 1000)); - int messageBodyContainerBgResource = isIncoming - ? R.drawable.incoming_message_bg - : R.drawable.outgoing_message_bg; - if (hasAttachments(chatMessage)) { - holder.messageBodyContainerLayout.setBackgroundResource(0); - holder.messageBodyContainerLayout.setPadding(0, 0, 0, 0); - holder.attachmentImageView.setMaskResourceId(messageBodyContainerBgResource); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) dateTextView.getLayoutParams(); + if (position == 0) { + lp.topMargin = ResourceUtils.getDimen(R.dimen.chat_date_header_top_margin); } else { - holder.messageBodyContainerLayout.setBackgroundResource(messageBodyContainerBgResource); - } - - RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) holder.messageAuthorTextView.getLayoutParams(); - if (isIncoming && hasAttachments(chatMessage)) { - lp.leftMargin = ResourceUtils.getDimen(R.dimen.chat_message_attachment_username_margin); - lp.topMargin = ResourceUtils.getDimen(R.dimen.chat_message_attachment_username_margin); - } else if (isIncoming) { - lp.leftMargin = ResourceUtils.getDimen(R.dimen.chat_message_username_margin); lp.topMargin = 0; } - holder.messageAuthorTextView.setLayoutParams(lp); - - int textColorResource = isIncoming - ? R.color.text_color_black - : R.color.text_color_white; - holder.messageBodyTextView.setTextColor(ResourceUtils.getColor(textColorResource)); - } - - private boolean hasAttachments(QBChatMessage chatMessage) { - Collection attachments = chatMessage.getAttachments(); - return attachments != null && !attachments.isEmpty(); - } - - private boolean isIncoming(QBChatMessage chatMessage) { - QBUser currentUser = ChatHelper.getCurrentUser(); - return chatMessage.getSenderId() != null && !chatMessage.getSenderId().equals(currentUser.getId()); - } - - private boolean isRead(QBChatMessage chatMessage){ - Integer currentUserId = ChatHelper.getCurrentUser().getId(); - return !CollectionsUtil.isEmpty(chatMessage.getReadIds()) && chatMessage.getReadIds().contains(currentUserId); - } - - private void readMessage(QBChatMessage chatMessage){ - try { - chatDialog.readMessage(chatMessage); - } catch (XMPPException | SmackException.NotConnectedException e) { - Log.w(TAG, e); - } - } - - private static class HeaderViewHolder { - public TextView dateTextView; - } - - private static class ViewHolder { - public TextView messageBodyTextView; - public TextView messageAuthorTextView; - public TextView messageInfoTextView; - public LinearLayout messageContainerLayout; - public RelativeLayout messageBodyContainerLayout; - public MaskedImageView attachmentImageView; - public ProgressBar attachmentProgressBar; - } - - public interface OnItemInfoExpandedListener { - void onItemInfoExpanded(int position); + dateTextView.setLayoutParams(lp); } -} +} \ No newline at end of file diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/CheckboxUsersAdapter.java b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/CheckboxUsersAdapter.java index 9465305d4..46446ee31 100644 --- a/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/CheckboxUsersAdapter.java +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/ui/adapter/CheckboxUsersAdapter.java @@ -7,16 +7,18 @@ import com.quickblox.users.model.QBUser; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class CheckboxUsersAdapter extends UsersAdapter { private List initiallySelectedUsers; - private List selectedUsers; + private Set selectedUsers; public CheckboxUsersAdapter(Context context, List users) { super(context, users); - this.selectedUsers = new ArrayList<>(); + selectedUsers = new HashSet<>(); this.selectedUsers.add(currentUser); this.initiallySelectedUsers = new ArrayList<>(); @@ -63,7 +65,7 @@ public void onClick(View v) { return view; } - public List getSelectedUsers() { + public Set getSelectedUsers() { return selectedUsers; } diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/utils/chat/ChatHelper.java b/sample-chat/src/main/java/com/quickblox/sample/chat/utils/chat/ChatHelper.java index 22dffa0d6..62c26f909 100644 --- a/sample-chat/src/main/java/com/quickblox/sample/chat/utils/chat/ChatHelper.java +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/utils/chat/ChatHelper.java @@ -1,6 +1,5 @@ package com.quickblox.sample.chat.utils.chat; -import android.content.Context; import android.os.Bundle; import android.util.Log; @@ -23,11 +22,9 @@ import com.quickblox.core.helper.StringifyArrayList; import com.quickblox.core.request.QBPagedRequestBuilder; import com.quickblox.core.request.QBRequestGetBuilder; -import com.quickblox.messages.services.SubscribeService; import com.quickblox.sample.chat.App; import com.quickblox.sample.chat.R; import com.quickblox.sample.chat.models.SampleConfigs; -import com.quickblox.sample.chat.ui.activity.DialogsActivity; import com.quickblox.sample.chat.utils.qb.QbDialogHolder; import com.quickblox.sample.chat.utils.qb.QbDialogUtils; import com.quickblox.sample.chat.utils.qb.QbUsersHolder; @@ -196,12 +193,20 @@ public void exitFromDialog(QBChatDialog qbDialog, QBEntityCallback callback.onError(new QBResponseException(e.getMessage())); } + QBUser currentUser = QBChatService.getInstance().getUser(); QBDialogRequestBuilder qbRequestBuilder = new QBDialogRequestBuilder(); - qbRequestBuilder.removeUsers(QBChatService.getInstance().getUser().getId()); + qbRequestBuilder.removeUsers(currentUser.getId()); + + qbDialog.setName(buildDialogNameWithoutUser(qbDialog.getName(), currentUser.getFullName())); QBRestChatService.updateGroupChatDialog(qbDialog, qbRequestBuilder).performAsync(callback); } + private static String buildDialogNameWithoutUser(String dialogName, String userName) { + String regex = ", " + userName + "|" + userName + ", "; + return dialogName.replaceAll(regex, ""); + } + public void updateDialogUsers(QBChatDialog qbDialog, final List newQbDialogUsersList, QBEntityCallback callback) { diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/utils/qb/QbDialogUtils.java b/sample-chat/src/main/java/com/quickblox/sample/chat/utils/qb/QbDialogUtils.java index dfa8809fa..620af3546 100644 --- a/sample-chat/src/main/java/com/quickblox/sample/chat/utils/qb/QbDialogUtils.java +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/utils/qb/QbDialogUtils.java @@ -19,14 +19,18 @@ public class QbDialogUtils { private static final String TAG = QbDialogUtils.class.getSimpleName(); - public static QBChatDialog createDialog(List users) { - QBUser currentUser = ChatHelper.getCurrentUser(); - users.remove(currentUser); - + if (isPrivateChat(users)) { + QBUser currentUser = ChatHelper.getCurrentUser(); + users.remove(currentUser); + } return DialogUtils.buildDialog(users.toArray(new QBUser[users.size()])); } + private static boolean isPrivateChat(List users) { + return users.size() == 2; + } + public static List getAddedUsers(QBChatDialog dialog, List currentUsers) { return getAddedUsers(getQbUsersFromQbDialog(dialog), currentUsers); } @@ -143,7 +147,7 @@ public static String getOccupantsIdsStringFromList(Collection occupantI return TextUtils.join(",", occupantIdsList); } - public static QBChatDialog buildPrivateChatDialog(String dialogId, Integer recipientId){ + public static QBChatDialog buildPrivateChatDialog(String dialogId, Integer recipientId) { QBChatDialog chatDialog = DialogUtils.buildPrivateDialog(recipientId); chatDialog.setDialogId(dialogId); diff --git a/sample-chat/src/main/java/com/quickblox/sample/chat/utils/qb/callback/QBPushSubscribeListenerImpl.java b/sample-chat/src/main/java/com/quickblox/sample/chat/utils/qb/callback/QBPushSubscribeListenerImpl.java new file mode 100644 index 000000000..91d6cc70b --- /dev/null +++ b/sample-chat/src/main/java/com/quickblox/sample/chat/utils/qb/callback/QBPushSubscribeListenerImpl.java @@ -0,0 +1,25 @@ +package com.quickblox.sample.chat.utils.qb.callback; + +import com.quickblox.messages.services.QBPushManager; + +/** + * Created by roman on 2/28/18. + */ + +public class QBPushSubscribeListenerImpl implements QBPushManager.QBSubscribeListener { + + @Override + public void onSubscriptionCreated() { + + } + + @Override + public void onSubscriptionError(Exception e, int i) { + + } + + @Override + public void onSubscriptionDeleted(boolean b) { + + } +} \ No newline at end of file diff --git a/sample-chat/src/main/res/drawable/chat_date_bubble.xml b/sample-chat/src/main/res/drawable/chat_date_bubble.xml new file mode 100644 index 000000000..67ceb7798 --- /dev/null +++ b/sample-chat/src/main/res/drawable/chat_date_bubble.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/sample-chat/src/main/res/layout/activity_chat.xml b/sample-chat/src/main/res/layout/activity_chat.xml index d0ff441f9..f98c6ab93 100644 --- a/sample-chat/src/main/res/layout/activity_chat.xml +++ b/sample-chat/src/main/res/layout/activity_chat.xml @@ -5,12 +5,11 @@ style="@style/MatchParent" tools:ignore="RtlHardcoded"> - + android:listSelector="@android:color/transparent"/> + + + + + + \ No newline at end of file diff --git a/sample-chat/src/main/res/layout/list_item_chat_message.xml b/sample-chat/src/main/res/layout/list_item_chat_message.xml deleted file mode 100644 index c7f4fcb32..000000000 --- a/sample-chat/src/main/res/layout/list_item_chat_message.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample-chat/src/main/res/layout/list_item_text_left.xml b/sample-chat/src/main/res/layout/list_item_text_left.xml new file mode 100644 index 000000000..1a8eea204 --- /dev/null +++ b/sample-chat/src/main/res/layout/list_item_text_left.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/sample-chat/src/main/res/layout/view_chat_message_header.xml b/sample-chat/src/main/res/layout/view_chat_message_header.xml index c0c91407e..d300ec42e 100644 --- a/sample-chat/src/main/res/layout/view_chat_message_header.xml +++ b/sample-chat/src/main/res/layout/view_chat_message_header.xml @@ -1,8 +1,11 @@ + xmlns:tools="http://schemas.android.com/tools" + style="@style/MatchWidth" + android:gravity="center"> + style="@style/ListDateHeaderStyle" + tools:text="MARCH 06" /> \ No newline at end of file diff --git a/sample-chat/src/main/res/layout/widget_item_attach_left.xml b/sample-chat/src/main/res/layout/widget_item_attach_left.xml new file mode 100644 index 000000000..a3820342f --- /dev/null +++ b/sample-chat/src/main/res/layout/widget_item_attach_left.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/sample-chat/src/main/res/values/colors.xml b/sample-chat/src/main/res/values/colors.xml index 3eb8db997..a9226ea7f 100644 --- a/sample-chat/src/main/res/values/colors.xml +++ b/sample-chat/src/main/res/values/colors.xml @@ -3,6 +3,7 @@ #d3d3d3 #f5f6f5 + #80dedede #e0e0e0 #e0e0e0 diff --git a/sample-chat/src/main/res/values/styles_widgets.xml b/sample-chat/src/main/res/values/styles_widgets.xml index 15aebfd08..41cfa1d4b 100644 --- a/sample-chat/src/main/res/values/styles_widgets.xml +++ b/sample-chat/src/main/res/values/styles_widgets.xml @@ -22,8 +22,9 @@ centerInside - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample-core/src/main/java/com/quickblox/sample/core/utils/UiUtils.java b/sample-core/src/main/java/com/quickblox/sample/core/utils/UiUtils.java index 89926a080..ff7a3a861 100644 --- a/sample-core/src/main/java/com/quickblox/sample/core/utils/UiUtils.java +++ b/sample-core/src/main/java/com/quickblox/sample/core/utils/UiUtils.java @@ -1,5 +1,6 @@ package com.quickblox.sample.core.utils; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.support.annotation.ColorInt; @@ -8,6 +9,8 @@ import com.quickblox.sample.core.CoreApp; import com.quickblox.sample.core.R; +import java.util.HashMap; +import java.util.Map; import java.util.Random; public class UiUtils { @@ -15,10 +18,15 @@ public class UiUtils { private static final int RANDOM_COLOR_START_RANGE = 0; private static final int RANDOM_COLOR_END_RANGE = 9; + private static final int COLOR_MAX_VALUE = 255; + private static final float COLOR_ALPHA = 0.8f; + private static Map colorsMap = new HashMap<>(); + private static final Random random = new Random(); private static int previousColor; - private UiUtils() {} + private UiUtils() { + } public static Drawable getGreyCircleDrawable() { return getColoredCircleDrawable(ResourceUtils.getColor(R.color.color_grey)); @@ -54,11 +62,31 @@ public static int getRandomCircleColor() { } public static int getCircleColor(@IntRange(from = RANDOM_COLOR_START_RANGE, to = RANDOM_COLOR_END_RANGE) - int colorPosition) { + int colorPosition) { String colorIdName = String.format("random_color_%d", colorPosition + 1); int colorId = CoreApp.getInstance().getResources() .getIdentifier(colorIdName, "color", CoreApp.getInstance().getPackageName()); return ResourceUtils.getColor(colorId); } + + public static int getRandomTextColorById(Integer senderId) { + if (colorsMap.get(senderId) != null) { + return colorsMap.get(senderId); + } else { + int colorValue = getRandomColor(); + colorsMap.put(senderId, colorValue); + return colorsMap.get(senderId); + } + } + + public static int getRandomColor() { + float[] hsv = new float[3]; + int color = Color.argb(COLOR_MAX_VALUE, random.nextInt(COLOR_MAX_VALUE), random.nextInt( + COLOR_MAX_VALUE), random.nextInt(COLOR_MAX_VALUE)); + Color.colorToHSV(color, hsv); + hsv[2] *= COLOR_ALPHA; + color = Color.HSVToColor(hsv); + return color; + } } diff --git a/sample-core/src/main/res/values/colors.xml b/sample-core/src/main/res/values/colors.xml index d381987fa..49319c163 100644 --- a/sample-core/src/main/res/values/colors.xml +++ b/sample-core/src/main/res/values/colors.xml @@ -11,6 +11,7 @@ #ffffff #046ad0 + #87cefa #4ad24d #99000000 diff --git a/sample-videochat-webrtc/src/main/AndroidManifest.xml b/sample-videochat-webrtc/src/main/AndroidManifest.xml index f1a91ed8a..6d7219e23 100755 --- a/sample-videochat-webrtc/src/main/AndroidManifest.xml +++ b/sample-videochat-webrtc/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ + diff --git a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/activities/CallActivity.java b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/activities/CallActivity.java index bf65f7b52..06d61f7bc 100755 --- a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/activities/CallActivity.java +++ b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/activities/CallActivity.java @@ -89,7 +89,7 @@ public class CallActivity extends BaseActivity implements QBRTCClientSessionCall private String hangUpReason; private boolean isInCommingCall; private QBRTCClient rtcClient; - private OnChangeDynamicToggle onChangeDynamicCallback; + private OnChangeAudioDevice onChangeAudioDeviceCallback; private ConnectionListener connectionListener; private boolean wifiEnabled = true; private SharedPreferences sharedPref; @@ -105,9 +105,6 @@ public class CallActivity extends BaseActivity implements QBRTCClientSessionCall private boolean isVideoCall; private long expirationReconnectionTime; private int reconnectHangUpTimeMillis; - private boolean headsetPlugged; - private boolean previousDeviceEarPiece; - private boolean showToastAfterHeadsetPlugged = true; private PermissionsChecker checker; private MediaProjectionManager mMediaProjectionManager; @@ -148,9 +145,22 @@ protected void onCreate(Bundle savedInstanceState) { connectionView = (LinearLayout) View.inflate(this, R.layout.connection_popup, null); checker = new PermissionsChecker(getApplicationContext()); + if (!isInCommingCall){ + startAudioManager(); + ringtonePlayer.play(true); + } startSuitableFragment(isInCommingCall); } + private void startAudioManager() { + audioManager.start((selectedAudioDevice, availableAudioDevices) -> { + Toaster.shortToast("Audio device switched to " + selectedAudioDevice); + + if (onChangeAudioDeviceCallback != null) { + onChangeAudioDeviceCallback.audioDeviceChanged(selectedAudioDevice); + } + }); + } private void startScreenSharing(final Intent data){ ScreenShareFragment screenShareFragment = ScreenShareFragment.newIntstance(); @@ -246,21 +256,7 @@ private void parseIntentExtras() { } private void initAudioManager() { - audioManager = AppRTCAudioManager.create(this, new AppRTCAudioManager.OnAudioManagerStateListener() { - @Override - public void onAudioChangedState(AppRTCAudioManager.AudioDevice audioDevice) { - if (callStarted) { - if (audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) { - previousDeviceEarPiece = true; - } else if (audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.SPEAKER_PHONE) { - previousDeviceEarPiece = false; - } - if (showToastAfterHeadsetPlugged) { - Toaster.shortToast("Audio device switched to " + audioDevice); - } - } - } - }); + audioManager = AppRTCAudioManager.create(this); isVideoCall = QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO.equals(currentSession.getConferenceType()); if (isVideoCall) { @@ -268,41 +264,21 @@ public void onAudioChangedState(AppRTCAudioManager.AudioDevice audioDevice) { Log.d(TAG, "AppRTCAudioManager.AudioDevice.SPEAKER_PHONE"); } else { audioManager.setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE); - previousDeviceEarPiece = true; + audioManager.setManageSpeakerPhoneByProximity(SettingsUtil.isManageSpeakerPhoneByProximity(this)); Log.d(TAG, "AppRTCAudioManager.AudioDevice.EARPIECE"); } - audioManager.setOnWiredHeadsetStateListener(new AppRTCAudioManager.OnWiredHeadsetStateListener() { - @Override - public void onWiredHeadsetStateChanged(boolean plugged, boolean hasMicrophone) { - headsetPlugged = plugged; - if (callStarted) { - Toaster.shortToast("Headset " + (plugged ? "plugged" : "unplugged")); - } - if (onChangeDynamicCallback != null) { - if (!plugged) { - showToastAfterHeadsetPlugged = false; - if (previousDeviceEarPiece) { - setAudioDeviceDelayed(AppRTCAudioManager.AudioDevice.EARPIECE); - } else { - setAudioDeviceDelayed(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); - } - } - onChangeDynamicCallback.enableDynamicToggle(plugged, previousDeviceEarPiece); - } + audioManager.setOnWiredHeadsetStateListener((plugged, hasMicrophone) -> { + if (callStarted) { + Toaster.shortToast("Headset " + (plugged ? "plugged" : "unplugged")); } }); - audioManager.init(); - } - private void setAudioDeviceDelayed(final AppRTCAudioManager.AudioDevice audioDevice) { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - showToastAfterHeadsetPlugged = true; - audioManager.setAudioDevice(audioDevice); + audioManager.setBluetoothAudioDeviceStateListener(connected -> { + if (callStarted) { + Toaster.shortToast("Bluetooth " + (connected ? "connected" : "disconnected")); } - }, 500); + }); } private void initQBRTCClient() { @@ -577,7 +553,7 @@ public void onSessionClosed(final QBRTCSession session) { Log.d(TAG, "Stop session"); if (audioManager != null) { - audioManager.close(); + audioManager.stop(); } releaseCurrentSession(); @@ -675,10 +651,8 @@ public void onUseHeadSet(boolean use) { audioManager.setManageHeadsetByDefault(use); } - public void sendHeadsetState() { - if (isInCommingCall) { - onChangeDynamicCallback.enableDynamicToggle(headsetPlugged, previousDeviceEarPiece); - } + public void notifyAboutCurrentAudioDevice() { + onChangeAudioDeviceCallback.audioDeviceChanged(audioManager.getSelectedAudioDevice()); } ////////////////////////////// IncomeCallFragmentCallbackListener //////////////////////////// @@ -686,6 +660,7 @@ public void sendHeadsetState() { @Override public void onAcceptCurrentSession() { if (currentSession != null) { + startAudioManager(); addConversationFragment(true); } else { Log.d(TAG, "SKIP addConversationFragment method"); @@ -760,11 +735,17 @@ public void onSetVideoEnabled(boolean isNeedEnableCam) { @Override public void onSwitchAudio() { - if (audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.WIRED_HEADSET - || audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) { - audioManager.setAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); + Log.v(TAG, "onSwitchAudio(), SelectedAudioDevice() = " + audioManager.getSelectedAudioDevice()); + if (audioManager.getSelectedAudioDevice() != AppRTCAudioManager.AudioDevice.SPEAKER_PHONE){ + audioManager.selectAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); } else { - audioManager.setAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE); + if (audioManager.getAudioDevices().contains(AppRTCAudioManager.AudioDevice.BLUETOOTH)){ + audioManager.selectAudioDevice(AppRTCAudioManager.AudioDevice.BLUETOOTH); + } else if (audioManager.getAudioDevices().contains(AppRTCAudioManager.AudioDevice.WIRED_HEADSET)){ + audioManager.selectAudioDevice(AppRTCAudioManager.AudioDevice.WIRED_HEADSET); + } else { + audioManager.selectAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE); + } } } @@ -791,14 +772,14 @@ public void removeCurrentCallStateCallback(CurrentCallStateCallback currentCallS } @Override - public void addOnChangeDynamicToggle(OnChangeDynamicToggle onChangeDynamicCallback) { - this.onChangeDynamicCallback = onChangeDynamicCallback; - sendHeadsetState(); + public void addOnChangeAudioDeviceCallback(OnChangeAudioDevice onChangeDynamicCallback) { + this.onChangeAudioDeviceCallback = onChangeDynamicCallback; + notifyAboutCurrentAudioDevice(); } @Override - public void removeOnChangeDynamicToggle(OnChangeDynamicToggle onChangeDynamicCallback) { - this.onChangeDynamicCallback = null; + public void removeOnChangeAudioDeviceCallback(OnChangeAudioDevice onChangeDynamicCallback) { + this.onChangeAudioDeviceCallback = null; } @Override @@ -828,8 +809,8 @@ public void reconnectingIn(int seconds) { } } - public interface OnChangeDynamicToggle { - void enableDynamicToggle(boolean plugged, boolean wasEarpiece); + public interface OnChangeAudioDevice { + void audioDeviceChanged(AppRTCAudioManager.AudioDevice newAudioDevice); } diff --git a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/AudioConversationFragment.java b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/AudioConversationFragment.java index f8c32c56e..962f1cfc2 100644 --- a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/AudioConversationFragment.java +++ b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/AudioConversationFragment.java @@ -16,13 +16,14 @@ import com.quickblox.sample.groupchatwebrtc.activities.CallActivity; import com.quickblox.sample.groupchatwebrtc.utils.CollectionsUtils; import com.quickblox.users.model.QBUser; +import com.quickblox.videochat.webrtc.AppRTCAudioManager; import java.util.ArrayList; /** * Created by tereha on 25.05.16. */ -public class AudioConversationFragment extends BaseConversationFragment implements CallActivity.OnChangeDynamicToggle { +public class AudioConversationFragment extends BaseConversationFragment implements CallActivity.OnChangeAudioDevice { private static final String TAG = AudioConversationFragment.class.getSimpleName(); private ToggleButton audioSwitchToggleButton; @@ -39,7 +40,7 @@ public void onCreate(Bundle savedInstanceState) { @Override public void onStart() { super.onStart(); - conversationFragmentCallbackListener.addOnChangeDynamicToggle(this); + conversationFragmentCallbackListener.addOnChangeAudioDeviceCallback(this); } @Nullable @@ -109,7 +110,7 @@ private String getOtherOpponentsNames() { @Override public void onStop() { super.onStop(); - conversationFragmentCallbackListener.removeOnChangeDynamicToggle(this); + conversationFragmentCallbackListener.removeOnChangeAudioDeviceCallback(this); } @Override @@ -127,9 +128,6 @@ public void onClick(View v) { @Override protected void actionButtonsEnabled(boolean inability) { super.actionButtonsEnabled(inability); - if (!headsetPlugged) { - audioSwitchToggleButton.setEnabled(inability); - } audioSwitchToggleButton.setActivated(inability); } @@ -146,19 +144,7 @@ public void onOpponentsListUpdated(ArrayList newUsers) { } @Override - public void enableDynamicToggle(boolean plugged, boolean previousDeviceEarPiece) { - headsetPlugged = plugged; - - if (isStarted) { - audioSwitchToggleButton.setEnabled(!plugged); - - if (plugged) { - audioSwitchToggleButton.setChecked(true); - }else if(previousDeviceEarPiece){ - audioSwitchToggleButton.setChecked(true); - } else { - audioSwitchToggleButton.setChecked(false); - } - } + public void audioDeviceChanged(AppRTCAudioManager.AudioDevice newAudioDevice) { + audioSwitchToggleButton.setChecked(newAudioDevice != AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); } } diff --git a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/ConversationFragmentCallbackListener.java b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/ConversationFragmentCallbackListener.java index 0c2328e88..b58e9e872 100644 --- a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/ConversationFragmentCallbackListener.java +++ b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/fragments/ConversationFragmentCallbackListener.java @@ -20,8 +20,8 @@ public interface ConversationFragmentCallbackListener { void addCurrentCallStateCallback (CallActivity.CurrentCallStateCallback currentCallStateCallback); void removeCurrentCallStateCallback (CallActivity.CurrentCallStateCallback currentCallStateCallback); - void addOnChangeDynamicToggle (CallActivity.OnChangeDynamicToggle onChangeDynamicCallback); - void removeOnChangeDynamicToggle (CallActivity.OnChangeDynamicToggle onChangeDynamicCallback); + void addOnChangeAudioDeviceCallback(CallActivity.OnChangeAudioDevice onChangeDynamicCallback); + void removeOnChangeAudioDeviceCallback(CallActivity.OnChangeAudioDevice onChangeDynamicCallback); void onSetAudioEnabled(boolean isAudioEnabled); diff --git a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/RingtonePlayer.java b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/RingtonePlayer.java index 2bc4336a8..34feb1d41 100755 --- a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/RingtonePlayer.java +++ b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/RingtonePlayer.java @@ -1,11 +1,15 @@ package com.quickblox.sample.groupchatwebrtc.utils; import android.content.Context; +import android.media.AudioAttributes; +import android.media.AudioManager; import android.media.MediaPlayer; import android.media.RingtoneManager; import android.net.Uri; import android.util.Log; +import java.io.IOException; + /** * QuickBlox team */ @@ -17,14 +21,48 @@ public class RingtonePlayer { public RingtonePlayer(Context context, int resource){ this.context = context; - mediaPlayer = android.media.MediaPlayer.create(context, resource); + Uri beepUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + resource); + mediaPlayer = new MediaPlayer(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING); + + mediaPlayer.setAudioAttributes(audioAttributesBuilder.build()); + } else { + mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); + } + + try { + mediaPlayer.setDataSource(context, beepUri); + mediaPlayer.prepare(); + } catch (IOException e) { + e.printStackTrace(); + } } public RingtonePlayer(Context context){ this.context = context; Uri notification = getNotification(); if (notification != null) { - mediaPlayer = android.media.MediaPlayer.create(context, notification); + mediaPlayer = new MediaPlayer(); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE); + + mediaPlayer.setAudioAttributes(audioAttributesBuilder.build()); + } else { + mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING); + } + + try { + mediaPlayer.setDataSource(context, notification); + mediaPlayer.prepare(); + } catch (IOException e) { + e.printStackTrace(); + } } } diff --git a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/SettingsUtil.java b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/SettingsUtil.java index ed14a5d0f..08f763c5e 100755 --- a/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/SettingsUtil.java +++ b/sample-videochat-webrtc/src/main/java/com/quickblox/sample/groupchatwebrtc/utils/SettingsUtil.java @@ -130,6 +130,14 @@ public static void configRTCTimers(Context context) { Log.e(TAG, "dialingTimeInterval = " + dialingTimeInterval); } + public static boolean isManageSpeakerPhoneByProximity(Context context){ + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + boolean manageSpeakerPhoneByProximity = sharedPref.getBoolean(context.getString(R.string.pref_manage_speakerphone_by_proximity_key), + Boolean.valueOf(context.getString(R.string.pref_manage_speakerphone_by_proximity_default))); + + return manageSpeakerPhoneByProximity; + } + private static void setVideoQuality(int resolutionItem) { if (resolutionItem != -1) { setVideoFromLibraryPreferences(resolutionItem); diff --git a/sample-videochat-webrtc/src/main/res/values/strings.xml b/sample-videochat-webrtc/src/main/res/values/strings.xml index be5e3d3d4..22db4e583 100755 --- a/sample-videochat-webrtc/src/main/res/values/strings.xml +++ b/sample-videochat-webrtc/src/main/res/values/strings.xml @@ -145,6 +145,12 @@ Disable audio processing pipeline false + manage_speakerphone_by_proximity_preference + Manage Speaker Phone by Proximity Sensor + Manage Speaker Phone by Proximity Sensor + Enables managing of Speaker Phone by Proximity Sensor for Audio calls + false + answer_time_interval diff --git a/sample-videochat-webrtc/src/main/res/xml/preferences.xml b/sample-videochat-webrtc/src/main/res/xml/preferences.xml index 062e48afc..0053b9c46 100755 --- a/sample-videochat-webrtc/src/main/res/xml/preferences.xml +++ b/sample-videochat-webrtc/src/main/res/xml/preferences.xml @@ -110,6 +110,13 @@ android:dialogTitle="@string/pref_noaudioprocessing_dlg" android:defaultValue="@string/pref_noaudioprocessing_default" /> + +