Merge branch '2.2-work'

This commit is contained in:
Mario Danic 2018-07-16 16:28:52 +02:00
commit 09ed036513
37 changed files with 921 additions and 226 deletions

View File

@ -19,6 +19,8 @@ android {
versionName "2.1.0"
flavorDimensions "default"
renderscriptTargetApi 19
renderscriptSupportModeEnabled true
productFlavors {
// used for f-droid
@ -152,7 +154,7 @@ dependencies {
implementation 'com.github.wooplr:Spotlight:1.2.3'
implementation 'com.github.stfalcon:chatkit:0.2.2'
implementation 'com.github.stfalcon:chatkit:0.3.0'
implementation 'com.otaliastudios:autocomplete:1.1.0'
implementation 'com.github.Kennyc1012:BottomSheet:2.4.0'

View File

@ -28,4 +28,9 @@ public class ClosedInterfaceImpl implements ClosedInterface {
public void providerInstallerInstallIfNeededAsync() {
// does absolutely nothing :)
}
@Override
public boolean isGooglePlayServicesAvailable() {
return false;
}
}

View File

@ -20,12 +20,16 @@
package com.nextcloud.talk.adapters.messages;
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.RelativeSizeSpan;
import android.view.View;
import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.google.android.flexbox.FlexboxLayout;
import com.kevalpatel2106.emoticongifkeyboard.widget.EmoticonTextView;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
@ -33,7 +37,9 @@ import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.models.json.chat.ChatMessage;
import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.database.user.UserUtils;
import com.nextcloud.talk.utils.emoticons.EmoticonUtils;
import com.stfalcon.chatkit.messages.MessageHolders;
import com.stfalcon.chatkit.utils.ShapeImageView;
import java.util.HashMap;
@ -53,16 +59,24 @@ public class MagicIncomingTextMessageViewHolder
@BindView(R.id.messageText)
EmoticonTextView messageText;
@BindView(R.id.messageUserAvatar)
ShapeImageView messageUserAvatarView;
@BindView(R.id.messageTime)
TextView messageTimeView;
@Inject
UserUtils userUtils;
private UserEntity currentUser;
private View itemView;
public MagicIncomingTextMessageViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
this.itemView = itemView;
currentUser = userUtils.getCurrentUser();
}
@ -77,8 +91,23 @@ public class MagicIncomingTextMessageViewHolder
messageAuthor.setText(R.string.nc_nick_guest);
}
if (message.getActorType().equals("guests")) {
TextDrawable drawable = TextDrawable.builder().beginConfig().bold()
.endConfig().buildRound(String.valueOf(messageAuthor.getText().charAt(0)), NextcloudTalkApplication
.getSharedApplication().getResources().getColor(R.color.nc_grey));
messageUserAvatarView.setVisibility(View.VISIBLE);
messageUserAvatarView.setImageDrawable(drawable);
}
HashMap<String, HashMap<String, String>> messageParameters = message.getMessageParameters();
Context context = NextcloudTalkApplication.getSharedApplication().getApplicationContext();
itemView.setSelected(false);
messageTimeView.setTextColor(context.getResources().getColor(R.color.warm_grey_four));
FlexboxLayout.LayoutParams layoutParams = (FlexboxLayout.LayoutParams) messageTimeView.getLayoutParams();
layoutParams.setWrapBefore(false);
Spannable messageString = new SpannableString(message.getText());
if (messageParameters != null && message.getMessageParameters().size() > 0) {
@ -100,8 +129,14 @@ public class MagicIncomingTextMessageViewHolder
}
}
} else if (EmoticonUtils.isMessageWithSingleEmoticonOnly(context, message.getText())) {
messageString.setSpan(new RelativeSizeSpan(2.5f), 0, messageString.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
layoutParams.setWrapBefore(true);
itemView.setSelected(true);
}
messageTimeView.setLayoutParams(layoutParams);
messageText.setText(messageString);
}
}

View File

@ -20,10 +20,14 @@
package com.nextcloud.talk.adapters.messages;
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.RelativeSizeSpan;
import android.view.View;
import android.widget.TextView;
import com.google.android.flexbox.FlexboxLayout;
import com.kevalpatel2106.emoticongifkeyboard.widget.EmoticonTextView;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
@ -31,6 +35,7 @@ import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.models.json.chat.ChatMessage;
import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.database.user.UserUtils;
import com.nextcloud.talk.utils.emoticons.EmoticonUtils;
import com.stfalcon.chatkit.messages.MessageHolders;
import java.util.HashMap;
@ -46,16 +51,22 @@ public class MagicOutcomingTextMessageViewHolder extends MessageHolders.Outcomin
@BindView(R.id.messageText)
EmoticonTextView messageText;
@BindView(R.id.messageTime)
TextView messageTimeView;
@Inject
UserUtils userUtils;
private UserEntity currentUser;
private View itemView;
public MagicOutcomingTextMessageViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
this.itemView = itemView;
currentUser = userUtils.getCurrentUser();
}
@ -67,6 +78,13 @@ public class MagicOutcomingTextMessageViewHolder extends MessageHolders.Outcomin
Spannable messageString = new SpannableString(message.getText());
Context context = NextcloudTalkApplication.getSharedApplication().getApplicationContext();
itemView.setSelected(false);
messageTimeView.setTextColor(context.getResources().getColor(R.color.white60));
FlexboxLayout.LayoutParams layoutParams = (FlexboxLayout.LayoutParams) messageTimeView.getLayoutParams();
layoutParams.setWrapBefore(false);
if (messageParameters != null && message.getMessageParameters().size() > 0) {
for (String key : message.getMessageParameters().keySet()) {
HashMap<String, String> individualHashMap = message.getMessageParameters().get(key);
@ -80,9 +98,15 @@ public class MagicOutcomingTextMessageViewHolder extends MessageHolders.Outcomin
}
}
} else if (EmoticonUtils.isMessageWithSingleEmoticonOnly(context, message.getText())) {
messageString.setSpan(new RelativeSizeSpan(2.5f), 0, messageString.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
layoutParams.setWrapBefore(true);
messageTimeView.setTextColor(context.getResources().getColor(R.color.warm_grey_four));
itemView.setSelected(true);
}
messageTimeView.setLayoutParams(layoutParams);
messageText.setText(messageString);
}
}

