Bunch of work on new conversation

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2018-02-02 11:40:42 +01:00
parent 9f11964fc5
commit 20b80b36b3
33 changed files with 860 additions and 179 deletions

View File

@ -111,7 +111,7 @@ dependencies {
debugImplementation "javax.transaction:transaction-api:1.1-rev-1"
implementation 'com.github.HITGIF:TextFieldBoxes:1.3.8'
implementation 'com.github.HITGIF:TextFieldBoxes:1.3.9'
implementation 'eu.davidea:flexible-adapter:5.0.0-rc4'
implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b1'
@ -138,6 +138,7 @@ dependencies {
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
implementation 'com.github.Kennyc1012:BottomSheet:2.4.0'
implementation 'eu.davidea:flipview:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation ('com.android.support.test.espresso:espresso-core:3.0.1', {
exclude group: 'com.android.support', module: 'support-annotations'

View File

@ -51,6 +51,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import eu.davidea.flexibleadapter.items.IFilterable;
import eu.davidea.flexibleadapter.utils.FlexibleUtils;
import eu.davidea.flipview.FlipView;
import eu.davidea.viewholders.FlexibleViewHolder;
public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.UserItemViewHolder> implements IFilterable {
@ -119,24 +120,23 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
if (userEntity.getBaseUrl().startsWith("http://") || userEntity.getBaseUrl().startsWith("https://")) {
holder.avatarImageView.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) holder.linearLayout.getLayoutParams();
layoutParams.setMarginStart(0);
layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
holder.linearLayout.setLayoutParams(layoutParams);
GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(),
participant.getUserId(), false), new LazyHeaders.Builder()
.setHeader("Accept", "image/*")
.setHeader("User-Agent", ApiUtils.getUserAgent())
.build());
int avatarSize = Math.round(NextcloudTalkApplication
.getSharedApplication().getResources().getDimension(R.dimen.avatar_size));
GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
.asBitmap()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(glideUrl)
.centerInside()
.override(avatarSize, avatarSize)
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(holder.avatarImageView);
.into(holder.avatarImageView.getFrontImageView());
} else {
holder.avatarImageView.setVisibility(View.GONE);
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) holder.linearLayout.getLayoutParams();
@ -161,11 +161,13 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
@BindView(R.id.secondary_text)
public TextView serverUrl;
@BindView(R.id.avatar_image)
public ImageView avatarImageView;
public FlipView avatarImageView;
@BindView(R.id.linear_layout)
LinearLayout linearLayout;
@BindView(R.id.more_menu)
ImageButton moreMenuButton;
@BindView(R.id.password_protected_image_view)
ImageView passwordProtectedImageView;
/**
* Default constructor.
@ -174,8 +176,7 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
super(view, adapter);
ButterKnife.bind(this, view);
moreMenuButton.setVisibility(View.GONE);
passwordProtectedImageView.setVisibility(View.GONE);
}
}
}

View File

@ -51,6 +51,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import eu.davidea.flexibleadapter.items.IFilterable;
import eu.davidea.flexibleadapter.utils.FlexibleUtils;
import eu.davidea.flipview.FlipView;
import eu.davidea.viewholders.FlexibleViewHolder;
public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder> implements IFilterable {
@ -120,6 +121,9 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
holder.passwordProtectedImageView.setVisibility(View.GONE);
}
int avatarSize = Math.round(NextcloudTalkApplication
.getSharedApplication().getResources().getDimension(R.dimen.avatar_size));
switch (room.getType()) {
case ROOM_TYPE_ONE_TO_ONE_CALL:
holder.avatarImageView.setVisibility(View.VISIBLE);
@ -133,12 +137,12 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
.asBitmap()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(glideUrl)
.centerInside()
.override(avatarSize, avatarSize)
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(holder.avatarImageView);
.into(holder.avatarImageView.getFrontImageView());
} else {
holder.avatarImageView.setVisibility(View.GONE);
@ -147,23 +151,23 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
case ROOM_GROUP_CALL:
GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
.asBitmap()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(R.drawable.ic_group_white_24px)
.centerInside()
.override(avatarSize, avatarSize)
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(holder.avatarImageView);
.into(holder.avatarImageView.getFrontImageView());
holder.avatarImageView.setVisibility(View.VISIBLE);
break;
case ROOM_PUBLIC_CALL:
GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
.asBitmap()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(R.drawable.ic_link_white_24px)
.centerInside()
.override(avatarSize, avatarSize)
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(holder.avatarImageView);
.into(holder.avatarImageView.getFrontImageView());
holder.avatarImageView.setVisibility(View.VISIBLE);
break;
default:
@ -188,7 +192,7 @@ public class CallItem extends AbstractFlexibleItem<CallItem.RoomItemViewHolder>
@BindView(R.id.secondary_text)
public TextView roomLastPing;
@BindView(R.id.avatar_image)
public ImageView avatarImageView;
public FlipView avatarImageView;
@BindView(R.id.more_menu)
public ImageButton moreMenuButton;
@BindView(R.id.password_protected_image_view)

View File

@ -0,0 +1,78 @@
/*
* 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/>.
*/
package com.nextcloud.talk.adapters.items;
import android.view.View;
import com.nextcloud.talk.R;
import java.util.List;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import eu.davidea.viewholders.FlexibleViewHolder;
public class EmptyFooterItem extends AbstractFlexibleItem<EmptyFooterItem.EmptyFooterItemViewHolder> {
int id;
public EmptyFooterItem(int id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (o instanceof EmptyFooterItem) {
EmptyFooterItem inItem = (EmptyFooterItem) o;
return id == inItem.getModel();
}
return false;
}
public int getModel() {
return id;
}
@Override
public int getLayoutRes() {
return R.layout.rv_item_empty_footer;
}
@Override
public EmptyFooterItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new EmptyFooterItemViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, EmptyFooterItemViewHolder holder, int position, List<Object> payloads) {
}
static class EmptyFooterItemViewHolder extends FlexibleViewHolder {
/**
* Default constructor.
*/
EmptyFooterItemViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
}
}
}

View File

@ -0,0 +1,103 @@
/*
* 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/>.
*/
package com.nextcloud.talk.adapters.items;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.nextcloud.talk.R;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractHeaderItem;
import eu.davidea.flexibleadapter.items.IFilterable;
import eu.davidea.viewholders.FlexibleViewHolder;
public class UserHeaderItem extends AbstractHeaderItem<UserHeaderItem.HeaderViewHolder> implements IFilterable {
private static final String TAG = "UserHeaderItem";
private String title;
public UserHeaderItem(String title) {
super();
setHidden(false);
setSelectable(false);
this.title = title;
}
@Override
public boolean filter(String constraint) {
return StringUtils.containsIgnoreCase(title, constraint);
}
public String getModel() {
return title;
}
@Override
public boolean equals(Object o) {
if (o instanceof UserHeaderItem) {
UserHeaderItem inItem = (UserHeaderItem) o;
return title.equals(inItem.getModel());
}
return false;
}
@Override
public int getLayoutRes() {
return R.layout.rv_item_title_header;
}
@Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new HeaderViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List<Object> payloads) {
if (payloads.size() > 0) {
Log.d(TAG, "We have payloads, so ignoring!");
} else {
holder.titleTextView.setText(title);
}
}
static class HeaderViewHolder extends FlexibleViewHolder {
@BindView(R.id.title_text_view)
public TextView titleTextView;
/**
* Default constructor.
*/
HeaderViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter, true);
ButterKnife.bind(this, view);
}
}
}

View File

@ -21,7 +21,6 @@
package com.nextcloud.talk.adapters.items;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
@ -45,17 +44,25 @@ import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import eu.davidea.flexibleadapter.items.IFilterable;
import eu.davidea.flexibleadapter.items.ISectionable;
import eu.davidea.flexibleadapter.utils.FlexibleUtils;
import eu.davidea.flipview.FlipView;
import eu.davidea.viewholders.FlexibleViewHolder;
public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder> implements IFilterable {
public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder> implements
ISectionable<UserItem.UserItemViewHolder, UserHeaderItem>, IFilterable {
private Participant participant;
private UserEntity userEntity;
private UserHeaderItem header;
public UserItem(Participant participant, UserEntity userEntity) {
private FlipView flipView;
public UserItem(Participant participant, UserEntity userEntity, UserHeaderItem userHeaderItem) {
this.participant = participant;
this.userEntity = userEntity;
this.header = userHeaderItem;
}
@Override
@ -84,6 +91,10 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
return userEntity;
}
public void flipItemSelection() {
flipView.flip(!flipView.isFlipped());
}
@Override
public int getLayoutRes() {
return R.layout.rv_item_contact;
@ -96,6 +107,9 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
@Override
public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) {
flipView = holder.avatarFlipView;
if (adapter.hasSearchText()) {
FlexibleUtils.highlightText(holder.contactDisplayName, participant.getName(), adapter.getSearchText());
} else {
@ -108,14 +122,17 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
.setHeader("User-Agent", ApiUtils.getUserAgent())
.build());
int avatarSize = Math.round(NextcloudTalkApplication
.getSharedApplication().getResources().getDimension(R.dimen.avatar_size));
GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
.asBitmap()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(glideUrl)
.centerInside()
.override(avatarSize, avatarSize)
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(holder.avatarImageView);
.into(holder.avatarFlipView.getFrontImageView());
}
@Override
@ -124,13 +141,23 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
StringUtils.containsIgnoreCase(participant.getName().trim(), constraint);
}
@Override
public UserHeaderItem getHeader() {
return header;
}
@Override
public void setHeader(UserHeaderItem header) {
this.header = header;
}
static class UserItemViewHolder extends FlexibleViewHolder {
@BindView(R.id.name_text)
public TextView contactDisplayName;
@BindView(R.id.avatar_image)
public ImageView avatarImageView;
@BindView(R.id.avatar_flip_view)
public FlipView avatarFlipView;
/**
* Default constructor.

View File

@ -117,7 +117,6 @@ public class NextcloudTalkApplication extends MultiDexApplication {
initializeWebRtc();
DisplayUtils.useCompatVectorIfNeeded();
try {
buildComponent();
} catch (final GeneralSecurityException exception) {

View File

@ -36,10 +36,10 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.evernote.android.job.JobRequest;
import com.nextcloud.talk.R;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.base.BaseController;
import com.nextcloud.talk.jobs.PushRegistrationJob;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.ApplicationWideMessageHolder;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;

View File

@ -31,7 +31,6 @@ import android.support.design.widget.BottomNavigationView;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.text.InputType;
@ -81,6 +80,7 @@ import javax.inject.Inject;
import autodagger.AutoInjector;
import butterknife.BindView;
import eu.davidea.fastscroller.FastScroller;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import io.reactivex.android.schedulers.AndroidSchedulers;
@ -90,7 +90,7 @@ import retrofit2.HttpException;
@AutoInjector(NextcloudTalkApplication.class)
public class CallsListController extends BaseController implements SearchView.OnQueryTextListener,
FlexibleAdapter.OnItemClickListener {
FlexibleAdapter.OnItemClickListener, FastScroller.OnScrollStateChangeListener {
public static final String TAG = "CallsListController";
@ -110,6 +110,11 @@ public class CallsListController extends BaseController implements SearchView.On
@BindView(R.id.swipe_refresh_layout)
SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.fast_scroller)
FastScroller fastScroller;
private SmoothScrollLinearLayoutManager layoutManager;
private UserEntity userEntity;
private Disposable roomsQueryDisposable;
private FlexibleAdapter<CallItem> adapter;
@ -117,6 +122,7 @@ public class CallsListController extends BaseController implements SearchView.On
private BottomSheet bottomSheet;
private MenuItem searchItem;
private Menu menuVariable;
private SearchView searchView;
private String searchQuery;
@ -158,7 +164,6 @@ public class CallsListController extends BaseController implements SearchView.On
adapter.addListener(this);
prepareViews();
}
@Override
@ -222,10 +227,32 @@ public class CallsListController extends BaseController implements SearchView.On
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_new_conversation:
searchItem.setVisible(false);
menuVariable.findItem(R.id.action_new_conversation).setVisible(false);
Bundle bundle = new Bundle();
bundle.putBoolean(BundleKeys.KEY_NEW_CONVERSATION, true);
if (getParentController() != null) {
getParentController().getRouter().pushController(
(RouterTransaction.with(new ContactsController(bundle))
.pushChangeHandler(new VerticalChangeHandler())
.popChangeHandler(new VerticalChangeHandler())));
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_filter, menu);
inflater.inflate(R.menu.menu_conversation_plus_filter, menu);
menuVariable = menu;
searchItem = menu.findItem(R.id.action_search);
initSearchView();
}
@ -312,7 +339,7 @@ public class CallsListController extends BaseController implements SearchView.On
}
private void prepareViews() {
LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
@ -325,6 +352,16 @@ public class CallsListController extends BaseController implements SearchView.On
swipeRefreshLayout.setOnRefreshListener(() -> fetchData(false));
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
fastScroller.addOnScrollStateChangeListener(this);
adapter.setFastScroller(fastScroller);
fastScroller.setBubbleTextCreator(position -> {
String displayName = adapter.getItem(position).getModel().getDisplayName();
if(displayName.length() > 8) {
displayName = displayName.substring(0, 4) + "...";
}
return displayName;
});
}
private void dispose(@Nullable Disposable disposable) {
@ -427,8 +464,14 @@ public class CallsListController extends BaseController implements SearchView.On
.pushChangeHandler(new HorizontalChangeHandler()));
}
bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create();
if (bottomSheet.getWindow() != null) {
boolean isNew = false;
if (bottomSheet == null) {
bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create();
isNew = true;
}
if (bottomSheet.getWindow() != null && isNew) {
bottomSheet.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
@ -465,4 +508,9 @@ public class CallsListController extends BaseController implements SearchView.On
protected String getTitle() {
return getResources().getString(R.string.nc_app_name);
}
@Override
public void onFastScrollerStateChange(boolean scrolling) {
swipeRefreshLayout.setEnabled(!scrolling);
}
}

View File

@ -31,12 +31,10 @@ import android.support.design.widget.BottomNavigationView;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.text.InputType;
import android.text.TextUtils;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -45,6 +43,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.LinearLayout;
import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
@ -52,6 +52,8 @@ import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
import com.nextcloud.talk.R;
import com.nextcloud.talk.activities.CallActivity;
import com.nextcloud.talk.adapters.items.EmptyFooterItem;
import com.nextcloud.talk.adapters.items.UserHeaderItem;
import com.nextcloud.talk.adapters.items.UserItem;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.application.NextcloudTalkApplication;
@ -61,6 +63,7 @@ import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.models.json.participants.Participant;
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.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;
@ -69,6 +72,7 @@ import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -77,9 +81,15 @@ import javax.inject.Inject;
import autodagger.AutoInjector;
import butterknife.BindView;
import butterknife.OnClick;
import butterknife.Optional;
import eu.davidea.fastscroller.FastScroller;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.SelectableAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.flipview.FlipView;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@ -88,7 +98,7 @@ import retrofit2.HttpException;
@AutoInjector(NextcloudTalkApplication.class)
public class ContactsController extends BaseController implements SearchView.OnQueryTextListener,
ActionMode.Callback, FlexibleAdapter.OnItemClickListener {
FlexibleAdapter.OnItemClickListener, FastScroller.OnScrollStateChangeListener {
public static final String TAG = "ContactsController";
@ -105,33 +115,67 @@ public class ContactsController extends BaseController implements SearchView.OnQ
@BindView(R.id.swipe_refresh_layout)
SwipeRefreshLayout swipeRefreshLayout;
@Nullable
@BindView(R.id.bottom_buttons_layout)
LinearLayout bottomButtonsLinearLayout;
@BindView(R.id.fast_scroller)
FastScroller fastScroller;
@Nullable
@BindView(R.id.clear_button)
Button clearButton;
private UserEntity userEntity;
private Disposable contactsQueryDisposable;
private Disposable cacheQueryDisposable;
private FlexibleAdapter<UserItem> adapter;
private List<UserItem> contactItems = new ArrayList<>();
private FlexibleAdapter adapter;
private List<AbstractFlexibleItem> contactItems = new ArrayList<>();
private SmoothScrollLinearLayoutManager layoutManager;
private MenuItem searchItem;
private SearchView searchView;
private String searchQuery;
private ActionMode actionMode;
private boolean isNewConversationView;
private HashMap<String, UserHeaderItem> userHeaderItems = new HashMap<String, UserHeaderItem>();
public ContactsController() {
super();
setHasOptionsMenu(true);
}
public ContactsController(Bundle args) {
super(args);
setHasOptionsMenu(true);
if (args.containsKey(BundleKeys.KEY_NEW_CONVERSATION)) {
isNewConversationView = true;
}
}
@Override
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
return inflater.inflate(R.layout.controller_generic_rv, container, false);
}
@Override
protected void onAttach(@NonNull View view) {
super.onAttach(view);
if (getActionBar() != null && isNewConversationView) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
@Override
protected void onViewBound(@NonNull View view) {
super.onViewBound(view);
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
FlipView.resetLayoutAnimationDelay(true, 1000L);
FlipView.stopLayoutAnimation();
userEntity = userUtils.getCurrentUser();
if (userEntity == null) {
@ -144,15 +188,47 @@ public class ContactsController extends BaseController implements SearchView.OnQ
if (adapter == null) {
adapter = new FlexibleAdapter<>(contactItems, getActivity(), false);
adapter.setNotifyChangeOfUnfilteredItems(true)
.setMode(SelectableAdapter.Mode.MULTI);
if (userEntity != null) {
fetchData();
}
}
adapter.setStickyHeaderElevation(5)
.setUnlinkAllItemsOnRemoveHeaders(true)
.setDisplayHeadersAtStartUp(true)
.setStickyHeaders(true);
adapter.addListener(this);
prepareViews();
}
@Optional
@OnClick(R.id.clear_button)
public void onClearButtonClick() {
if (adapter != null) {
List<Integer> selectedPositions = adapter.getSelectedPositions();
for (Integer position : selectedPositions) {
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 initSearchView() {
if (getActivity() != null) {
SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
@ -172,10 +248,14 @@ public class ContactsController extends BaseController implements SearchView.OnQ
final View mSearchEditFrame = searchView
.findViewById(android.support.v7.appcompat.R.id.search_edit_frame);
BottomNavigationView bottomNavigationView = getParentController().getView().findViewById(R.id.navigation);
BottomNavigationView bottomNavigationView = null;
if (getParentController() != null && getParentController().getView() != null) {
bottomNavigationView = getParentController().getView().findViewById(R.id.navigation);
}
Handler handler = new Handler();
ViewTreeObserver vto = mSearchEditFrame.getViewTreeObserver();
BottomNavigationView finalBottomNavigationView = bottomNavigationView;
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
int oldVisibility = -1;
@ -186,10 +266,14 @@ public class ContactsController extends BaseController implements SearchView.OnQ
if (currentVisibility != oldVisibility) {
if (currentVisibility == View.VISIBLE) {
handler.postDelayed(() -> bottomNavigationView.setVisibility(View.GONE), 100);
if (finalBottomNavigationView != null) {
handler.postDelayed(() -> finalBottomNavigationView.setVisibility(View.GONE), 100);
}
} else {
handler.postDelayed(() -> {
bottomNavigationView.setVisibility(View.VISIBLE);
if (finalBottomNavigationView != null) {
finalBottomNavigationView.setVisibility(View.VISIBLE);
}
searchItem.setVisible(contactItems.size() > 0);
}, 500);
}
@ -202,11 +286,23 @@ public class ContactsController extends BaseController implements SearchView.OnQ
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getRouter().popCurrentController();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_filter, menu);
inflater.inflate(R.menu.menu_conversation_plus_filter, menu);
searchItem = menu.findItem(R.id.action_search);
menu.findItem(R.id.action_new_conversation).setVisible(false);
initSearchView();
}
@ -218,6 +314,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
searchItem.expandActionView();
searchView.setQuery(adapter.getSearchText(), false);
}
}
private void fetchData() {
@ -226,6 +323,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
Set<Sharee> shareeHashSet = new HashSet<>();
contactItems = new ArrayList<>();
userHeaderItems = new HashMap<>();
RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(userEntity.getBaseUrl(),
"");
@ -234,7 +332,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(shareesOverall -> {
.subscribe((ShareesOverall shareesOverall) -> {
if (shareesOverall != null) {
if (shareesOverall.getOcs().getData().getUsers() != null) {
@ -252,18 +350,52 @@ public class ContactsController extends BaseController implements SearchView.OnQ
if (!sharee.getValue().getShareWith().equals(userEntity.getUsername())) {
participant = new Participant();
participant.setName(sharee.getLabel());
String headerTitle;
headerTitle = sharee.getLabel().substring(0, 1).toUpperCase();
UserHeaderItem userHeaderItem;
if (!userHeaderItems.containsKey(headerTitle)) {
userHeaderItem = new UserHeaderItem(headerTitle);
userHeaderItems.put(headerTitle, userHeaderItem);
}
participant.setUserId(sharee.getValue().getShareWith());
contactItems.add(new UserItem(participant, userEntity));
contactItems.add(new UserItem(participant, userEntity,
userHeaderItems.get(headerTitle)));
}
}
Collections.sort(contactItems, (userItem, t1) ->
userItem.getModel().getName().compareToIgnoreCase(t1.getModel().getName()));
userHeaderItems = new HashMap<>();
Collections.sort(contactItems, (o1, o2) -> {
String firstName;
String secondName;
if (o1 instanceof UserItem) {
firstName = ((UserItem) o1).getModel().getName();
} else {
firstName = ((UserHeaderItem) o1).getModel();
}
if (o2 instanceof UserItem) {
secondName = ((UserItem) o2).getModel().getName();
} else {
secondName = ((UserHeaderItem) o2).getModel();
}
return firstName.compareToIgnoreCase(secondName);
});
adapter.updateDataSet(contactItems, true);
searchItem.setVisible(contactItems.size() > 0);
swipeRefreshLayout.setRefreshing(false);
if (isNewConversationView) {
checkAndHandleBottomButtons();
}
}
}, throwable -> {
@ -299,7 +431,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
}
private void prepareViews() {
LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
@ -311,6 +443,17 @@ public class ContactsController extends BaseController implements SearchView.OnQ
swipeRefreshLayout.setOnRefreshListener(this::fetchData);
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
fastScroller.addOnScrollStateChangeListener(this);
adapter.setFastScroller(fastScroller);
fastScroller.setBubbleTextCreator(position -> {
IFlexible abstractFlexibleItem = adapter.getItem(position);
if (abstractFlexibleItem instanceof UserItem) {
return ((UserItem)adapter.getItem(position)).getHeader().getModel();
} else {
return ((UserHeaderItem)adapter.getItem(position)).getModel();
}
});
}
private void dispose(@Nullable Disposable disposable) {
@ -330,8 +473,10 @@ public class ContactsController extends BaseController implements SearchView.OnQ
}
}
@Override
public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) {
adapter.onSaveInstanceState(outState);
super.onSaveViewState(view, outState);
if (searchView != null && !TextUtils.isEmpty(searchView.getQuery())) {
outState.putString(KEY_SEARCH_QUERY, searchView.getQuery().toString());
@ -342,6 +487,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) {
super.onRestoreViewState(view, savedViewState);
searchQuery = savedViewState.getString(KEY_SEARCH_QUERY, "");
if (adapter != null) {
adapter.onRestoreInstanceState(savedViewState);
}
}
@Override
@ -377,110 +525,90 @@ public class ContactsController extends BaseController implements SearchView.OnQ
}
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
adapter.setMode(SelectableAdapter.Mode.MULTI);
public boolean onItemClick(int position) {
if (adapter.getItem(position) instanceof UserItem) {
if (!isNewConversationView) {
UserItem userItem = (UserItem) adapter.getItem(position);
RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(userEntity.getBaseUrl(), "1",
userItem.getModel().getUserId());
ncApi.createRoom(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()),
retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<RoomOverall>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(RoomOverall roomOverall) {
overridePushHandler(new NoOpControllerChangeHandler());
overridePopHandler(new NoOpControllerChangeHandler());
Intent callIntent = new Intent(getActivity(), CallActivity.class);
Bundle bundle = new Bundle();
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken());
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(userEntity));
callIntent.putExtras(bundle);
startActivity(callIntent);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
} else {
((UserItem) adapter.getItem(position)).flipItemSelection();
adapter.toggleSelection(position);
checkAndHandleBottomButtons();
}
}
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return false;
}
private void checkAndHandleBottomButtons() {
if (adapter != null && bottomButtonsLinearLayout != null && clearButton != null) {
if (adapter.getSelectedItemCount() > 0) {
if (bottomButtonsLinearLayout.getVisibility() != View.VISIBLE) {
bottomButtonsLinearLayout.setVisibility(View.VISIBLE);
}
} else {
bottomButtonsLinearLayout.setVisibility(View.GONE);
}
} else if (bottomButtonsLinearLayout != null) {
bottomButtonsLinearLayout.setVisibility(View.GONE);
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode actionMode) {
adapter.setMode(SelectableAdapter.Mode.IDLE);
actionMode = null;
}
/*@Override
public boolean onItemClick(int position) {
if (actionMode != null && position != RecyclerView.NO_POSITION) {
// Mark the position selected
toggleSelection(position);
return true;
if (bottomButtonsLinearLayout != null && bottomButtonsLinearLayout.getVisibility() == View.VISIBLE) {
if (adapter.getScrollableFooters().size() == 0) {
adapter.addScrollableFooterWithDelay(new EmptyFooterItem(999), 0, layoutManager
.findLastVisibleItemPosition() == adapter.getItemCount() - 1);
}
} else {
// Handle the item click listener
// We don't need to activate anything
return false;
if (adapter != null) {
adapter.removeAllScrollableFooters();
}
}
}*/
private void toggleSelection(int position) {
adapter.toggleSelection(position);
int count = adapter.getSelectedItemCount();
if (count == 0) {
actionMode.finish();
} else {
//setContextTitle(count);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
adapter.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (adapter != null) {
adapter.onRestoreInstanceState(savedInstanceState);
}
}
@Override
public boolean onItemClick(int position) {
UserItem userItem = adapter.getItem(position);
RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(userEntity.getBaseUrl(), "1",
userItem.getModel().getUserId());
ncApi.createRoom(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()),
retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<RoomOverall>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(RoomOverall roomOverall) {
overridePushHandler(new NoOpControllerChangeHandler());
overridePopHandler(new NoOpControllerChangeHandler());
Intent callIntent = new Intent(getActivity(), CallActivity.class);
Bundle bundle = new Bundle();
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken());
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(userEntity));
callIntent.putExtras(bundle);
startActivity(callIntent);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
return true;
}
@Override
protected String getTitle() {
return getResources().getString(R.string.nc_app_name);
if (!isNewConversationView) {
return getResources().getString(R.string.nc_app_name);
} else {
return getResources().getString(R.string.nc_select_contacts);
}
}
@Override
public void onFastScrollerStateChange(boolean scrolling) {
swipeRefreshLayout.setEnabled(!scrolling);
}
}

View File

@ -39,10 +39,10 @@ import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.nextcloud.talk.R;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.base.BaseController;
import com.nextcloud.talk.utils.AccountUtils;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.ApplicationWideMessageHolder;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;

View File

@ -31,6 +31,7 @@ import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@ -129,16 +130,29 @@ public class SwitchAccountController extends BaseController {
};
public SwitchAccountController() {
setHasOptionsMenu(true);
}
public SwitchAccountController(Bundle args) {
super(args);
setHasOptionsMenu(true);
if (args.containsKey(BundleKeys.KEY_IS_ACCOUNT_IMPORT)) {
isAccountImport = true;
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getRouter().popCurrentController();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
return inflater.inflate(R.layout.controller_generic_rv, container, false);
@ -204,6 +218,14 @@ public class SwitchAccountController extends BaseController {
prepareViews();
}
@Override
protected void onAttach(@NonNull View view) {
super.onAttach(view);
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
private void prepareViews() {
LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);

View File

@ -53,6 +53,7 @@ public abstract class BaseController extends RefWatchingController {
@Override
protected void onAttach(@NonNull View view) {
setTitle();
getActionBar().setDisplayHomeAsUpEnabled(false);
super.onAttach(view);
}

View File

@ -36,10 +36,10 @@ import com.kennyc.bottomsheet.adapters.AppAdapter;
import com.nextcloud.talk.R;
import com.nextcloud.talk.adapters.items.AppItem;
import com.nextcloud.talk.adapters.items.MenuItem;
import com.nextcloud.talk.models.json.rooms.Room;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.base.BaseController;
import com.nextcloud.talk.events.BottomSheetLockEvent;
import com.nextcloud.talk.models.json.rooms.Room;
import com.nextcloud.talk.utils.ShareUtils;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;

View File

@ -28,8 +28,8 @@ import android.util.Log;
import com.github.aurae.retrofit2.LoganSquareConverterFactory;
import com.nextcloud.talk.BuildConfig;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.preferences.AppPreferences;
import com.nextcloud.talk.utils.ssl.MagicTrustManager;
import com.nextcloud.talk.utils.ssl.SSLSocketFactoryCompat;

View File

@ -40,9 +40,9 @@ import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.nextcloud.talk.R;
import com.nextcloud.talk.activities.CallActivity;
import com.nextcloud.talk.models.SignatureVerification;
import com.nextcloud.talk.models.json.push.DecryptedPushMessage;
import com.nextcloud.talk.models.json.push.PushMessage;
import com.nextcloud.talk.models.SignatureVerification;
import com.nextcloud.talk.utils.NotificationUtils;
import com.nextcloud.talk.utils.PushUtils;
import com.nextcloud.talk.utils.bundle.BundleKeys;

View File

@ -0,0 +1,45 @@
/*
* 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/>.
*/
package com.nextcloud.talk.utils;
import android.content.Context;
import android.util.AttributeSet;
import eu.davidea.flipview.FlipView;
public class MagicFlipView extends FlipView {
public MagicFlipView(Context context) {
super(context);
}
public MagicFlipView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDetachedFromWindow() {
try {
super.onDetachedFromWindow();
} catch (IllegalArgumentException e) {
stopFlipping();
}
}
}

View File

@ -36,4 +36,5 @@ public class BundleKeys {
public static final String KEY_CALL_SESSION = "KEY_CALL_SESSION";
public static final String KEY_ROOM_TOKEN = "KEY_ROOM_TOKEN";
public static final String KEY_USER_ENTITY = "KEY_USER_ENTITY";
public static final String KEY_NEW_CONVERSATION = "KEY_NEW_CONVERSATION";
}

View File

@ -26,12 +26,12 @@ import android.util.Log;
import com.bluelinelabs.logansquare.LoganSquare;
import com.nextcloud.talk.R;
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.events.MediaStreamEvent;
import com.nextcloud.talk.events.PeerConnectionEvent;
import com.nextcloud.talk.events.SessionDescriptionSendEvent;
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
import org.greenrobot.eventbus.EventBus;
import org.webrtc.DataChannel;

View File

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Original code:
* Part of the code related to codec handling is originally:
*
*
* Copyright 2016 The WebRTC Project Authors. All rights reserved.

View File

@ -0,0 +1,25 @@
<!--
~ 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: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="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,52 @@
<?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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bottom_buttons_layout"
android:layout_width="match_parent"
android:layout_height="32dp"
android:alpha="0.7"
android:animateLayoutChanges="true"
android:gravity="bottom"
android:orientation="horizontal"
android:visibility="gone">
<Button
android:id="@+id/clear_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.6"
android:background="@color/nc_darkRed"
android:text="@string/nc_contacts_clear"
android:textAlignment="center"
android:textColor="@color/nc_white_color_complete"/>
<Button
android:id="@+id/done_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:background="@color/nc_darkGreen"
android:text="@string/nc_contacts_done"
android:textAlignment="center"
android:textColor="@color/nc_white_color_complete"/>
</LinearLayout>

View File

@ -20,10 +20,10 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/nc_white_color">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/nc_white_color">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"

View File

@ -57,9 +57,9 @@
android:layout_marginBottom="12dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:alpha="0.7"
android:background="#0000"
android:enabled="false"
android:alpha="0.7"
android:text="@string/nc_proceed"
android:textColor="@color/colorPrimary"/>

View File

@ -19,17 +19,37 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/nc_white_color">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/nc_white_color">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/rv_item_call"/>
android:layout_height="match_parent">
</android.support.v4.widget.SwipeRefreshLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/rv_item_call"/>
</FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout>
<include
layout="@layout/bottom_buttons"
android:layout_width="match_parent"
android:layout_height="32dp"
android:layout_gravity="bottom"
android:visibility="gone"/>
<include layout="@layout/fast_scroller"/>
</FrameLayout>

View File

@ -0,0 +1,38 @@
<?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/>.
-->
<eu.davidea.fastscroller.FastScroller 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/fast_scroller"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignBottom="@+id/swipe_refresh_layout"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/swipe_refresh_layout"
android:layout_centerHorizontal="true"
app:fastScrollerAutoHideDelayInMillis="1000"
app:fastScrollerAutoHideEnabled="true"
app:fastScrollerBubbleEnabled="true"
app:fastScrollerBubblePosition="adjacent"
app:fastScrollerIgnoreTouchesOutsideHandle="false"
tools:visibility="visible">
</eu.davidea.fastscroller.FastScroller>

View File

@ -34,10 +34,15 @@
android:layout_centerVertical="true"
android:layout_marginStart="@dimen/activity_horizontal_margin">
<ImageView
<com.nextcloud.talk.utils.MagicFlipView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/avatar_image"
android:layout_width="@dimen/avatar_size"
android:layout_height="@dimen/avatar_size"/>
android:layout_height="@dimen/avatar_size"
app:animationDuration="170"
app:checked="false"
app:enableInitialAnimation="true"
app:rearBackgroundColor="@color/colorPrimary"/>
<ImageView
android:id="@+id/password_protected_image_view"
@ -83,11 +88,15 @@
<ImageButton
android:id="@+id/more_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginStart="@dimen/margin_between_elements"
android:background="@drawable/ic_more_horiz_black_24dp"/>
android:background="?android:attr/selectableItemBackground"
android:paddingEnd="8dp"
android:paddingStart="8dp"
android:scaleType="center"
android:src="@drawable/ic_more_horiz_black_24dp"/>
</RelativeLayout>

View File

@ -28,23 +28,27 @@
android:layout_height="@dimen/item_height"
android:orientation="vertical">
<ImageView
android:id="@+id/avatar_image"
<com.nextcloud.talk.utils.MagicFlipView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/avatar_flip_view"
android:layout_width="@dimen/avatar_size"
android:layout_height="@dimen/avatar_size"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginStart="@dimen/activity_horizontal_margin"/>
android:layout_marginStart="@dimen/activity_horizontal_margin"
app:animationDuration="170"
app:enableInitialAnimation="true"
app:rearBackgroundColor="@color/colorPrimary"/>
<TextView
android:id="@+id/name_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:ellipsize="end"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_toEndOf="@id/avatar_flip_view"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceListItem"
android:layout_toEndOf="@id/avatar_image"
tools:text="Contact item text"/>
</RelativeLayout>

View File

@ -0,0 +1,27 @@
<?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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="32dp"
android:orientation="vertical">
</LinearLayout>

View File

@ -0,0 +1,39 @@
<?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/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.7"
android:background="@color/colorPrimary"
android:elevation="5dp">
<TextView
android:id="@+id/title_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="24dp"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:textColor="@color/nc_white_color_complete"/>
</RelativeLayout>

View File

@ -36,8 +36,8 @@
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_above="@id/peer_nick_text_view"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_videocam_off_white_24px"
android:visibility="invisible"/>
@ -56,9 +56,9 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_marginTop="4dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:textColor="@color/nc_white_color_complete"/>
</RelativeLayout>

View File

@ -28,6 +28,11 @@
android:icon="@drawable/ic_search_white_24dp"
app:showAsAction="collapseActionView|always"
android:animateLayoutChanges="true"
app:actionViewClass="android.support.v7.widget.SearchView"/>
app:actionViewClass="android.support.v7.widget.SearchView"
/>
</menu>
<item android:id="@+id/action_new_conversation"
android:title="@string/nc_new_conversation"
android:icon="@drawable/ic_add_white_24px"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -28,6 +28,7 @@
<string name="nc_never">Never joined</string>
<string name="nc_search">Search</string>
<string name="nc_new_conversation">Create a new conversation</string>
<string name="nc_certificate_dialog_title">Check out the certificate</string>
<string name="nc_certificate_dialog_text">Do you trust the until now unknown SSL certificate, issued by %1$s for %2$s, valid from %3$s to %4$s?</string>
@ -84,8 +85,11 @@
<string name="nc_delete_call">Delete call</string>
<!-- Contacts -->
<string name="nc_select_contacts">Select contacts</string>
<string name="nc_one_contact_selected">contact selected</string>
<string name="nc_more_contacts_selected">contacts selected</string>
<string name="nc_contacts_clear">Clear</string>
<string name="nc_contacts_done">Done</string>
<!-- Permissions -->
<string name="nc_permissions">Permissions need to be granted to establish a video and/or audio call. Please click \"ALLOW\" in the upcoming system dialog.</string>