diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle b/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle index c1f3b9d..b21704e 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle @@ -33,8 +33,8 @@ android { applicationId "de.beyondsoft.ownchat" minSdkVersion 19 targetSdkVersion 27 - versionCode 16 - versionName "1.16" + versionCode 18 + versionName "1.18" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true } diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/MessageRealmRepository.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/MessageRealmRepository.java index ac51e91..e43380a 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/MessageRealmRepository.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/MessageRealmRepository.java @@ -84,6 +84,7 @@ public class MessageRealmRepository implements Repository { public Completable update(Iterable items, Specification specification) { final RealmWrapper realmWrapper = new RealmWrapper(); MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification; + return Observable.from(items) .doOnSubscribe(() -> { realmWrapper.realm = mRealmProvider.provide(); @@ -114,6 +115,7 @@ public class MessageRealmRepository implements Repository { public Observable> updateMessages(Iterable items, Specification specification) { final RealmWrapper realmWrapper = new RealmWrapper(); MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification; + return Observable.from(items) .flatMapIterable((Func1>) message -> items) .map(mToRealmMapper::from) @@ -147,6 +149,7 @@ public class MessageRealmRepository implements Repository { public Completable remove(Specification specification) { RealmWrapper realmWrapper = new RealmWrapper(); MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification; + return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm).findFirst()) .doOnSubscribe(() -> { realmWrapper.realm = mRealmProvider.provide(); @@ -167,10 +170,8 @@ public class MessageRealmRepository implements Repository { @Override public Completable removeAll() { RealmWrapper realmWrapper = new RealmWrapper(); - return Observable.fromCallable((Callable) () -> { - realmWrapper.realm.delete(MessageRealm.class); - return null; - }) + + return Observable.fromCallable((Callable) () -> { realmWrapper.realm.delete(MessageRealm.class); return null; }) .doOnSubscribe(() -> { realmWrapper.realm = mRealmProvider.provide(); realmWrapper.realm.beginTransaction(); @@ -191,6 +192,7 @@ public class MessageRealmRepository implements Repository { RealmWrapper realmWrapper = new RealmWrapper(); MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification; ((MessageByIdSpecification) specification).setMessageId(id); + return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm).findFirst()) .map(mToMessageMapper::from) .doOnSubscribe(() -> { @@ -211,6 +213,7 @@ public class MessageRealmRepository implements Repository { public Observable> queryForAll(Specification specification) { RealmWrapper realmWrapper = new RealmWrapper(); RealmSpecification realmSpecification = (RealmSpecification) specification; + return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm) .findAll() .sort(MessageRealm.CREATED_AT, Sort.DESCENDING)) @@ -230,5 +233,4 @@ public class MessageRealmRepository implements Repository { realmWrapper.realm.close(); }); } - } diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/models/MessageRealm.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/models/MessageRealm.java index d64866b..6e56267 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/models/MessageRealm.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/models/MessageRealm.java @@ -15,6 +15,7 @@ public class MessageRealm extends RealmObject { public static final String CREATED_AT = "createdAt"; public static final String USER_ID = "userId"; public static final String MESSAGE = "message"; + public static final String FILENAME = "filename"; @PrimaryKey private String uniqueId; diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/specifications/MessagesByGroupAndSearchTextSpecification.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/specifications/MessagesByGroupAndSearchTextSpecification.java index 1e2932c..a34c7a4 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/specifications/MessagesByGroupAndSearchTextSpecification.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/data/repository/specifications/MessagesByGroupAndSearchTextSpecification.java @@ -23,8 +23,12 @@ public class MessagesByGroupAndSearchTextSpecification implements RealmSpecifica @Override public RealmQuery toRealmQuery(Realm realm) { return realm.where(MessageRealm.class) - .contains(MessageRealm.MESSAGE, mSearchText, Case.INSENSITIVE) .equalTo(MessageRealm.GROUP_ID, mGroupId) - .equalTo(MessageRealm.USER_ID, mUserId); + .equalTo(MessageRealm.USER_ID, mUserId) + .beginGroup() + .contains(MessageRealm.MESSAGE, mSearchText, Case.INSENSITIVE) + .or() + .contains(MessageRealm.FILENAME, mSearchText, Case.INSENSITIVE) + .endGroup(); } } diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/fragments/DocPickerFragment.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/fragments/DocPickerFragment.java index 039fe71..ae715b0 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/fragments/DocPickerFragment.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/fragments/DocPickerFragment.java @@ -23,7 +23,7 @@ import de.beyondsoft.ownchat.filepicker.models.Document; import de.beyondsoft.ownchat.filepicker.models.FileType; import de.beyondsoft.ownchat.filepicker.utils.MediaStoreHelper; import de.beyondsoft.ownchat.filepicker.utils.TabLayoutHelper; -import de.beyondsoft.ownchat.filepicker.utils.Utils; +import de.beyondsoft.ownchat.filepicker.utils.FilePickerUtils; /** * Created by Lyndon on 26-Oct-2017. @@ -146,6 +146,6 @@ public class DocPickerFragment extends BaseFragment { private ArrayList filterDocuments(final String[] type, List documents) { final Predicate docType = document -> document.isThisType(type); - return new ArrayList<>(Utils.filter(new HashSet<>(documents),docType)); + return new ArrayList<>(FilePickerUtils.filter(new HashSet<>(documents),docType)); } } diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/BaseFile.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/BaseFile.java index 2a99815..5cc9ea9 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/BaseFile.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/BaseFile.java @@ -3,7 +3,7 @@ package de.beyondsoft.ownchat.filepicker.models; import android.os.Parcel; import android.os.Parcelable; -import de.beyondsoft.ownchat.filepicker.utils.Utils; +import de.beyondsoft.ownchat.filepicker.utils.FilePickerUtils; /** * Created by Lyndon on 26-Oct-2017. @@ -42,7 +42,7 @@ public class BaseFile implements Parcelable { public boolean isImage() { String[] types = {"jpg","jpeg","png","gif"}; - return Utils.contains(types, this.path); + return FilePickerUtils.contains(types, this.path); } @Override diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/Document.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/Document.java index 0235411..614256d 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/Document.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/models/Document.java @@ -2,7 +2,7 @@ package de.beyondsoft.ownchat.filepicker.models; import java.io.File; -import de.beyondsoft.ownchat.filepicker.utils.Utils; +import de.beyondsoft.ownchat.filepicker.utils.FilePickerUtils; /** * Created by Lyndon on 26-Oct-2017. @@ -78,7 +78,7 @@ public class Document extends BaseFile { public boolean isThisType(String[] types) { - return Utils.contains(types, this.path); + return FilePickerUtils.contains(types, this.path); } public FileType getFileType() diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/Utils.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/FilePickerUtils.java similarity index 79% rename from BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/Utils.java rename to BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/FilePickerUtils.java index 034ca04..c119160 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/Utils.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/FilePickerUtils.java @@ -12,18 +12,16 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; -/** - * Created by Lyndon on 26-Oct-2017. - */ - -public class Utils { +public class FilePickerUtils { public static Collection filter(Collection target, Predicate predicate) { - Collection result = new ArrayList(); - for (T element: target) { - if (predicate.apply(element)) { + Collection result = new ArrayList<>(); + + for(T element : target) { + if(predicate.apply(element)) { result.add(element); } } + return result; } @@ -31,15 +29,16 @@ public class Utils { String name = file.getName(); try { return name.substring(name.lastIndexOf(".") + 1); - } catch (Exception e) { + } catch(Exception e) { return ""; } } public static boolean contains(String[] types, String path) { - for (String string : types) { - if (path.toLowerCase().endsWith(string)) return true; + for(String string : types) { + if(path.toLowerCase().endsWith(string)) return true; } + return false; } @@ -59,15 +58,14 @@ public class Utils { widthHeight[WIDTH_INDEX] = size.x; widthHeight[HEIGHT_INDEX] = size.y; - if (!isScreenSizeRetrieved(widthHeight)) { + if(!isScreenSizeRetrieved(widthHeight)) { DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); widthHeight[0] = metrics.widthPixels; widthHeight[1] = metrics.heightPixels; } - // Last defense. Use deprecated API that was introduced in lower than API 13 - if (!isScreenSizeRetrieved(widthHeight)) { + if(!isScreenSizeRetrieved(widthHeight)) { widthHeight[0] = display.getWidth(); // deprecated widthHeight[1] = display.getHeight(); // deprecated } diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/FileUtils.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/FileUtils.java deleted file mode 100644 index c98e867..0000000 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/filepicker/utils/FileUtils.java +++ /dev/null @@ -1,72 +0,0 @@ -package de.beyondsoft.ownchat.filepicker.utils; - -import android.text.TextUtils; - -import java.io.File; - -import de.beyondsoft.ownchat.R; -import de.beyondsoft.ownchat.filepicker.FilePickerConst; - -/** - * Created by Lyndon on 26-Oct-2017. - */ - -public class FileUtils { - public static int getTypeDrawable(String path) { - return R.drawable.document; - } - - public static FilePickerConst.FILE_TYPE getFileType(String path) { - String fileExtension = Utils.getFileExtension(new File(path)); - if(TextUtils.isEmpty(fileExtension)) { - return FilePickerConst.FILE_TYPE.UNKNOWN; - } - - if(isExcelFile(path)) { - return FilePickerConst.FILE_TYPE.EXCEL; - } - - if(isDocFile(path)) { - return FilePickerConst.FILE_TYPE.WORD; - } - - if(isPPTFile(path)) { - return FilePickerConst.FILE_TYPE.PPT; - } - - if(isPDFFile(path)) { - return FilePickerConst.FILE_TYPE.PDF; - } - - if(isTxtFile(path)) { - return FilePickerConst.FILE_TYPE.TXT; - } else { - return FilePickerConst.FILE_TYPE.UNKNOWN; - } - } - - public static boolean isExcelFile(String path) { - String[] types = {"xls","xlsx"}; - return Utils.contains(types, path); - } - - public static boolean isDocFile(String path) { - String[] types = {"doc","docx", "dot","dotx"}; - return Utils.contains(types, path); - } - - public static boolean isPPTFile(String path) { - String[] types = {"ppt","pptx"}; - return Utils.contains(types, path); - } - - public static boolean isPDFFile(String path) { - String[] types = {"pdf"}; - return Utils.contains(types, path); - } - - public static boolean isTxtFile(String path) { - String[] types = {"txt"}; - return Utils.contains(types, path); - } -} diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java index a216216..cf34419 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java @@ -64,6 +64,8 @@ import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; +import android.widget.AbsListView; +import android.widget.AdapterView; import android.widget.GridLayout; import android.widget.ImageView; import android.widget.TextView; @@ -71,6 +73,7 @@ import android.widget.Toast; import java.io.File; import java.io.IOException; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Locale; import java.util.concurrent.TimeUnit; @@ -174,6 +177,7 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha private Rect mDialogContainer; private int mInitialLeft = 0; private int mInitialMarginLeft = 0; + private int mCurrentVisibleItemPosition = -1; @Inject @Named(AppPrefsConstants.CHAT_OPENED) @@ -193,12 +197,20 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha mSendMessageImageView.setEnabled(false); InjectionHelper.getChatComponent(this).inject(this); mBaseChatAdapter = getAdapter(); - mEmptyLayout.setOnTryAgainClickListener(v -> mCallback.fetchMoreMessagesByGroupId()); + + mEmptyLayout.setOnTryAgainClickListener(v -> { + mCallback.fetchMoreMessagesByGroupId(); + }); + setupRecyclerView(); EmojIconActions emojiIcon = new EmojIconActions(this, mCoordinatorLayout, mChatMessageEmojiEditText, mEmojiButton); emojiIcon.ShowEmojIcon(); + mChatMessageEmojiEditText.setOnClickListener(view -> { + mCurrentVisibleItemPosition = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); + }); + showLoading(); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(mKeyboardListener); @@ -218,10 +230,14 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha mEndlessScrollListener = new EndlessScrollListener(VISIBLE_THRESHOLD, linearLayoutManager) { @Override public boolean onLoad() { - if(mCallback == null || mCallback.isLoading()) { + if(mCallback == null || mCallback.isLoading() || mIsSearching || mIsJumpingToMessage) { return false; } + mIsJumpingToMessage = false; + + Log.e("SCROLLING_LOG", "Fetching messages in onLoad"); + mCallback.fetchMoreMessagesByGroupId(); return true; } @@ -253,15 +269,26 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha @Override public void messagesLoaded(ArrayList messages, boolean isFirstPage) { if(isFirstPage) { + Log.d("SCROLLING_LOG", "messagesLoaded with first page"); + mEndlessScrollListener.reset(); mBaseChatAdapter.setMessages(messages); mRecyclerView.scrollToPosition(0); + return; } + Log.d("SCROLLING_LOG", "messagesLoaded"); + mBaseChatAdapter.addMessages(messages); } + @Override + public void messagesLoaded(ArrayList messages) { + mEndlessScrollListener.reset(); + mBaseChatAdapter.setMessages(messages); + } + private void shareFile(boolean isFile) { mIsComingFromSharingActivity.set(false); @@ -336,7 +363,7 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha @Override public void hideMessagesLoading() { - isSearching = false; + mIsSearching = false; mBaseChatAdapter.setLoading(false); @@ -695,6 +722,35 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha mCallback.resendMessage(message, !TextUtils.isEmpty(message.filePath) ? getContentResolver().getType(Uri.parse(message.filePath)) : ""); } + private boolean mIsJumpingToMessage; + @Override + public void jumpToMessage(Message message) { + if(mChatSearchView.isIconified()) { + return; + } + + if(!mChatPresenter.isAttached()) { + mChatPresenter.attachView(this); + } + + Log.i("SCROLLING_LOG", "Jumping to message"); + mCallback.loadMessagesInContext(mGroup.id, message.createdAt, message); + } + + @Override + public void scrollToMessage(Message message) { + try { + final int position = mBaseChatAdapter.getPosition(message); + + mIsJumpingToMessage = true; + + Log.i("SCROLLING_LOG", "About to scroll to position " + position); + mRecyclerView.postDelayed(() -> mRecyclerView.smoothScrollToPosition(position), 350); + } catch(Exception e) { + Log.e("SCROLL_TO_MESSAGE", e.getMessage()); + } + } + @Override public MediaType getMediaType(Uri fileUri) { return MediaType.parse(getContentResolver().getType(fileUri)); @@ -777,6 +833,8 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha @Override public void addOneMessage(Message message) { + Log.i("SCROLLING_LOG", "Adding message '" + message.message + "'"); + mBaseChatAdapter.addMessage(message); if(mEndlessScrollListener.getVisibleItem() == 0) { @@ -812,32 +870,39 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha } } + static int mAppHeight; private ViewTreeObserver.OnGlobalLayoutListener mKeyboardListener = new ViewTreeObserver.OnGlobalLayoutListener() { - - private boolean mKeyboardShown = false; + private int mPreviousHeight; @Override public void onGlobalLayout() { - Rect r = new Rect(); - getWindow().getDecorView().getWindowVisibleDisplayFrame(r); - int screenHeight = getWindow().getDecorView().getHeight(); + int newHeight = mRecyclerView.getHeight(); - // r.bottom is the position above soft keypad or device button. - // if keypad is shown, the r.bottom is smaller than that before. - int keypadHeight = screenHeight - r.bottom; + if(mPreviousHeight == newHeight) { + return; + } - if(keypadHeight > screenHeight * 0.15 && !mKeyboardShown) { // 0.15 ratio is perhaps enough to determine keypad height. - // keyboard is opened - mRecyclerView.scrollToPosition(0); - mKeyboardShown = true; - } else { - mKeyboardShown = false; + mPreviousHeight = newHeight; + + if(newHeight >= mAppHeight) { + mAppHeight = newHeight; + } + + if(newHeight != 0) { + if(mAppHeight > newHeight && mCurrentVisibleItemPosition <= 0) { + mRecyclerView.scrollToPosition(0); + } } } }; @Override public void onBackPressed() { + if(!mChatSearchView.isIconified()) { + closeSearchView(); + return; + } + mIsComingFromSharingActivity.set(false); mSelectedFilePath.delete(); @@ -866,20 +931,7 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha return false; } - public boolean isSearching = false; - - private String mSearchText; - - @Override - public String getSearchText() { - return mSearchText; - } - - @Override - public void resetSearchText() { - mSearchText = null; - isSearching = false; - } + public boolean mIsSearching = false; @Override public void showEmptySearchResultMessage(String text) { @@ -888,12 +940,12 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha mEmptyLayout.setEmptyText(emptyText); mEmptyLayout.showEmpty(); - isSearching = false; + mIsSearching = false; } @Override public boolean onQueryTextChange(String newText) { - if(isSearching && !TextUtils.isEmpty(newText)) { + if(mIsSearching && !TextUtils.isEmpty(newText)) { return false; } @@ -906,14 +958,13 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha mCallback.resetMessages(); } - isSearching = true; + mIsSearching = true; if(TextUtils.isEmpty(newText)) { loadMessages(); } else { - mSearchText = newText; + mBaseChatAdapter.setMessages(new ArrayList<>()); - messagesLoaded(new ArrayList<>(), true); loadSearchResult(newText); } }, 300); @@ -927,4 +978,6 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha mChatSearchView.setIconified(false); } } + + public abstract void closeSearchView(); } \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatAdapter.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatAdapter.java index 1c5da1b..442862c 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatAdapter.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatAdapter.java @@ -9,6 +9,7 @@ import android.util.TypedValue; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; +import android.widget.RelativeLayout; import android.widget.TextView; import com.bumptech.glide.load.engine.DiskCacheStrategy; @@ -41,10 +42,11 @@ abstract class BaseChatAdapter extends RecyclerView.Adapter mMessages = new ArrayList<>(); + final ArrayList mMessages = new ArrayList<>(); private final List mDatePositions = new ArrayList<>(); - final List mDateList = new ArrayList<>(); - final List mPositionOffset = new ArrayList<>(); + final List mDateList = new ArrayList<>(); + final List mPositionOffset = new ArrayList<>(); + private boolean mIsLoading; private int mLastOffset = 0; private Calendar mCurrentDay; @@ -64,6 +66,7 @@ abstract class BaseChatAdapter extends RecyclerView.Adapter= mMessages.size()) { + position = 0; + } + + Message messageToJumpTo = mMessages.get(position); + + mMessageClickListener.jumpToMessage(messageToJumpTo); + } + @Optional @OnClick(R.id.chat_item_sending_failed) void retrySendingClicked() { @@ -471,5 +518,7 @@ abstract class BaseChatAdapter extends RecyclerView.Adapter implements Cha private void loadMessagesFromDBByGroupId() { MessageByGroupIdSpecification messageByGroupIdSpecification = new MessageByGroupIdSpecification(mUserIdPreference.get()); messageByGroupIdSpecification.setGroupId(mGroupId); + mMessageRealmRepository.queryForAll(messageByGroupIdSpecification) .map(ArrayList::new) .compose(RxUtils.provideDefaultTransformer()) .subscribe(this::loadMessagesFromDBSuccessful, this::loadMessagesError); } + private String mText; public void searchMessagesFromDBByGroupIdAndText(long groupId, String text) { MessagesByGroupAndSearchTextSpecification messagesByGroupAndSearchTextSpecification = new MessagesByGroupAndSearchTextSpecification(mUserIdPreference.get()); messagesByGroupAndSearchTextSpecification.setGroupId(groupId); messagesByGroupAndSearchTextSpecification.setSearchText(text); + mText = text; + mMessageRealmRepository.queryForAll(messagesByGroupAndSearchTextSpecification) .map(ArrayList::new) .compose(RxUtils.provideDefaultTransformer()) @@ -332,14 +336,9 @@ public class ChatPresenter extends MVPAbstractPresenter implements Cha getView().hideLoading(); getView().hideMessagesLoading(); - String searchText = getView().getSearchText(); - - getView().resetSearchText(); - if(!mMessages.isEmpty()) { int endOffset = mOffset + LIMIT > mMessages.size() ? mMessages.size() : mOffset + LIMIT; if(mOffset > endOffset) { - Log.i("", ""); return; } @@ -373,7 +372,92 @@ public class ChatPresenter extends MVPAbstractPresenter implements Cha return; } - getView().showEmptySearchResultMessage(searchText); + getView().showEmptySearchResultMessage(mText); + } + + private Message mMessageToScrollTo; + public void loadMessagesInContext(long groupId, long createdAt, Message messageToScrollTo) { + MessageByGroupIdSpecification messageByGroupIdSpecification = new MessageByGroupIdSpecification(mUserIdPreference.get()); + messageByGroupIdSpecification.setGroupId(groupId); + + mMessageToScrollTo = messageToScrollTo; + + mMessageRealmRepository.queryForAll(messageByGroupIdSpecification) + .map(ArrayList::new) + .compose(RxUtils.provideDefaultTransformer()) + .subscribe(this::loadMessagesInContextSuccessful, this::loadMessagesError); + } + + private void loadMessagesInContextSuccessful(ArrayList messages) { + if(!isAttached()) { + return; + } + + Log.w("SCROLLING_LOG" ,"Count of messages loaded: " + messages.size()); + + mMessages.clear(); + mMessages.addAll(messages); + mLoading = false; + + getView().hideLoading(); + getView().hideMessagesLoading(); + + if(!mMessages.isEmpty()) { + int tempLimit = (LIMIT - 1) / 2; + + int position = 0; + mOffset = 0; + for(Message message : mMessages) { + if(message.equals(mMessageToScrollTo)) { + position = mMessages.indexOf(message); + } + } + + mOffset = position < tempLimit ? 0 : position - tempLimit; + + int endOffset = mOffset + LIMIT > mMessages.size() ? mMessages.size() : mOffset + LIMIT; + + if(mOffset > endOffset) { + return; + } + + double pos = (double) position; + double lim = (double) LIMIT; + + mPage = (int) Math.ceil(pos / lim); + boolean isFirstPage = mPage == FIRST_PAGE; + + getView().messagesLoaded(new ArrayList<>(mMessages.subList(mOffset, endOffset))); + + if(isFirstPage && mSystemUtils.isNetworkUnavailable()) { + Message firstMessage = mMessages.get(0); + Message message = new Message(firstMessage.id, + firstMessage.groupId, + firstMessage.createdAt / 1000, + firstMessage.message, + firstMessage.filePath, + firstMessage.smallerImage, + firstMessage.originalFilename, + firstMessage.uri, + firstMessage.senderId, + firstMessage.senderName, + firstMessage.senderAvatar, + firstMessage.status, + true, + firstMessage.userId, + firstMessage.fileDownloaded); + storeGroupLocally(mGroupId, message); + } + + //mPage++; + mOffset += LIMIT; + + getView().scrollToMessage(mMessageToScrollTo); + + return; + } + + getView().showEmptyMessage(); } /** @@ -393,6 +477,9 @@ public class ChatPresenter extends MVPAbstractPresenter implements Cha if(!mMessages.isEmpty()) { int endOffset = mOffset + LIMIT > mMessages.size() ? mMessages.size() : mOffset + LIMIT; + + LogFetchingInformation(mOffset, endOffset, LIMIT, mMessages.size()); + if(mOffset > endOffset) { return; } @@ -447,6 +534,7 @@ public class ChatPresenter extends MVPAbstractPresenter implements Cha public void fetchMoreMessagesByGroupId() { if(mMessages.size() < 25) { getView().hideMessagesLoading(); + return; } @@ -461,6 +549,7 @@ public class ChatPresenter extends MVPAbstractPresenter implements Cha if(mSystemUtils.isNetworkUnavailable()) { loadMessagesFromDBByGroupId(); getView().showError(R.string.error_no_internet_connection); + return; } @@ -1160,4 +1249,20 @@ public class ChatPresenter extends MVPAbstractPresenter implements Cha private static String generateRandomId() { return "_" + new Random().nextInt(); } + + private static void LogFetchingInformation(int offset, int endOffset, int limit, int messageCollectionSize) { + Log.i("FETCHING_INFORMATION", "\n-" + + "\nOffset: " + offset + + "\nEnd offset: " + endOffset + + "\nNumber of messages: " + messageCollectionSize + + "\nLimit: " + limit + + "\noffset > end offset: " + (offset > endOffset) + ); + } + + private static void LogMessageCollection(List messages) { + for(int i = 0; i < messages.size(); i++) { + Log.d("MESSAGES_LOG", String.format("%03d", i) + " " + messages.get(i).id + ": " + (messages.get(i).message == null ? messages.get(i).originalFilename : messages.get(i).message)); + } + } } \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/ChatView.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/ChatView.java index 942bf71..55e06a0 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/ChatView.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/ChatView.java @@ -12,15 +12,14 @@ import java.util.ArrayList; import okhttp3.MediaType; -/** - * Created by Rares Teodorescu on 21/02/2017. - */ interface ChatView extends MVPView { void setCallback(Callback callback); void messagesLoaded(ArrayList messages, boolean isFirstPage); + void messagesLoaded(ArrayList messages); + void showLoading(); void hideLoading(); @@ -57,11 +56,10 @@ interface ChatView extends MVPView { void showEmptySearchResultMessage(String searchText); - String getSearchText(); - - void resetSearchText(); + void scrollToMessage(Message message); interface Callback { + void loadMessagesInContext(long groupId, long createdAt, Message messageToExclude); void loadMessagesFromServerByGroupId(long groupId); diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/GroupChatActivity.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/GroupChatActivity.java index 17920fc..014c7d8 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/GroupChatActivity.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/GroupChatActivity.java @@ -195,4 +195,11 @@ public class GroupChatActivity extends BaseChatActivity { cancelNotification(); } + + public void closeSearchView() { + mGroupMembersTextView.setVisibility(View.VISIBLE); + mGroupTitleTextView.setVisibility(View.VISIBLE); + mChatImageView.setVisibility(View.VISIBLE); + mChatSearchView.setIconified(true); + } } \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatActivity.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatActivity.java index e03f79e..f1a7ae7 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatActivity.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatActivity.java @@ -16,10 +16,8 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.text.TextUtils; -import android.util.Log; import android.view.View; import android.view.ViewGroup; -import android.widget.EditText; import com.bumptech.glide.load.engine.DiskCacheStrategy; @@ -201,4 +199,10 @@ public class SingleChatActivity extends BaseChatActivity { } } }; + + public void closeSearchView() { + mSingleChatTitleTextView.setVisibility(View.VISIBLE); + mChatImageView.setVisibility(View.VISIBLE); + mChatSearchView.setIconified(true); + } } \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatAdapter.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatAdapter.java index 2a76d9b..d92f726 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatAdapter.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/SingleChatAdapter.java @@ -5,6 +5,7 @@ import de.beyondsoft.ownchat.model.Message; import de.beyondsoft.ownchat.utils.DateUtils; import de.beyondsoft.ownchat.utils.ui.ViewUtils; +import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.Gravity; import android.view.LayoutInflater; @@ -19,8 +20,9 @@ class SingleChatAdapter extends BaseChatAdapter { super(userId); } + @NonNull @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if(viewType == LEFT_CHAT_TYPE) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_single_chat_left, parent, false); @@ -49,7 +51,7 @@ class SingleChatAdapter extends BaseChatAdapter { @SuppressWarnings("ConstantConditions") @Override - public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { int viewType = getItemViewType(position); switch(viewType) { case LOADING_TYPE: @@ -91,5 +93,4 @@ class SingleChatAdapter extends BaseChatAdapter { break; } } - } diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/EndlessScrollListener.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/EndlessScrollListener.java index e28d105..b116b4e 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/EndlessScrollListener.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/EndlessScrollListener.java @@ -2,11 +2,12 @@ package de.beyondsoft.ownchat.utils; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; public abstract class EndlessScrollListener extends RecyclerView.OnScrollListener { private int mPreviousTotal = 0; - private int mVisibleThreshold = 5; + private int mVisibleThreshold; private boolean mLoading = false; private final LinearLayoutManager mLayoutManager; private int mVisibleItem; @@ -37,6 +38,8 @@ public abstract class EndlessScrollListener extends RecyclerView.OnScrollListene return; } + Log.e("SCROLLING_LOG", "Scrolling " + (dy < 0 ? "up" : "down")); + recyclerView.post(() -> { if(!mLoading && (totalItemCount - visibleItemCount) <= (mVisibleItem + mVisibleThreshold)) { mLoading = onLoad(); diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/ui/ViewUtils.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/ui/ViewUtils.java index 1af8a0b..c333912 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/ui/ViewUtils.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/ui/ViewUtils.java @@ -1,10 +1,7 @@ package de.beyondsoft.ownchat.utils.ui; -import de.beyondsoft.ownchat.R; - import android.app.Activity; import android.content.Context; -import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.Rect; import android.os.Build; @@ -13,12 +10,8 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.ProgressBar; +import de.beyondsoft.ownchat.R; -/** - * Helper methods for view related classes - *

- * Created by BoldijarPaul on 03/08/16. - */ public abstract class ViewUtils { private ViewUtils() { diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/item_group_chat_left.xml b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/item_group_chat_left.xml index 563bc21..17c5474 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/item_group_chat_left.xml +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/item_group_chat_left.xml @@ -21,13 +21,13 @@ tools:src="@drawable/avatar" /> + android:padding="7dp"> + tools:visibility="visible" /> + tools:visibility="visible" /> + android:layout_toEndOf="@id/chat_item_contact_image" + android:padding="7dp"> + tools:visibility="visible" /> + android:layout_height="wrap_content" + android:padding="7dp"> + tools:visibility="visible" /> + tools:src="@drawable/delivered" + tools:visibility="visible" /> + tools:visibility="visible" /> @color/colorBlack @color/colorBlack + @color/colorBlack