View File

@ -289,4 +289,13 @@ public interface NcApi {
@Url String url, @Query("search") String query,
@Nullable @Query("limit") Integer limit);
// Url is: /api/{apiVersion}/room/{token}/favorite
@POST
Observable<GenericOverall> addConversationToFavorites(@Header("Authorization") String authorization,
@Url String url);
// Url is: /api/{apiVersion}/room/{token}/favorite
@DELETE
Observable<GenericOverall> removeConversationFromFavorites(@Header("Authorization") String authorization,
@Url String url);
}

View File

@ -46,10 +46,11 @@ import com.nextcloud.talk.models.json.generic.Status;
import com.nextcloud.talk.models.json.rooms.RoomsOverall;
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder;
import com.nextcloud.talk.utils.ClosedInterfaceImpl;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;
import com.nextcloud.talk.utils.preferences.AppPreferences;
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@ -261,7 +262,13 @@ public class AccountVerificationController extends BaseController {
public void onNext(UserEntity userEntity) {
internalAccountId = userEntity.getId();
registerForPush();
if (new ClosedInterfaceImpl().isGooglePlayServicesAvailable()) {
registerForPush();
} else {
getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" +
getResources().getString(R.string.nc_push_disabled)));
fetchAndStoreCapabilities();
}
}
@Override

View File

@ -598,13 +598,7 @@ public class CallController extends BaseController {
public void onEnableSpeakerphoneClick() {
if (audioManager != null) {
audioManager.toggleUseSpeakerphone();
if (audioManager.isSpeakerphoneAutoOn()) {
callControlEnableSpeaker.getFrontImageView().setImageDrawable(getResources().getDrawable(R.drawable
.ic_speaker_white_24dp));
} else {
callControlEnableSpeaker.getFrontImageView().setImageDrawable(getResources().getDrawable(R.drawable
.ic_hearing_white_24dp));
}
callControlEnableSpeaker.flipSilently(!callControlEnableSpeaker.isFlipped());
}
}

View File

@ -21,10 +21,17 @@
package com.nextcloud.talk.controllers;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.support.annotation.NonNull;
import android.support.constraint.ConstraintLayout;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@ -40,7 +47,10 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.LazyHeaders;
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import com.nextcloud.talk.R;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.application.NextcloudTalkApplication;
@ -96,6 +106,9 @@ public class CallNotificationController extends BaseController {
@BindView(R.id.callAnswerCameraView)
MagicFlipView callAnswerCameraView;
@BindView(R.id.constraintLayout)
ConstraintLayout constraintLayout;
private List<Disposable> disposablesList = new ArrayList<>();
private Bundle originalBundle;
private String roomId;
@ -104,6 +117,7 @@ public class CallNotificationController extends BaseController {
private Room currentRoom;
private MediaPlayer mediaPlayer;
private boolean leavingScreen = false;
private RenderScript renderScript;
public CallNotificationController(Bundle args) {
super(args);
@ -249,6 +263,7 @@ public class CallNotificationController extends BaseController {
protected void onViewBound(@NonNull View view) {
super.onViewBound(view);
renderScript = RenderScript.create(getActivity());
handleFromNotification();
String callRingtonePreferenceString = appPreferences.getCallRingtoneUri();
@ -296,8 +311,27 @@ public class CallNotificationController extends BaseController {
.load(glideUrl)
.centerInside()
.override(avatarSize, avatarSize)
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(avatarImageView);
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
if (getActivity() != null) {
avatarImageView.setImageBitmap(TransformationUtils.circleCrop(GlideApp.get
(getActivity()).getBitmapPool(), resource, avatarSize, avatarSize));
}
final Allocation input = Allocation.createFromBitmap(renderScript, resource);
final Allocation output = Allocation.createTyped(renderScript, input.getType());
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(renderScript, Element
.U8_4(renderScript));
script.setRadius(15f);
script.setInput(input);
script.forEach(output);
output.copyTo(resource);
constraintLayout.setBackground(new BitmapDrawable(resource));
}
});
break;
case ROOM_GROUP_CALL:

View File

@ -46,6 +46,9 @@ import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.bumptech.glide.load.DataSource;
@ -86,6 +89,7 @@ import com.otaliastudios.autocomplete.AutocompletePresenter;
import com.otaliastudios.autocomplete.CharPolicy;
import com.stfalcon.chatkit.commons.ImageLoader;
import com.stfalcon.chatkit.commons.models.IMessage;
import com.stfalcon.chatkit.messages.MessageHolders;
import com.stfalcon.chatkit.messages.MessageInput;
import com.stfalcon.chatkit.messages.MessagesList;
import com.stfalcon.chatkit.messages.MessagesListAdapter;
@ -107,6 +111,7 @@ import javax.inject.Inject;
import autodagger.AutoInjector;
import butterknife.BindView;
import butterknife.OnClick;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@ -133,6 +138,12 @@ public class ChatController extends BaseController implements MessagesListAdapte
MessageInput messageInputView;
@BindView(R.id.popupBubbleView)
PopupBubble popupBubble;
@BindView(R.id.emptyLayout)
RelativeLayout emptyLayout;
@BindView(R.id.sendHiTextView)
TextView sendHiTextView;
@BindView(R.id.progressBar)
ProgressBar loadingProgressBar;
private List<Disposable> disposableList = new ArrayList<>();
private String conversationName;
private String roomToken;
@ -158,6 +169,9 @@ public class ChatController extends BaseController implements MessagesListAdapte
private String roomId;
private boolean voiceOnly;
private boolean isFirstMessagesProcessing = true;
private boolean isHelloClicked;
public ChatController(Bundle args) {
super(args);
setHasOptionsMenu(true);
@ -286,9 +300,13 @@ public class ChatController extends BaseController implements MessagesListAdapte
getActionBar().show();
boolean adapterWasNull = false;
sendHiTextView.setText(String.format(getResources().getString(R.string.nc_chat_empty), getResources()
.getString(R.string.nc_hello)));
if (adapter == null) {
loadingProgressBar.setVisibility(View.VISIBLE);
try {
cache.evictAll();
} catch (IOException e) {
@ -297,13 +315,11 @@ public class ChatController extends BaseController implements MessagesListAdapte
adapterWasNull = true;
MessagesListAdapter.HoldersConfig holdersConfig = new MessagesListAdapter.HoldersConfig();
holdersConfig.setIncoming(MagicIncomingTextMessageViewHolder.class,
R.layout.item_custom_incoming_text_message);
holdersConfig.setOutcoming(MagicOutcomingTextMessageViewHolder.class,
R.layout.item_custom_outcoming_text_message);
MessageHolders messageHolders = new MessageHolders();
messageHolders.setIncomingTextConfig(MagicIncomingTextMessageViewHolder.class, R.layout.item_custom_incoming_text_message);
messageHolders.setOutcomingTextConfig(MagicOutcomingTextMessageViewHolder.class, R.layout.item_custom_outcoming_text_message);
adapter = new MessagesListAdapter<>(conversationUser.getUserId(), holdersConfig, new ImageLoader() {
adapter = new MessagesListAdapter<>(conversationUser.getUserId(), messageHolders, new ImageLoader() {
@Override
public void loadImage(ImageView imageView, String url) {
GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
@ -317,7 +333,6 @@ public class ChatController extends BaseController implements MessagesListAdapte
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
TextDrawable drawable = TextDrawable.builder().beginConfig().bold()
.width(imageView.getMeasuredWidth()).height(imageView.getMeasuredHeight())
.endConfig().buildRound("?", getResources().getColor(R.color.nc_grey));
imageView.setImageDrawable(drawable);
return true;
@ -331,6 +346,12 @@ public class ChatController extends BaseController implements MessagesListAdapte
.into(imageView);
}
});
} else {
if (adapter.getItemCount() == 0) {
emptyLayout.setVisibility(View.VISIBLE);
} else {
messagesListView.setVisibility(View.VISIBLE);
}
}
@ -480,6 +501,14 @@ public class ChatController extends BaseController implements MessagesListAdapte
}
@OnClick(R.id.emptyLayout)
public void sendHello() {
if (!isHelloClicked) {
isHelloClicked = true;
sendMessage(getResources().getString(R.string.nc_hello) + " 👋", 1);
}
}
private void joinRoomWithPassword() {
if (currentCall == null) {
@ -697,7 +726,26 @@ public class ChatController extends BaseController implements MessagesListAdapte
ChatOverall chatOverall = (ChatOverall) response.body();
List<ChatMessage> chatMessageList = chatOverall.getOcs().getData();
if (isFirstMessagesProcessing) {
isFirstMessagesProcessing = false;
loadingProgressBar.setVisibility(View.GONE);
if (chatMessageList.size() == 0) {
emptyLayout.setVisibility(View.VISIBLE);
} else {
messagesListView.setVisibility(View.VISIBLE);
}
} else {
if (emptyLayout.getVisibility() != View.GONE) {
emptyLayout.setVisibility(View.GONE);
}
if (messagesListView.getVisibility() != View.VISIBLE) {
messagesListView.setVisibility(View.VISIBLE);
}
}
if (!isFromTheFuture) {
for (int i = 0; i < chatMessageList.size(); i++) {
chatMessageList.get(i).setBaseUrl(conversationUser.getBaseUrl());
if (globalLastKnownPastMessageId == -1 || chatMessageList.get(i).getJsonMessageId() <
@ -712,8 +760,10 @@ public class ChatController extends BaseController implements MessagesListAdapte
}
}
adapter.addToEnd(chatMessageList, false);
} else {
for (int i = 0; i < chatMessageList.size(); i++) {
chatMessageList.get(i).setBaseUrl(conversationUser.getBaseUrl());
@ -754,6 +804,15 @@ public class ChatController extends BaseController implements MessagesListAdapte
pullChatMessages(1);
}
} else if (response.code() == 304 && !isFromTheFuture) {
if (isFirstMessagesProcessing) {
isFirstMessagesProcessing = false;
loadingProgressBar.setVisibility(View.GONE);
if (emptyLayout.getVisibility() != View.VISIBLE) {
emptyLayout.setVisibility(View.VISIBLE);
}
}
historyRead = true;
if (!lookingIntoFuture) {
@ -794,7 +853,6 @@ public class ChatController extends BaseController implements MessagesListAdapte
case android.R.id.home:
onDestroy();
return true;
case R.id.conversation_video_call:
startACall(false);
return true;

View File

@ -28,7 +28,6 @@ import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomNavigationView;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
@ -43,7 +42,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.RelativeLayout;
import com.bluelinelabs.conductor.RouterTransaction;
@ -68,7 +66,6 @@ import com.nextcloud.talk.models.json.rooms.RoomOverall;
import com.nextcloud.talk.models.json.sharees.Sharee;
import com.nextcloud.talk.models.json.sharees.ShareesOverall;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.animations.ViewHidingBehaviourAnimation;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;
@ -130,16 +127,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
@BindView(R.id.swipe_refresh_layout)
SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.bottom_buttons_layout)
CoordinatorLayout bottomButtonsLinearLayout;
@BindView(R.id.fast_scroller)
FastScroller fastScroller;
@Nullable
@BindView(R.id.clear_button)
Button clearButton;
private UserEntity currentUser;
private Disposable contactsQueryDisposable;
private Disposable cacheQueryDisposable;
@ -165,6 +155,8 @@ public class ContactsController extends BaseController implements SearchView.OnQ
private boolean canFetchFurther = true;
private boolean canFetchSearchFurther = true;
private MenuItem doneMenuItem;
public ContactsController() {
super();
setHasOptionsMenu(true);
@ -195,7 +187,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
if (isNewConversationView) {
toggleNewCallHeaderVisibility(!isPublicCall);
checkAndHandleBottomButtons();
checkAndHandleDoneMenuItem();
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
@ -257,29 +249,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
adapter.addListener(this);
}
@Optional
@OnClick(R.id.clear_button)
public void onClearButtonClick() {
if (adapter != null) {
List<Integer> selectedPositions = adapter.getSelectedPositions();
for (Integer position : selectedPositions) {
if (adapter.getItem(position) instanceof UserItem) {
UserItem userItem = (UserItem) adapter.getItem(position);
adapter.toggleSelection(position);
if (userItem != null) {
userItem.flipItemSelection();
}
}
}
}
checkAndHandleBottomButtons();
}
@Optional
@OnClick(R.id.done_button)
public void onDoneButtonClick() {
private void selectionDone() {
if (!isPublicCall && adapter.getSelectedPositions().size() == 1) {
RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), "1",
((UserItem) adapter.getItem(adapter.getSelectedPositions().get(0))).getModel().getUserId(), null);
@ -411,6 +381,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
case android.R.id.home:
getRouter().popCurrentController();
return true;
case R.id.contacts_selection_done:
selectionDone();
return true;
default:
return super.onOptionsItemSelected(item);
}
@ -421,7 +394,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_conversation_plus_filter, menu);
searchItem = menu.findItem(R.id.action_search);
doneMenuItem = menu.findItem(R.id.contacts_selection_done);
menu.findItem(R.id.action_new_conversation).setVisible(false);
initSearchView();
}
@ -576,7 +551,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
}
if (isNewConversationView) {
checkAndHandleBottomButtons();
checkAndHandleDoneMenuItem();
}
}
;
@ -648,9 +623,6 @@ public class ContactsController extends BaseController implements SearchView.OnQ
return "";
}
});
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) bottomButtonsLinearLayout.getLayoutParams();
layoutParams.setBehavior(new ViewHidingBehaviourAnimation());
}
private void dispose(@Nullable Disposable disposable) {
@ -725,23 +697,18 @@ public class ContactsController extends BaseController implements SearchView.OnQ
return onQueryTextChange(query);
}
private void checkAndHandleBottomButtons() {
if (adapter != null && bottomButtonsLinearLayout != null && clearButton != null) {
private void checkAndHandleDoneMenuItem() {
if (adapter != null && doneMenuItem != null) {
if (adapter.getSelectedItemCount() > 0 || isPublicCall) {
if (bottomButtonsLinearLayout.getVisibility() != View.VISIBLE) {
bottomButtonsLinearLayout.setVisibility(View.VISIBLE);
if (!doneMenuItem.isVisible()) {
doneMenuItem.setVisible(true);
}
if (adapter.getSelectedItemCount() == 0) {
clearButton.setVisibility(View.GONE);
} else {
clearButton.setVisibility(View.VISIBLE);
}
} else {
bottomButtonsLinearLayout.setVisibility(View.GONE);
doneMenuItem.setVisible(false);
}
} else if (bottomButtonsLinearLayout != null) {
bottomButtonsLinearLayout.setVisibility(View.GONE);
} else if (doneMenuItem != null) {
doneMenuItem.setVisible(false);
}
}
@ -865,7 +832,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
((UserItem) adapter.getItem(position)).flipItemSelection();
adapter.toggleSelection(position);
checkAndHandleBottomButtons();
checkAndHandleDoneMenuItem();
}
}
return true;
@ -876,7 +843,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
void toggleCallHeader() {
toggleNewCallHeaderVisibility(isPublicCall);
isPublicCall = !isPublicCall;
checkAndHandleBottomButtons();
checkAndHandleDoneMenuItem();
}
private void toggleNewCallHeaderVisibility(boolean showInitialLayout) {

View File

@ -125,14 +125,9 @@ public class CallMenuController extends BaseController implements FlexibleAdapte
if (menuType.equals(MenuType.REGULAR)) {
if (!TextUtils.isEmpty(room.getDisplayName())) {
menuItems.add(new MenuItem(
getResources().getString(
R.string.nc_configure_named_room, room.getDisplayName()), 0, null)
);
menuItems.add(new MenuItem(room.getDisplayName(), 0, null));
} else if (!TextUtils.isEmpty(room.getName())) {
menuItems.add(new MenuItem(getResources().getString(
R.string.nc_configure_named_room, room.getName()), 0, null)
);
menuItems.add(new MenuItem(room.getName(), 0, null));
} else {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_configure_room), 0, null));
}

View File

@ -184,6 +184,10 @@ public class EntryMenuController extends BaseController {
super.onViewBound(view);
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
if (room != null && operationCode == 2) {
editText.setText(room.getName());
}
editText.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_DONE && proceedButton != null && proceedButton.isEnabled()) {
proceedButton.callOnClick();

View File

@ -242,7 +242,7 @@ public class NotificationJob extends Job {
if (notificationManager != null) {
notificationManager.notify((int) crc32.getValue(), notificationBuilder.build());
if (soundUri != null) {
if (soundUri != null & !ApplicationWideCurrentRoomHolder.getInstance().isInCall()) {
MediaPlayer mediaPlayer = MediaPlayer.create(context, soundUri);
mediaPlayer.start();
mediaPlayer.setOnCompletionListener(MediaPlayer::release);

View File

@ -91,7 +91,11 @@ public class ChatMessage implements IMessage {
@Override
public String getAvatar() {
return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, R.dimen.avatar_size);
if (getActorType().equals("users")) {
return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, R.dimen.avatar_size);
} else {
return null;
}
}
};
}

View File

@ -63,6 +63,8 @@ public class Room {
@JsonField(name = "sessionId")
public String sessionId;
public String password;
@JsonField(name = "isFavorite")
public boolean isFavorite;
public boolean isPublic() {
return (RoomType.ROOM_PUBLIC_CALL.equals(type));

View File

@ -187,4 +187,8 @@ public class ApiUtils {
return NextcloudTalkApplication.getSharedApplication().
getApplicationContext().getResources().getString(R.string.nc_push_server_url) + "/devices";
}
public static String getUrlForConversationFavorite(String baseUrl, String roomToken) {
return baseUrl + ocsApiVersion + "/room/" + roomToken + "/favorite";
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2017 Keval Patel.
*
* 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.nextcloud.talk.utils.emoticons;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.support.v7.content.res.AppCompatResources;
import android.text.style.DynamicDrawableSpan;
import android.text.style.ImageSpan;
/**
* Created by Keval Patel on 20/08/17.
* The {@link ImageSpan} to display custom emoticon icon based on the unicode.
*
* @author 'https://github.com/kevalpatel2106'
* @see <a href='https://github.com/rockerhieu/emojicon/blob/master/library/src/main/java/io/github/rockerhieu/emojicon/EmojiconSpan.java>EmojiconSpan.java</a>
*/
final class EmoticonSpan extends DynamicDrawableSpan {
private final float mEmoticonSize;
private final Context mContext;
private final int mEmoticonIcon;
private Drawable mDeferredDrawable;
EmoticonSpan(final Context context, final int emoticonIcon, final float size) {
this.mContext = context;
this.mEmoticonIcon = emoticonIcon;
this.mEmoticonSize = size;
}
@SuppressWarnings("ConstantConditions")
@Override
public Drawable getDrawable() {
if (mDeferredDrawable == null) {
mDeferredDrawable = AppCompatResources.getDrawable(mContext, mEmoticonIcon);
mDeferredDrawable.setBounds(0, 0, (int) mEmoticonSize, (int) mEmoticonSize);
}
return mDeferredDrawable;
}
@Override
public int getSize(final Paint paint, final CharSequence text, final int start,
final int end, final Paint.FontMetricsInt fontMetrics) {
if (fontMetrics != null) {
final Paint.FontMetrics paintFontMetrics = paint.getFontMetrics();
final float fontHeight = paintFontMetrics.descent - paintFontMetrics.ascent;
final float centerY = paintFontMetrics.ascent + fontHeight / 2;
fontMetrics.ascent = (int) (centerY - mEmoticonSize / 2);
fontMetrics.top = fontMetrics.ascent;
fontMetrics.bottom = (int) (centerY + mEmoticonSize / 2);
fontMetrics.descent = fontMetrics.bottom;
}
return (int) mEmoticonSize;
}
@Override
public void draw(final Canvas canvas, final CharSequence text, final int start,
final int end, final float x, final int top, final int y,
final int bottom, final Paint paint) {
final Drawable drawable = getDrawable();
final Paint.FontMetrics paintFontMetrics = paint.getFontMetrics();
final float fontHeight = paintFontMetrics.descent - paintFontMetrics.ascent;
final float centerY = y + paintFontMetrics.descent - fontHeight / 2;
final float transitionY = centerY - mEmoticonSize / 2;
canvas.save();
canvas.translate(x, transitionY);
drawable.draw(canvas);
canvas.restore();
}
}

View File

@ -0,0 +1,255 @@
/*
* Copyright 2017 Keval Patel.
*
* 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.
*
* Modified by Mario Danic (mario@lovelyhq.com) - Copyright 2018
*/
package com.nextcloud.talk.utils.emoticons;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.TextUtils;
import com.kevalpatel2106.emoticongifkeyboard.R;
import com.kevalpatel2106.emoticongifkeyboard.emoticons.Emoticon;
import com.kevalpatel2106.emoticongifkeyboard.emoticons.EmoticonProvider;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Keval Patel on 20/08/17.
* Utils to find emoticons from the string.
*
* @author 'https://github.com/kevalpatel2106'
*/
public final class EmoticonUtils {
/**
* {@link Pattern} to find the supported emoticons unicodes.
*/
private static Pattern sRegexPattern;
/**
* Private constructor.
*/
private EmoticonUtils() {
//Do nothing
}
/**
* Replace the system emoticons with the provided custom emoticons drawable. This will find the
* unicodes with supported emoticons in the provided text and will replace the emoticons with
* appropriate images.
*
* @param context instance of caller.
* @param text Text to replace
* @param emoticonProvider {@link EmoticonProvider} for emoticons images
* @param emoticonSize Size of the emoticons in dp
* @return Modified text.
*/
public static void replaceWithImages(@NonNull final Context context,
@NonNull final Spannable text,
@NonNull final EmoticonProvider emoticonProvider,
final int emoticonSize) {
final EmoticonSpan[] existingSpans = text.getSpans(0, text.length(), EmoticonSpan.class);
final ArrayList<Integer> existingSpanPositions = new ArrayList<>(existingSpans.length);
for (EmoticonSpan existingSpan : existingSpans)
existingSpanPositions.add(text.getSpanStart(existingSpan));
//Get location and unicode of all emoticons.
final List<EmoticonRange> findAllEmojis = findAllEmoticons(context, text, emoticonProvider);
//Replace all the emoticons with their relatives.
for (int i = 0; i < findAllEmojis.size(); i++) {
final EmoticonRange location = findAllEmojis.get(i);
if (!existingSpanPositions.contains(location.mStartPos)) {
text.setSpan(new EmoticonSpan(context, location.mEmoticon.getIcon(), emoticonSize),
location.mStartPos,
location.mEndPos,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
/**
* Find all the unicodes that represents emoticons and location of starting and ending point of that emoticon.
*
* @param context Instance of caller.
* @param text Text to replace
* @param emoticonProvider {@link EmoticonProvider} for emoticons images
* @return List of {@link EmoticonRange}.
* @see EmoticonRange
*/
@NonNull
private static List<EmoticonRange> findAllEmoticons(@NonNull final Context context,
@Nullable final CharSequence text,
@NonNull final EmoticonProvider emoticonProvider) {
final List<EmoticonRange> result = new ArrayList<>();
if (!TextUtils.isEmpty(text)) {
final Matcher matcher = getRegex(context).matcher(text);
while (matcher.find()) {
String unicode = text.subSequence(matcher.start(), matcher.end()).toString();
if (emoticonProvider.hasEmoticonIcon(unicode)) { //Check if the the emoticon has icon?
final Emoticon found = new Emoticon(unicode, emoticonProvider.getIcon(unicode));
//Add this emoticon to change list.
result.add(new EmoticonRange(matcher.start(), matcher.end(), found));
}
}
}
return result;
}
public static boolean isMessageWithSingleEmoticonOnly(@NonNull final Context context,
@Nullable final CharSequence text) {
final List<EmoticonRange> result = new ArrayList<>();
if (!TextUtils.isEmpty(text)) {
// Regexp acquired from https://gist.github.com/sergeychilingaryan/94902985a636658496cb69c300bba05f
String unicode10regexString = "(?:[\\u2700-\\u27bf]|" +
"(?:[\\ud83c\\udde6-\\ud83c\\uddff]){2}|" +
"[\\ud800\\udc00-\\uDBFF\\uDFFF]|[\\u2600-\\u26FF])[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0]|[\\ud83c\\udffb-\\ud83c\\udfff])?" +
"(?:\\u200d(?:[^\\ud800-\\udfff]|" +
"(?:[\\ud83c\\udde6-\\ud83c\\uddff]){2}|" +
"[\\ud800\\udc00-\\uDBFF\\uDFFF]|[\\u2600-\\u26FF])[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0]|[\\ud83c\\udffb-\\ud83c\\udfff])?)*|" +
"[\\u0023-\\u0039]\\ufe0f?\\u20e3|\\u3299|\\u3297|\\u303d|\\u3030|\\u24c2|[\\ud83c\\udd70-\\ud83c\\udd71]|[\\ud83c\\udd7e-\\ud83c\\udd7f]|\\ud83c\\udd8e|[\\ud83c\\udd91-\\ud83c\\udd9a]|[\\ud83c\\udde6-\\ud83c\\uddff]|[\\ud83c\\ude01-\\ud83c\\ude02]|\\ud83c\\ude1a|\\ud83c\\ude2f|[\\ud83c\\ude32-\\ud83c\\ude3a]|[\\ud83c\\ude50-\\ud83c\\ude51]|\\u203c|\\u2049|[\\u25aa-\\u25ab]|\\u25b6|\\u25c0|[\\u25fb-\\u25fe]|\\u00a9|\\u00ae|\\u2122|\\u2139|\\ud83c\\udc04|[\\u2600-\\u26FF]|\\u2b05|\\u2b06|\\u2b07|\\u2b1b|\\u2b1c|\\u2b50|\\u2b55|\\u231a|\\u231b|\\u2328|\\u23cf|[\\u23e9-\\u23f3]|[\\u23f8-\\u23fa]|\\ud83c\\udccf|\\u2934|\\u2935|[\\u2190-\\u21ff]";
final Matcher matcher = Pattern.compile(unicode10regexString, Pattern.UNICODE_CASE).matcher(text);
while (matcher.find()) {
String unicode = text.subSequence(matcher.start(), matcher.end()).toString();
// quick hack
final Emoticon found = new Emoticon(unicode, R.drawable.emoji_food);
//Add this emoticon to change list.
result.add(new EmoticonRange(matcher.start(), matcher.end(), found));
}
} else {
return false;
}
return result.size() == 1 && result.get(0).mStartPos == 0 && text.length() == result.get(0).mEndPos;
}
/**
* Load the regex to parse unicode from the shared preference if {@link #sRegexPattern} is not
* loaded.
*
* @param context Instance.
* @return Regex to find emoticon unicode from string.
*/
@NonNull
private static Pattern getRegex(@NonNull final Context context) {
if (sRegexPattern == null) {
String regex = readTextFile(context, R.raw.regex);
sRegexPattern = Pattern.compile(regex);
}
return sRegexPattern;
}
@NonNull
private static String readTextFile(@NonNull Context context, int rowResource) {
InputStream inputStream = context.getResources().openRawResource(rowResource); // getting json
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder = new StringBuilder();
try {
String sCurrentLine;
while ((sCurrentLine = br.readLine()) != null) builder.append(sCurrentLine);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return builder.toString();
}
/**
* Range of the emoticons unicode.
*/
private static final class EmoticonRange {
/**
* Start portion of the emoticon in string.
*/
final int mStartPos;
/**
* End portion of the emoticon in string.
*/
final int mEndPos;
/**
* {@link Emoticon}.
*/
final Emoticon mEmoticon;
/**
* Private constructor.
*
* @param start Start portion of the emoticon in string.
* @param end End portion of the emoticon in string.
* @param emoticon {@link Emoticon}
*/
private EmoticonRange(final int start,
final int end,
@NonNull final Emoticon emoticon) {
this.mStartPos = start;
this.mEndPos = end;
this.mEmoticon = emoticon;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final EmoticonRange that = (EmoticonRange) o;
return mStartPos == that.mStartPos
&& mEndPos == that.mEndPos
&& mEmoticon.equals(that.mEmoticon);
}
@Override
public int hashCode() {
int result = mStartPos;
result = 31 * result + mEndPos;
result = 31 * result + mEmoticon.hashCode();
return result;
}
}
}

View File

@ -1,25 +0,0 @@
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#FFFFFF" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
</vector>

View File

@ -21,6 +21,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#FFFFFF" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF"
android:pathData="M17,2L7,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,1.99 2,1.99L17,22c1.1,0 2,-0.9 2,-2L19,4c0,-1.1 -0.9,-2 -2,-2zM12,4c1.1,0 2,0.9 2,2s-0.9,2 -2,2c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2zM12,20c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,12c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
<path android:fillColor="#FFFFFF" android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
</vector>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="-90"
android:endColor="@color/transparent"
android:startColor="#323232"/>
</shape>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="@dimen/message_bubble_corners_radius"
android:bottomRightRadius="@dimen/message_bubble_corners_radius"
android:bottomLeftRadius="@dimen/message_bubble_corners_radius"
android:topRightRadius="@dimen/message_bubble_corners_radius" />
<solid android:color="@color/white" />
</shape>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="@dimen/message_bubble_corners_radius"
android:topRightRadius="@dimen/message_bubble_corners_radius"
android:topLeftRadius="@dimen/message_bubble_corners_radius"
android:bottomRightRadius="@dimen/message_bubble_corners_radius" />
<solid android:color="@color/white" />
</shape>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="0dp"
android:bottomRightRadius="@dimen/message_bubble_corners_radius"
android:bottomLeftRadius="@dimen/message_bubble_corners_radius"
android:topRightRadius="@dimen/message_bubble_corners_radius" />
<solid android:color="@color/white" />
</shape>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="@dimen/message_bubble_corners_radius"
android:topRightRadius="0dp"
android:topLeftRadius="@dimen/message_bubble_corners_radius"
android:bottomRightRadius="@dimen/message_bubble_corners_radius" />
<solid android:color="@color/white" />
</shape>

View File

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bottom_buttons_layout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:animateLayoutChanges="true"
android:gravity="bottom"
android:orientation="horizontal"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/clear_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.6"
android:background="@color/nc_darkRed"
android:text="@string/nc_contacts_clear"
android:textAlignment="center"
android:textColor="@android:color/white"/>
<Button
android:id="@+id/done_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.4"
android:background="@color/nc_darkGreen"
android:text="@string/nc_contacts_done"
android:textAlignment="center"
android:textColor="@android:color/white"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>

View File

@ -151,7 +151,10 @@
android:visibility="gone"
app:enableInitialAnimation="false"
app:frontBackgroundColor="@color/colorPrimary"
app:frontImage="@drawable/ic_hearing_white_24dp"/>
app:animateRearImage="false"
app:rearBackgroundColor="@color/colorPrimaryDark"
app:frontImage="@drawable/ic_volume_up_white_24dp"
app:rearImage="@drawable/ic_volume_up_white_24dp"/>
</LinearLayout>

View File

@ -21,33 +21,42 @@
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey950">
<TextView
android:id="@+id/incomingCallTextView"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/nc_incoming_call"
android:textAlignment="center"
android:textColor="@color/white30"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
android:background="@drawable/incoming_gradient">
<TextView
android:id="@+id/conversationNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="28sp"
app:layout_constraintTop_toBottomOf="@+id/incomingCallTextView"
tools:text="Victor Gregorius Magnus"/>
<TextView
android:id="@+id/incomingCallTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/nc_incoming_call"
android:textAlignment="center"
android:textColor="@color/white30"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/conversationNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="28sp"
android:layout_marginBottom="16dp"
android:layout_below="@+id/incomingCallTextView"
tools:text="Victor Gregorius Magnus"/>
</RelativeLayout>
<ImageView
android:id="@+id/avatarImageView"

View File

@ -24,45 +24,84 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="@dimen/item_height"
android:layout_height="@dimen/item_height"
android:layout_centerInParent="true"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:indeterminate="true"
android:indeterminateTint="@color/colorPrimary"
android:indeterminateTintMode="src_in"
android:visibility="gone"/>
<RelativeLayout
android:id="@+id/emptyLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:id="@+id/wawingTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="👋"
android:textAlignment="center"
android:textSize="72sp"/>
<TextView
android:id="@+id/sendHiTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/wawingTextView"
android:layout_margin="8dp"
android:textAlignment="center"
android:textSize="20sp"/>
</RelativeLayout>
<com.stfalcon.chatkit.messages.MessagesList
android:id="@+id/messagesListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:layout_above="@+id/messageInputView"
android:paddingBottom="16dp"
android:visibility="gone"
app:dateHeaderTextSize="13sp"
app:incomingBubblePaddingBottom="@dimen/message_bubble_corners_padding"
app:incomingBubblePaddingLeft="@dimen/message_bubble_corners_padding"
app:incomingBubblePaddingRight="@dimen/message_bubble_corners_padding"
app:incomingBubblePaddingTop="@dimen/message_bubble_corners_padding"
app:incomingDefaultBubbleColor="@color/white_two"
app:incomingDefaultBubblePressedColor="@color/white_two"
app:incomingDefaultBubbleSelectedColor="@color/colorPrimaryDark"
app:incomingDefaultBubbleSelectedColor="@color/transparent"
app:incomingTextColor="@color/nc_incoming_text_default"
app:incomingTextLinkColor="@color/nc_incoming_text_default"
app:incomingTextSize="@dimen/chat_text_size"
app:incomingTimeTextSize="12sp"
app:incomingBubblePaddingTop="@dimen/message_bubble_corners_padding"
app:incomingBubblePaddingBottom="@dimen/message_bubble_corners_padding"
app:incomingBubblePaddingLeft="@dimen/message_bubble_corners_padding"
app:incomingBubblePaddingRight="@dimen/message_bubble_corners_padding"
app:outcomingBubblePaddingTop="@dimen/message_bubble_corners_padding"
app:outcomingBubblePaddingBottom="@dimen/message_bubble_corners_padding"
app:outcomingBubblePaddingLeft="@dimen/message_bubble_corners_padding"
app:outcomingBubblePaddingRight="@dimen/message_bubble_corners_padding"
app:outcomingBubblePaddingTop="@dimen/message_bubble_corners_padding"
app:outcomingDefaultBubbleColor="@color/colorPrimary"
app:outcomingDefaultBubblePressedColor="@color/colorPrimary"
app:outcomingDefaultBubbleSelectedColor="@color/colorPrimaryDark"
app:outcomingDefaultBubbleSelectedColor="@color/transparent"
app:outcomingTextColor="@color/nc_outcoming_text_default"
app:outcomingTextLinkColor="@color/nc_outcoming_text_default"
app:outcomingTextSize="@dimen/chat_text_size"
app:outcomingTimeTextColor="@color/warm_grey_four"
app:outcomingTimeTextSize="12sp"
app:dateHeaderTextSize="13sp"
app:textAutoLink="all"/>
<com.webianks.library.PopupBubble
android:id="@+id/popupBubbleView"
android:layout_margin="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/separator"
android:layout_centerHorizontal="true"
android:layout_margin="16dp"
android:paddingEnd="8dp"
app:pb_backgroundColor="@color/colorPrimary"
app:pb_icon="@drawable/ic_baseline_arrow_downward_24px"

View File

@ -45,9 +45,5 @@
</android.support.v4.widget.SwipeRefreshLayout>
<include
layout="@layout/bottom_buttons"
android:visibility="gone"/>
<include layout="@layout/fast_scroller"/>
</android.support.design.widget.CoordinatorLayout>

View File

@ -33,7 +33,7 @@
android:id="@id/messageUserAvatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="8dp"/>
<com.google.android.flexbox.FlexboxLayout
@ -64,14 +64,13 @@
app:layout_wrapBefore="true"/>
<TextView
android:id="@id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageText"
android:layout_marginStart="8dp"
app:layout_alignSelf="center"/>
</com.google.android.flexbox.FlexboxLayout>
<TextView
android:id="@id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/bubble"
android:layout_below="@id/bubble"
android:layout_marginStart="16dp"/>
</RelativeLayout>

View File

@ -47,14 +47,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignWithParentIfMissing="true"/>
<TextView
android:id="@id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageText"
android:layout_marginStart="8dp"
app:layout_alignSelf="center"/>
</com.google.android.flexbox.FlexboxLayout>
<TextView
android:id="@id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/bubble"
android:layout_below="@id/bubble"
android:layout_marginStart="16dp"/>
</RelativeLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<View
android:id="@+id/fast_scroller_bar"
android:layout_width="7dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@color/transparent"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/fast_scroller_bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="0dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_toStartOf="@+id/fast_scroller_handle"
android:background="@drawable/fast_scroller_bubble"
android:gravity="center"
android:textColor="?android:attr/textColorPrimaryInverse"
android:textSize="38sp"
android:visibility="gone"
tools:visibility="visible"
tools:text="A" />
<ImageView
android:id="@+id/fast_scroller_handle"
android:alpha="0.5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:paddingStart="6dp"
android:contentDescription="@null"
android:src="@drawable/fast_scroller_handle"/>
</RelativeLayout>
</merge>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/contacts_selection_done"
android:title="@string/nc_contacts_done"
app:showAsAction="always"
android:visible="false"/>
</menu>

View File

@ -35,4 +35,11 @@
android:title="@string/nc_new_conversation"
android:icon="@drawable/ic_add_white_24px"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/contacts_selection_done"
android:title="@string/nc_contacts_done"
app:showAsAction="always"
android:visible="false"/>
</menu>

View File

@ -17,7 +17,7 @@
<dimen name="avatar_size">40dp</dimen>
<dimen name="avatar_size_big">80dp</dimen>
<dimen name="avatar_size_very_big">@dimen/avatar_fetching_size_very_big</dimen>
<dimen name="avatar_fetching_size_very_big">140dp</dimen>
<dimen name="avatar_fetching_size_very_big">180dp</dimen>
<dimen name="avatar_corner_radius">20dp</dimen>
<dimen name="chat_text_size">14sp</dimen>

View File

@ -85,7 +85,6 @@
<!-- Room menu -->
<string name="nc_start_conversation">Start a conversation</string>
<string name="nc_configure_room">Configure conversation</string>
<string name="nc_configure_named_room">%1$s</string>
<string name="nc_leave">Leave conversation</string>
<string name="nc_rename">Rename conversation</string>
<string name="nc_set_password">Set a password</string>
@ -145,7 +144,6 @@
<string name="nc_share_text_pass">\nPassword: %1$s</string>
<!-- Magical stuff -->
<string name="nc_empty"></string>
<string name="nc_push_to_talk">Push-to-talk</string>
<string name="nc_push_to_talk_desc">With microphone disabled, click&amp;hold to use Push-to-talk</string>
<string name="nc_store_short_desc">Have private video calls and chat using your own server.</string>
@ -197,4 +195,8 @@ Find Nextcloud on https://nextcloud.com</string>
<string name="nc_description_more_menu_group">Menu for public conversation %1$s</string>
<string name="nc_description_send_message_button">Send message</string>
<!-- Chat empty state -->
<string name="nc_chat_empty">Click to be the first to say %1$s!</string>
<string name="nc_hello">Hello</string>
</resources>