kopia lustrzana https://github.com/ryukoposting/Signal-Android
Allow user to launch directly to a specific story, fix story chronology.
rodzic
8bb27b60fa
commit
63e48efdfe
|
@ -183,7 +183,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||||
|
|
||||||
public abstract boolean isStory(long messageId);
|
public abstract boolean isStory(long messageId);
|
||||||
public abstract @NonNull Reader getOutgoingStoriesTo(@NonNull RecipientId recipientId);
|
public abstract @NonNull Reader getOutgoingStoriesTo(@NonNull RecipientId recipientId);
|
||||||
public abstract @NonNull Reader getAllOutgoingStories();
|
public abstract @NonNull Reader getAllOutgoingStories(boolean reverse);
|
||||||
public abstract @NonNull Reader getAllStories();
|
public abstract @NonNull Reader getAllStories();
|
||||||
public abstract @NonNull List<RecipientId> getAllStoriesRecipientsList();
|
public abstract @NonNull List<RecipientId> getAllStoriesRecipientsList();
|
||||||
public abstract @NonNull Reader getAllStoriesFor(@NonNull RecipientId recipientId);
|
public abstract @NonNull Reader getAllStoriesFor(@NonNull RecipientId recipientId);
|
||||||
|
|
|
@ -571,10 +571,10 @@ public class MmsDatabase extends MessageDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull MessageDatabase.Reader getAllOutgoingStories() {
|
public @NonNull MessageDatabase.Reader getAllOutgoingStories(boolean reverse) {
|
||||||
String where = IS_STORY_CLAUSE + " AND (" + getOutgoingTypeClause() + ")";
|
String where = IS_STORY_CLAUSE + " AND (" + getOutgoingTypeClause() + ")";
|
||||||
|
|
||||||
return new Reader(rawQuery(where, null, true, -1L));
|
return new Reader(rawQuery(where, null, reverse, -1L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -587,7 +587,7 @@ public class MmsDatabase extends MessageDatabase {
|
||||||
long threadId = SignalDatabase.threads().getThreadIdIfExistsFor(recipientId);
|
long threadId = SignalDatabase.threads().getThreadIdIfExistsFor(recipientId);
|
||||||
String where = IS_STORY_CLAUSE + " AND " + THREAD_ID_WHERE;
|
String where = IS_STORY_CLAUSE + " AND " + THREAD_ID_WHERE;
|
||||||
String[] whereArgs = SqlUtil.buildArgs(threadId);
|
String[] whereArgs = SqlUtil.buildArgs(threadId);
|
||||||
Cursor cursor = rawQuery(where, whereArgs, true, -1L);
|
Cursor cursor = rawQuery(where, whereArgs, false, -1L);
|
||||||
|
|
||||||
return new Reader(cursor);
|
return new Reader(cursor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull MessageDatabase.Reader getAllOutgoingStories() {
|
public @NonNull MessageDatabase.Reader getAllOutgoingStories(boolean reverse) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,8 +61,13 @@ class MyStoriesFragment : DSLSettingsFragment(
|
||||||
lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe()
|
lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe()
|
||||||
Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show()
|
||||||
} else {
|
} else {
|
||||||
// TODO [stories] pass in something more specific to start with the correct progress
|
val recipientId = if (it.distributionStory.messageRecord.recipient.isGroup) {
|
||||||
startActivity(StoryViewerActivity.createIntent(requireContext(), Recipient.self().id))
|
it.distributionStory.messageRecord.recipient.id
|
||||||
|
} else {
|
||||||
|
Recipient.self().id
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivity(StoryViewerActivity.createIntent(requireContext(), recipientId, conversationMessage.messageRecord.id))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSaveClick = {
|
onSaveClick = {
|
||||||
|
|
|
@ -82,6 +82,7 @@ object MyStoriesItem {
|
||||||
private val errorIndicator: View = itemView.findViewById(R.id.error_indicator)
|
private val errorIndicator: View = itemView.findViewById(R.id.error_indicator)
|
||||||
|
|
||||||
override fun bind(model: Model) {
|
override fun bind(model: Model) {
|
||||||
|
storyPreview.isClickable = false
|
||||||
itemView.setOnClickListener { model.onClick(model) }
|
itemView.setOnClickListener { model.onClick(model) }
|
||||||
downloadTarget.setOnClickListener { model.onSaveClick(model) }
|
downloadTarget.setOnClickListener { model.onSaveClick(model) }
|
||||||
moreTarget.setOnClickListener { showContextMenu(model) }
|
moreTarget.setOnClickListener { showContextMenu(model) }
|
||||||
|
|
|
@ -26,7 +26,7 @@ class MyStoriesRepository(context: Context) {
|
||||||
return Observable.create { emitter ->
|
return Observable.create { emitter ->
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
val storiesMap = mutableMapOf<Recipient, List<MessageRecord>>()
|
val storiesMap = mutableMapOf<Recipient, List<MessageRecord>>()
|
||||||
SignalDatabase.mms.allOutgoingStories.use {
|
SignalDatabase.mms.getAllOutgoingStories(true).use {
|
||||||
while (it.next != null) {
|
while (it.next != null) {
|
||||||
val messageRecord = it.current
|
val messageRecord = it.current
|
||||||
val currentList = storiesMap[messageRecord.recipient] ?: emptyList()
|
val currentList = storiesMap[messageRecord.recipient] ?: emptyList()
|
||||||
|
|
|
@ -21,17 +21,22 @@ class StoryViewerActivity : PassphraseRequiredActivity() {
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.fragment_container, StoryViewerFragment.create(intent.getParcelableExtra(ARG_START_RECIPIENT_ID)!!))
|
.replace(R.id.fragment_container, StoryViewerFragment.create(
|
||||||
|
intent.getParcelableExtra(ARG_START_RECIPIENT_ID)!!,
|
||||||
|
intent.getLongExtra(ARG_START_STORY_ID, -1L)
|
||||||
|
))
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG_START_RECIPIENT_ID = "start.recipient.id"
|
private const val ARG_START_RECIPIENT_ID = "start.recipient.id"
|
||||||
|
private const val ARG_START_STORY_ID = "start.story.id"
|
||||||
|
|
||||||
fun createIntent(context: Context, storyId: RecipientId): Intent {
|
fun createIntent(context: Context, recipientId: RecipientId, storyId: Long = -1L): Intent {
|
||||||
return Intent(context, StoryViewerActivity::class.java)
|
return Intent(context, StoryViewerActivity::class.java)
|
||||||
.putExtra(ARG_START_RECIPIENT_ID, storyId)
|
.putExtra(ARG_START_RECIPIENT_ID, recipientId)
|
||||||
|
.putExtra(ARG_START_STORY_ID, storyId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,13 @@ class StoryViewerFragment : Fragment(R.layout.stories_viewer_fragment), StoryVie
|
||||||
private val storyRecipientId: RecipientId
|
private val storyRecipientId: RecipientId
|
||||||
get() = requireArguments().getParcelable(ARG_START_RECIPIENT_ID)!!
|
get() = requireArguments().getParcelable(ARG_START_RECIPIENT_ID)!!
|
||||||
|
|
||||||
|
private val storyId: Long
|
||||||
|
get() = requireArguments().getLong(ARG_START_STORY_ID, -1L)
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
storyPager = view.findViewById(R.id.story_item_pager)
|
storyPager = view.findViewById(R.id.story_item_pager)
|
||||||
|
|
||||||
val adapter = StoryViewerPagerAdapter(this)
|
val adapter = StoryViewerPagerAdapter(this, storyId)
|
||||||
storyPager.adapter = adapter
|
storyPager.adapter = adapter
|
||||||
|
|
||||||
viewModel.state.observe(viewLifecycleOwner) { state ->
|
viewModel.state.observe(viewLifecycleOwner) { state ->
|
||||||
|
@ -77,11 +80,13 @@ class StoryViewerFragment : Fragment(R.layout.stories_viewer_fragment), StoryVie
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG_START_RECIPIENT_ID = "start.recipient.id"
|
private const val ARG_START_RECIPIENT_ID = "start.recipient.id"
|
||||||
|
private const val ARG_START_STORY_ID = "start.story.id"
|
||||||
|
|
||||||
fun create(storyRecipientId: RecipientId): Fragment {
|
fun create(storyRecipientId: RecipientId, storyId: Long): Fragment {
|
||||||
return StoryViewerFragment().apply {
|
return StoryViewerFragment().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
putParcelable(ARG_START_RECIPIENT_ID, storyRecipientId)
|
putParcelable(ARG_START_RECIPIENT_ID, storyRecipientId)
|
||||||
|
putLong(ARG_START_STORY_ID, storyId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewerPageFragment
|
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewerPageFragment
|
||||||
|
|
||||||
class StoryViewerPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
class StoryViewerPagerAdapter(fragment: Fragment, private val initialStoryId: Long) : FragmentStateAdapter(fragment) {
|
||||||
|
|
||||||
private var pages: List<RecipientId> = emptyList()
|
private var pages: List<RecipientId> = emptyList()
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class StoryViewerPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragmen
|
||||||
override fun getItemCount(): Int = pages.size
|
override fun getItemCount(): Int = pages.size
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
return StoryViewerPageFragment.create(pages[position])
|
return StoryViewerPageFragment.create(pages[position], initialStoryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Callback(
|
private class Callback(
|
||||||
|
|
|
@ -19,7 +19,7 @@ class StoryViewerRepository {
|
||||||
|
|
||||||
val myStory: RecipientId = SignalDatabase.recipients.getOrInsertFromDistributionListId(DistributionListId.MY_STORY)
|
val myStory: RecipientId = SignalDatabase.recipients.getOrInsertFromDistributionListId(DistributionListId.MY_STORY)
|
||||||
|
|
||||||
val myStoriesCount = SignalDatabase.mms.allOutgoingStories.use {
|
val myStoriesCount = SignalDatabase.mms.getAllOutgoingStories(true).use {
|
||||||
var count = 0
|
var count = 0
|
||||||
while (it.next != null) {
|
while (it.next != null) {
|
||||||
if (!it.current.recipient.isGroup) {
|
if (!it.current.recipient.isGroup) {
|
||||||
|
|
|
@ -77,7 +77,7 @@ class StoryViewerPageFragment :
|
||||||
|
|
||||||
private val viewModel: StoryViewerPageViewModel by viewModels(
|
private val viewModel: StoryViewerPageViewModel by viewModels(
|
||||||
factoryProducer = {
|
factoryProducer = {
|
||||||
StoryViewerPageViewModel.Factory(storyRecipientId, StoryViewerPageRepository(requireContext()))
|
StoryViewerPageViewModel.Factory(storyRecipientId, initialStoryId, StoryViewerPageRepository(requireContext()))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,6 +93,9 @@ class StoryViewerPageFragment :
|
||||||
private val storyRecipientId: RecipientId
|
private val storyRecipientId: RecipientId
|
||||||
get() = requireArguments().getParcelable(ARG_STORY_RECIPIENT_ID)!!
|
get() = requireArguments().getParcelable(ARG_STORY_RECIPIENT_ID)!!
|
||||||
|
|
||||||
|
private val initialStoryId: Long
|
||||||
|
get() = requireArguments().getLong(ARG_STORY_ID, -1L)
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
callback = requireListener()
|
callback = requireListener()
|
||||||
|
@ -624,11 +627,13 @@ class StoryViewerPageFragment :
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG_STORY_RECIPIENT_ID = "arg.story.recipient.id"
|
private const val ARG_STORY_RECIPIENT_ID = "arg.story.recipient.id"
|
||||||
|
private const val ARG_STORY_ID = "arg.story.id"
|
||||||
|
|
||||||
fun create(recipientId: RecipientId): Fragment {
|
fun create(recipientId: RecipientId, initialStoryId: Long): Fragment {
|
||||||
return StoryViewerPageFragment().apply {
|
return StoryViewerPageFragment().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
putParcelable(ARG_STORY_RECIPIENT_ID, recipientId)
|
putParcelable(ARG_STORY_RECIPIENT_ID, recipientId)
|
||||||
|
putLong(ARG_STORY_ID, initialStoryId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ class StoryViewerPageRepository(context: Context) {
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
val stories = if (recipient.isMyStory) {
|
val stories = if (recipient.isMyStory) {
|
||||||
SignalDatabase.mms.allOutgoingStories
|
SignalDatabase.mms.getAllOutgoingStories(false)
|
||||||
} else {
|
} else {
|
||||||
SignalDatabase.mms.getAllStoriesFor(recipientId)
|
SignalDatabase.mms.getAllStoriesFor(recipientId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import kotlin.math.min
|
||||||
*/
|
*/
|
||||||
class StoryViewerPageViewModel(
|
class StoryViewerPageViewModel(
|
||||||
private val recipientId: RecipientId,
|
private val recipientId: RecipientId,
|
||||||
|
private val initialStoryId: Long,
|
||||||
private val repository: StoryViewerPageRepository
|
private val repository: StoryViewerPageRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
@ -44,10 +45,18 @@ class StoryViewerPageViewModel(
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
disposables.clear()
|
disposables.clear()
|
||||||
disposables += repository.getStoryPostsFor(recipientId).subscribe { posts ->
|
disposables += repository.getStoryPostsFor(recipientId).subscribe { posts ->
|
||||||
store.update {
|
store.update { state ->
|
||||||
it.copy(
|
val startIndex = if (state.posts.isEmpty() && initialStoryId > 0) {
|
||||||
|
val initialIndex = posts.indexOfFirst { it.id == initialStoryId }
|
||||||
|
initialIndex.takeIf { it > -1 } ?: state.selectedPostIndex
|
||||||
|
} else {
|
||||||
|
state.selectedPostIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
state.copy(
|
||||||
posts = posts,
|
posts = posts,
|
||||||
replyState = resolveSwipeToReplyState(it)
|
replyState = resolveSwipeToReplyState(state, startIndex),
|
||||||
|
selectedPostIndex = startIndex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +171,7 @@ class StoryViewerPageViewModel(
|
||||||
storyViewerPlaybackStore.update { it.copy(isDisplayingLinkPreviewTooltip = isDisplayingLinkPreviewTooltip) }
|
storyViewerPlaybackStore.update { it.copy(isDisplayingLinkPreviewTooltip = isDisplayingLinkPreviewTooltip) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveSwipeToReplyState(state: StoryViewerPageState, index: Int = state.selectedPostIndex): StoryViewerPageState.ReplyState {
|
private fun resolveSwipeToReplyState(state: StoryViewerPageState, index: Int): StoryViewerPageState.ReplyState {
|
||||||
if (index !in state.posts.indices) {
|
if (index !in state.posts.indices) {
|
||||||
return StoryViewerPageState.ReplyState.NONE
|
return StoryViewerPageState.ReplyState.NONE
|
||||||
}
|
}
|
||||||
|
@ -182,9 +191,9 @@ class StoryViewerPageViewModel(
|
||||||
return store.state.posts.getOrNull(index)
|
return store.state.posts.getOrNull(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(private val recipientId: RecipientId, private val repository: StoryViewerPageRepository) : ViewModelProvider.Factory {
|
class Factory(private val recipientId: RecipientId, private val initialStoryId: Long, private val repository: StoryViewerPageRepository) : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||||
return modelClass.cast(StoryViewerPageViewModel(recipientId, repository)) as T
|
return modelClass.cast(StoryViewerPageViewModel(recipientId, initialStoryId, repository)) as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue