Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AloqaIM-Android
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
AloqaIM-Android
Commits
7f855b85
Commit
7f855b85
authored
Jan 17, 2017
by
Yusuke Iwaki
Committed by
GitHub
Jan 17, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #151 from RocketChat/auto_scroll
auto scroll to latest message
parents
2bec0df7
55a924a5
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
262 additions
and
7 deletions
+262
-7
RoomFragment.java
...a/chat/rocket/android/fragment/chatroom/RoomFragment.java
+90
-4
RecyclerViewAutoScrollManager.java
.../rocket/android/helper/RecyclerViewAutoScrollManager.java
+31
-0
RecyclerViewScrolledToBottomListener.java
.../android/helper/RecyclerViewScrolledToBottomListener.java
+67
-0
ExtRealmModelListAdapter.java
...rocket/android/layouthelper/ExtRealmModelListAdapter.java
+2
-2
AbstractNewMessageIndicatorManager.java
...uthelper/chatroom/AbstractNewMessageIndicatorManager.java
+43
-0
Message.java
app/src/main/java/chat/rocket/android/model/ddp/Message.java
+1
-0
strings.xml
app/src/main/res/values/strings.xml
+3
-0
RealmModelListAdapter.java
...at/rocket/android/realm_helper/RealmModelListAdapter.java
+25
-1
No files found.
app/src/main/java/chat/rocket/android/fragment/chatroom/RoomFragment.java
View file @
7f855b85
...
...
@@ -6,6 +6,7 @@ import android.content.Intent;
import
android.os.Bundle
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.design.widget.Snackbar
;
import
android.support.v4.view.GravityCompat
;
import
android.support.v4.widget.DrawerLayout
;
import
android.support.v4.widget.SlidingPaneLayout
;
...
...
@@ -27,16 +28,19 @@ import chat.rocket.android.helper.FileUploadHelper;
import
chat.rocket.android.helper.LoadMoreScrollListener
;
import
chat.rocket.android.helper.LogcatIfError
;
import
chat.rocket.android.helper.OnBackPressListener
;
import
chat.rocket.android.helper.RecyclerViewAutoScrollManager
;
import
chat.rocket.android.helper.RecyclerViewScrolledToBottomListener
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.layouthelper.chatroom.MessageFormManager
;
import
chat.rocket.android.layouthelper.chatroom.MessageListAdapter
;
import
chat.rocket.android.layouthelper.chatroom.AbstractNewMessageIndicatorManager
;
import
chat.rocket.android.layouthelper.chatroom.PairedMessage
;
import
chat.rocket.android.layouthelper.extra_action.MessageExtraActionBehavior
;
import
chat.rocket.android.log.RCLog
;
import
chat.rocket.android.layouthelper.extra_action.upload.AudioUploadActionItem
;
import
chat.rocket.android.layouthelper.extra_action.upload.AbstractUploadActionItem
;
import
chat.rocket.android.layouthelper.extra_action.upload.AudioUploadActionItem
;
import
chat.rocket.android.layouthelper.extra_action.upload.ImageUploadActionItem
;
import
chat.rocket.android.layouthelper.extra_action.upload.VideoUploadActionItem
;
import
chat.rocket.android.log.RCLog
;
import
chat.rocket.android.model.ServerConfig
;
import
chat.rocket.android.model.SyncState
;
import
chat.rocket.android.model.ddp.Message
;
...
...
@@ -70,6 +74,10 @@ public class RoomFragment extends AbstractChatRoomFragment
private
LoadMoreScrollListener
scrollListener
;
private
RealmObjectObserver
<
LoadMessageProcedure
>
procedureObserver
;
private
MessageFormManager
messageFormManager
;
private
RecyclerViewAutoScrollManager
autoScrollManager
;
private
AbstractNewMessageIndicatorManager
newMessageIndicatorManager
;
private
Snackbar
unreadIndicator
;
private
boolean
previousUnreadMessageExists
;
public
RoomFragment
()
{
}
...
...
@@ -145,6 +153,15 @@ public class RoomFragment extends AbstractChatRoomFragment
LinearLayoutManager
layoutManager
=
new
LinearLayoutManager
(
getContext
(),
LinearLayoutManager
.
VERTICAL
,
true
);
listView
.
setLayoutManager
(
layoutManager
);
autoScrollManager
=
new
RecyclerViewAutoScrollManager
(
layoutManager
)
{
@Override
protected
void
onAutoScrollMissed
()
{
if
(
newMessageIndicatorManager
!=
null
)
{
newMessageIndicatorManager
.
updateNewMessageCount
(
getUnreadMessageCount
());
}
}
};
adapter
.
registerAdapterDataObserver
(
autoScrollManager
);
scrollListener
=
new
LoadMoreScrollListener
(
layoutManager
,
40
)
{
@Override
...
...
@@ -153,11 +170,69 @@ public class RoomFragment extends AbstractChatRoomFragment
}
};
listView
.
addOnScrollListener
(
scrollListener
);
listView
.
addOnScrollListener
(
new
RecyclerViewScrolledToBottomListener
(
layoutManager
,
1
,
this
::
markAsReadIfNeeded
));
newMessageIndicatorManager
=
new
AbstractNewMessageIndicatorManager
()
{
@Override
protected
void
onShowIndicator
(
int
count
,
boolean
onlyAlreadyShown
)
{
if
((
onlyAlreadyShown
&&
unreadIndicator
!=
null
&&
unreadIndicator
.
isShown
())
||
!
onlyAlreadyShown
)
{
unreadIndicator
=
getUnreadCountIndicatorView
(
count
);
unreadIndicator
.
show
();
}
}
@Override
protected
void
onHideIndicator
()
{
if
(
unreadIndicator
!=
null
&&
unreadIndicator
.
isShown
())
{
unreadIndicator
.
dismiss
();
}
}
};
setupSideMenu
();
setupMessageComposer
();
}
private
void
scrollToLatestMessage
()
{
RecyclerView
listView
=
(
RecyclerView
)
rootView
.
findViewById
(
R
.
id
.
recyclerview
);
if
(
listView
!=
null
)
{
listView
.
scrollToPosition
(
0
);
}
}
private
Snackbar
getUnreadCountIndicatorView
(
int
count
)
{
// TODO: replace with another custom View widget, not to hide message composer.
final
String
caption
=
getResources
().
getString
(
R
.
string
.
fmt_dialog_view_latest_message_title
,
count
);
return
Snackbar
.
make
(
rootView
,
caption
,
Snackbar
.
LENGTH_LONG
)
.
setAction
(
R
.
string
.
dialog_view_latest_message_action
,
view
->
scrollToLatestMessage
());
}
private
int
getUnreadMessageCount
()
{
RoomSubscription
room
=
realmHelper
.
executeTransactionForRead
(
realm
->
realm
.
where
(
RoomSubscription
.
class
).
equalTo
(
RoomSubscription
.
ROOM_ID
,
roomId
).
findFirst
());
if
(
room
!=
null
)
{
return
realmHelper
.
executeTransactionForReadResults
(
realm
->
realm
.
where
(
Message
.
class
)
.
equalTo
(
Message
.
ROOM_ID
,
roomId
)
.
greaterThanOrEqualTo
(
Message
.
TIMESTAMP
,
room
.
getLastSeen
())
.
notEqualTo
(
Message
.
USER_ID
,
userId
)
.
findAll
()).
size
();
}
else
{
return
0
;
}
}
@Override
public
void
onDestroyView
()
{
RecyclerView
listView
=
(
RecyclerView
)
rootView
.
findViewById
(
R
.
id
.
recyclerview
);
listView
.
getAdapter
().
unregisterAdapterDataObserver
(
autoScrollManager
);
super
.
onDestroyView
();
}
@Override
public
void
onItemClick
(
PairedMessage
pairedMessage
)
{
if
(
pairedMessage
.
target
!=
null
)
{
...
...
@@ -230,7 +305,13 @@ public class RoomFragment extends AbstractChatRoomFragment
.
put
(
Message
.
SYNC_STATE
,
SyncState
.
NOT_SYNCED
)
.
put
(
Message
.
TIMESTAMP
,
System
.
currentTimeMillis
())
.
put
(
Message
.
ROOM_ID
,
roomId
)
.
put
(
Message
.
MESSAGE
,
messageText
))));
.
put
(
Message
.
USER
,
new
JSONObject
()
.
put
(
User
.
ID
,
userId
))
.
put
(
Message
.
MESSAGE
,
messageText
)))
.
onSuccess
(
_task
->
{
scrollToLatestMessage
();
return
null
;
}));
messageFormManager
.
registerExtraActionItem
(
new
ImageUploadActionItem
());
messageFormManager
.
registerExtraActionItem
(
new
AudioUploadActionItem
());
messageFormManager
.
registerExtraActionItem
(
new
VideoUploadActionItem
());
...
...
@@ -275,6 +356,12 @@ public class RoomFragment extends AbstractChatRoomFragment
setToolbarRoomIcon
(
0
);
}
setToolbarTitle
(
roomSubscription
.
getName
());
boolean
unreadMessageExists
=
roomSubscription
.
isAlert
();
if
(
newMessageIndicatorManager
!=
null
&&
previousUnreadMessageExists
&&
!
unreadMessageExists
)
{
newMessageIndicatorManager
.
reset
();
}
previousUnreadMessageExists
=
unreadMessageExists
;
}
private
void
onUpdateLoadMessageProcedure
(
LoadMessageProcedure
procedure
)
{
...
...
@@ -346,7 +433,6 @@ public class RoomFragment extends AbstractChatRoomFragment
roomObserver
.
sub
();
procedureObserver
.
sub
();
closeSideMenuIfNeeded
();
markAsReadIfNeeded
();
}
@Override
...
...
app/src/main/java/chat/rocket/android/helper/RecyclerViewAutoScrollManager.java
0 → 100644
View file @
7f855b85
package
chat
.
rocket
.
android
.
helper
;
import
android.support.v7.widget.LinearLayoutManager
;
import
android.support.v7.widget.RecyclerView
;
/**
* workaround for bug https://code.google.com/p/android/issues/detail?id=174227
*/
public
class
RecyclerViewAutoScrollManager
extends
RecyclerView
.
AdapterDataObserver
{
private
final
LinearLayoutManager
linearLayoutManager
;
public
RecyclerViewAutoScrollManager
(
LinearLayoutManager
linearLayoutManager
)
{
this
.
linearLayoutManager
=
linearLayoutManager
;
}
@Override
public
void
onItemRangeInserted
(
int
positionStart
,
int
itemCount
)
{
super
.
onItemRangeInserted
(
positionStart
,
itemCount
);
if
(
linearLayoutManager
.
findFirstVisibleItemPosition
()
<=
positionStart
)
{
linearLayoutManager
.
scrollToPosition
(
positionStart
);
}
else
{
onAutoScrollMissed
();
}
}
protected
void
onAutoScrollMissed
()
{
//do nothing by default.
}
}
app/src/main/java/chat/rocket/android/helper/RecyclerViewScrolledToBottomListener.java
0 → 100644
View file @
7f855b85
package
chat
.
rocket
.
android
.
helper
;
import
android.os.Handler
;
import
android.support.v7.widget.LinearLayoutManager
;
import
android.support.v7.widget.RecyclerView
;
/**
* ScrollListener for detecting scrolled on the bottom of the RecyclerView.
*/
public
class
RecyclerViewScrolledToBottomListener
extends
RecyclerView
.
OnScrollListener
{
/**
* callback.
*/
public
interface
Callback
{
void
onScrolledToBottom
();
}
private
final
LinearLayoutManager
layoutManager
;
private
final
int
thresholdPosition
;
private
final
Handler
handler
;
private
final
Callback
callback
;
/**
* Trigger callback if the bottom item position > thresholdPosition.
*/
public
RecyclerViewScrolledToBottomListener
(
LinearLayoutManager
layoutManager
,
int
thresholdPosition
,
Callback
callback
)
{
this
.
layoutManager
=
layoutManager
;
this
.
thresholdPosition
=
thresholdPosition
;
this
.
callback
=
callback
;
this
.
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
android
.
os
.
Message
msg
)
{
onScrollEnd
();
}
};
}
@Override
public
void
onScrolled
(
RecyclerView
recyclerView
,
int
deltaX
,
int
deltaY
)
{
super
.
onScrolled
(
recyclerView
,
deltaX
,
deltaY
);
handler
.
removeMessages
(
0
);
handler
.
sendEmptyMessageDelayed
(
0
,
120
);
}
private
void
onScrollEnd
()
{
if
(
layoutManager
.
getReverseLayout
())
{
if
(
layoutManager
.
findFirstVisibleItemPosition
()
<=
thresholdPosition
)
{
doCallback
();
}
}
else
{
if
(
layoutManager
.
findLastVisibleItemPosition
()
>=
thresholdPosition
)
{
doCallback
();
}
}
}
private
void
doCallback
()
{
if
(
callback
!=
null
)
{
callback
.
onScrolledToBottom
();
}
}
}
app/src/main/java/chat/rocket/android/layouthelper/ExtRealmModelListAdapter.java
View file @
7f855b85
...
...
@@ -38,7 +38,7 @@ public abstract class ExtRealmModelListAdapter<T extends RealmObject, VM,
notifyItemChanged
(
position
+
1
);
}
pr
otected
ListUpdateCallback
listUpdateCallback
=
new
ListUpdateCallback
()
{
pr
ivate
final
ListUpdateCallback
listUpdateCallback
=
new
ListUpdateCallback
()
{
@Override
public
void
onInserted
(
int
position
,
int
count
)
{
notifyItemRangeInserted
(
position
+
1
,
count
);
...
...
@@ -108,7 +108,7 @@ public abstract class ExtRealmModelListAdapter<T extends RealmObject, VM,
}
@Override
p
ublic
ListUpdateCallback
getListUpdateCallback
()
{
p
rotected
ListUpdateCallback
getListUpdateCallback
()
{
return
listUpdateCallback
;
}
}
app/src/main/java/chat/rocket/android/layouthelper/chatroom/AbstractNewMessageIndicatorManager.java
0 → 100644
View file @
7f855b85
package
chat
.
rocket
.
android
.
layouthelper
.
chatroom
;
/**
* manager class for showing "You have XX messages" indicator.
*/
public
abstract
class
AbstractNewMessageIndicatorManager
{
private
int
count
;
private
boolean
onlyAlreadyShown
;
/**
* update the number of unread message.
*/
public
void
updateNewMessageCount
(
int
count
)
{
if
(
count
>
0
)
{
this
.
count
=
count
;
update
();
onlyAlreadyShown
=
true
;
}
else
{
reset
();
}
}
/**
* Should call this method when user checked new message.
*/
public
void
reset
()
{
count
=
0
;
onlyAlreadyShown
=
false
;
update
();
}
private
void
update
()
{
if
(
count
>
0
)
{
onShowIndicator
(
count
,
onlyAlreadyShown
);
}
else
{
onHideIndicator
();
}
}
protected
abstract
void
onShowIndicator
(
int
count
,
boolean
onlyAlreadyShown
);
protected
abstract
void
onHideIndicator
();
}
app/src/main/java/chat/rocket/android/model/ddp/Message.java
View file @
7f855b85
...
...
@@ -25,6 +25,7 @@ public class Message extends RealmObject {
@SuppressWarnings
({
"PMD.AvoidFieldNameMatchingTypeName"
})
public
static
final
String
MESSAGE
=
"msg"
;
public
static
final
String
USER
=
"u"
;
public
static
final
String
USER_ID
=
"u._id"
;
public
static
final
String
GROUPABLE
=
"groupable"
;
public
static
final
String
ATTACHMENTS
=
"attachments"
;
public
static
final
String
URLS
=
"urls"
;
...
...
app/src/main/res/values/strings.xml
View file @
7f855b85
...
...
@@ -18,6 +18,9 @@
<string
name=
"resend"
>
Resend
</string>
<string
name=
"discard"
>
Discard
</string>
<string
name=
"fmt_dialog_view_latest_message_title"
>
New %d messages
</string>
<string
name=
"dialog_view_latest_message_action"
>
View
</string>
<string
name=
"file_uploading_title"
>
Uploading…
</string>
<string
name=
"dialog_user_registration_email"
>
Email
</string>
...
...
realm-helpers/src/main/java/chat/rocket/android/realm_helper/RealmModelListAdapter.java
View file @
7f855b85
...
...
@@ -103,7 +103,31 @@ public abstract class RealmModelListAdapter<T extends RealmObject, VM,
protected
abstract
DiffUtil
.
Callback
getDiffCallback
(
List
<
VM
>
oldData
,
List
<
VM
>
newData
);
protected
abstract
ListUpdateCallback
getListUpdateCallback
();
private
final
ListUpdateCallback
listUpdateCallback
=
new
ListUpdateCallback
()
{
@Override
public
void
onInserted
(
int
position
,
int
count
)
{
notifyItemRangeInserted
(
position
,
count
);
}
@Override
public
void
onRemoved
(
int
position
,
int
count
)
{
notifyItemRangeRemoved
(
position
,
count
);
}
@Override
public
void
onMoved
(
int
fromPosition
,
int
toPosition
)
{
notifyItemMoved
(
fromPosition
,
toPosition
);
}
@Override
public
void
onChanged
(
int
position
,
int
count
,
Object
payload
)
{
notifyItemRangeChanged
(
position
,
count
,
payload
);
}
};
protected
ListUpdateCallback
getListUpdateCallback
()
{
return
listUpdateCallback
;
}
public
void
setOnItemClickListener
(
OnItemClickListener
<
VM
>
onItemClickListener
)
{
this
.
onItemClickListener
=
onItemClickListener
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment