Finalize wallpaper UX.

Co-authored-by: Greyson Parrelli <greyson@signal.org>
Co-authored-by: Alan Evans <alan@signal.org>
This commit is contained in:
Alex Hart
2021-01-20 17:09:36 -04:00
committed by Greyson Parrelli
parent a8ad1e718e
commit c244a98962
29 changed files with 455 additions and 90 deletions

View File

@@ -429,6 +429,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
setContentView(R.layout.conversation_activity);
getWindow().getDecorView().setBackgroundResource(R.color.signal_background_primary);
WindowUtil.setLightNavigationBar(getWindow());
fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale());
@@ -1989,6 +1990,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
}
private void updateWallpaper(@Nullable ChatWallpaper chatWallpaper) {
Log.d(TAG, "Setting wallpaper.");
if (chatWallpaper != null) {
chatWallpaper.loadInto(wallpaper);
ChatWallpaperDimLevelUtil.applyDimLevelForNightMode(wallpaperDim, chatWallpaper);
@@ -1996,6 +1998,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
wallpaper.setImageDrawable(null);
wallpaperDim.setVisibility(View.GONE);
}
fragment.onWallpaperChanged(chatWallpaper);
}
protected void initializeActionBar() {
@@ -2105,6 +2108,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
this.viewModel = ViewModelProviders.of(this, new ConversationViewModel.Factory()).get(ConversationViewModel.class);
this.viewModel.setArgs(args);
this.viewModel.getWallpaper().observe(this, this::updateWallpaper);
}
private void initializeGroupViewModel() {
@@ -2290,7 +2294,6 @@ public class ConversationActivity extends PassphraseRequiredActivity
updateReminders();
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
initializeSecurity(isSecureText, isDefaultSms);
updateWallpaper(recipient.getWallpaper());
if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
invalidateOptionsMenu();

View File

@@ -73,6 +73,10 @@ public class ConversationAdapter
private static final String TAG = Log.tag(ConversationAdapter.class);
public static final int HEADER_TYPE_POPOVER_DATE = 1;
public static final int HEADER_TYPE_INLINE_DATE = 2;
public static final int HEADER_TYPE_LAST_SEEN = 3;
private static final int MESSAGE_TYPE_OUTGOING_MULTIMEDIA = 0;
private static final int MESSAGE_TYPE_OUTGOING_TEXT = 1;
private static final int MESSAGE_TYPE_INCOMING_MULTIMEDIA = 2;
@@ -102,6 +106,7 @@ public class ConversationAdapter
private View headerView;
private View footerView;
private PagingController pagingController;
private boolean hasWallpaper;
ConversationAdapter(@NonNull LifecycleOwner lifecycleOwner,
@NonNull GlideRequests glideRequests,
@@ -132,6 +137,7 @@ public class ConversationAdapter
this.releasedFastRecords = new HashSet<>();
this.calendar = Calendar.getInstance();
this.digest = getMessageDigestOrThrow();
this.hasWallpaper = recipient.hasWallpaper();
setHasStableIds(true);
}
@@ -243,7 +249,7 @@ public class ConversationAdapter
recipient,
searchQuery,
conversationMessage == recordToPulse,
recipient.hasWallpaper());
hasWallpaper);
if (conversationMessage == recordToPulse) {
recordToPulse = null;
@@ -290,19 +296,27 @@ public class ConversationAdapter
}
@Override
public StickyHeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
public StickyHeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position, int type) {
return new StickyHeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.conversation_item_header, parent, false));
}
@Override
public void onBindHeaderViewHolder(StickyHeaderViewHolder viewHolder, int position) {
public void onBindHeaderViewHolder(StickyHeaderViewHolder viewHolder, int position, int type) {
ConversationMessage conversationMessage = Objects.requireNonNull(getItem(position));
viewHolder.setText(DateUtils.getRelativeDate(viewHolder.itemView.getContext(), locale, conversationMessage.getMessageRecord().getDateReceived()));
if (recipient.hasWallpaper()) {
viewHolder.setBackgroundRes(R.drawable.wallpaper_bubble_background_8);
} else {
viewHolder.clearBackground();
if (type == HEADER_TYPE_POPOVER_DATE) {
if (hasWallpaper) {
viewHolder.setBackgroundRes(R.drawable.wallpaper_bubble_background_8);
} else {
viewHolder.setBackgroundRes(R.drawable.sticky_date_header_background);
}
} else if (type == HEADER_TYPE_INLINE_DATE) {
if (hasWallpaper) {
viewHolder.setBackgroundRes(R.drawable.wallpaper_bubble_background_8);
} else {
viewHolder.clearBackground();
}
}
}
@@ -334,7 +348,7 @@ public class ConversationAdapter
void onBindLastSeenViewHolder(StickyHeaderViewHolder viewHolder, int position) {
viewHolder.setText(viewHolder.itemView.getContext().getResources().getQuantityString(R.plurals.ConversationAdapter_n_unread_messages, (position + 1), (position + 1)));
if (recipient.hasWallpaper()) {
if (hasWallpaper) {
viewHolder.setBackgroundRes(R.drawable.wallpaper_bubble_background_8);
} else {
viewHolder.clearBackground();
@@ -435,6 +449,17 @@ public class ConversationAdapter
notifyDataSetChanged();
}
/**
* Lets the adapter know that the wallpaper state has changed.
*/
void onHasWallpaperChanged(boolean hasWallpaper) {
if (this.hasWallpaper != hasWallpaper) {
Log.d(TAG, "Resetting adapter due to wallpaper change.");
this.hasWallpaper = hasWallpaper;
notifyDataSetChanged();
}
}
/**
* Adds a record to a memory cache to allow it to be rendered immediately, as opposed to waiting
* for a database change.

View File

@@ -141,6 +141,7 @@ import org.thoughtcrime.securesms.util.WindowUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.views.AdaptiveActionsToolbar;
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
@@ -171,7 +172,7 @@ public class ConversationFragment extends LoggingFragment {
private Locale locale;
private RecyclerView list;
private RecyclerView.ItemDecoration lastSeenDecoration;
private RecyclerView.ItemDecoration stickyHeaderDecoration;
private RecyclerView.ItemDecoration popoverDateDecoration;
private ViewSwitcher topLoadMoreView;
private ViewSwitcher bottomLoadMoreView;
private ConversationTypingView typingView;
@@ -390,6 +391,13 @@ public class ConversationFragment extends LoggingFragment {
snapToTopDataObserver.requestScrollPosition(position);
}
public void onWallpaperChanged(@Nullable ChatWallpaper wallpaper) {
if (list != null) {
Log.d(TAG, "Notifying adapter that wallpaper state has changed.");
getListAdapter().onHasWallpaperChanged(wallpaper != null);
}
}
private int getStartPosition() {
return conversationViewModel.getArgs().getStartingPosition();
}
@@ -488,7 +496,7 @@ public class ConversationFragment extends LoggingFragment {
this.threadId = conversationViewModel.getArgs().getThreadId();
this.markReadHelper = new MarkReadHelper(threadId, requireContext());
conversationViewModel.onConversationDataAvailable(threadId, startingPosition);
conversationViewModel.onConversationDataAvailable(recipient.getId(), threadId, startingPosition);
messageCountsViewModel.setThreadId(threadId);
messageCountsViewModel.getUnreadMessagesCount().observe(getViewLifecycleOwner(), scrollToBottomButton::setUnreadCount);
@@ -511,7 +519,7 @@ public class ConversationFragment extends LoggingFragment {
ConversationAdapter adapter = new ConversationAdapter(this, GlideApp.with(this), locale, selectionClickListener, this.recipient.get());
adapter.setPagingController(conversationViewModel.getPagingController());
list.setAdapter(adapter);
setStickyHeaderDecoration(adapter);
setPopoverDateDecoration(adapter);
ConversationAdapter.initializePool(list.getRecycledViewPool());
adapter.registerAdapterDataObserver(snapToTopDataObserver);
@@ -638,7 +646,7 @@ public class ConversationFragment extends LoggingFragment {
messageRequestViewModel.setConversationInfo(recipient.getId(), threadId);
snapToTopDataObserver.requestScrollPosition(0);
conversationViewModel.onConversationDataAvailable(threadId, -1);
conversationViewModel.onConversationDataAvailable(recipient.getId(), threadId, -1);
messageCountsViewModel.setThreadId(threadId);
initializeListAdapter();
}
@@ -652,13 +660,13 @@ public class ConversationFragment extends LoggingFragment {
}
}
public void setStickyHeaderDecoration(@NonNull ConversationAdapter adapter) {
if (stickyHeaderDecoration != null) {
list.removeItemDecoration(stickyHeaderDecoration);
public void setPopoverDateDecoration(@NonNull ConversationAdapter adapter) {
if (popoverDateDecoration != null) {
list.removeItemDecoration(popoverDateDecoration);
}
stickyHeaderDecoration = new StickyHeaderDecoration(adapter, false, false);
list.addItemDecoration(stickyHeaderDecoration);
popoverDateDecoration = new StickyHeaderDecoration(adapter, false, false, ConversationAdapter.HEADER_TYPE_INLINE_DATE);
list.addItemDecoration(popoverDateDecoration);
}
public void setLastSeen(long lastSeen) {
@@ -1180,7 +1188,7 @@ public class ConversationFragment extends LoggingFragment {
private void bindScrollHeader(StickyHeaderViewHolder headerViewHolder, int positionId) {
if (((ConversationAdapter)list.getAdapter()).getHeaderId(positionId) != -1) {
((ConversationAdapter) list.getAdapter()).onBindHeaderViewHolder(headerViewHolder, positionId);
((ConversationAdapter) list.getAdapter()).onBindHeaderViewHolder(headerViewHolder, positionId, ConversationAdapter.HEADER_TYPE_POPOVER_DATE);
}
}
}

View File

@@ -142,7 +142,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
}
public void setListVerticalTranslation(float translationY) {
maskView.setTargetParentTranslationY(translationY);
maskView.setTargetParentTranslationY(translationY - ViewUtil.getStatusBarHeight(maskView));
}
public void show(@NonNull Activity activity,

View File

@@ -19,7 +19,11 @@ import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mediasend.MediaRepository;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
import org.whispersystems.libsignal.util.Pair;
import java.util.List;
@@ -41,6 +45,8 @@ class ConversationViewModel extends ViewModel {
private final LiveData<Boolean> canShowAsBubble;
private final ProxyPagingController pagingController;
private final DatabaseObserver.Observer messageObserver;
private final MutableLiveData<RecipientId> recipientId;
private final LiveData<ChatWallpaper> wallpaper;
private ConversationIntents.Args args;
private int jumpToPosition;
@@ -53,6 +59,7 @@ class ConversationViewModel extends ViewModel {
this.threadId = new MutableLiveData<>();
this.showScrollButtons = new MutableLiveData<>(false);
this.hasUnreadMentions = new MutableLiveData<>(false);
this.recipientId = new MutableLiveData<>();
this.pagingController = new ProxyPagingController();
this.messageObserver = pagingController::onDataInvalidated;
@@ -97,6 +104,9 @@ class ConversationViewModel extends ViewModel {
conversationMetadata = Transformations.switchMap(messages, m -> metadata);
canShowAsBubble = LiveDataUtil.mapAsync(threadId, conversationRepository::canShowAsBubble);
wallpaper = Transformations.distinctUntilChanged(Transformations.map(Transformations.switchMap(recipientId,
id -> Recipient.live(id).getLiveData()),
Recipient::getWallpaper));
}
void onAttachmentKeyboardOpen() {
@@ -104,11 +114,12 @@ class ConversationViewModel extends ViewModel {
}
@MainThread
void onConversationDataAvailable(long threadId, int startingPosition) {
void onConversationDataAvailable(@NonNull RecipientId recipientId, long threadId, int startingPosition) {
Log.d(TAG, "[onConversationDataAvailable] threadId: " + threadId + ", startingPosition: " + startingPosition);
this.jumpToPosition = startingPosition;
this.threadId.setValue(threadId);
this.recipientId.setValue(recipientId);
}
void clearThreadId() {
@@ -128,6 +139,10 @@ class ConversationViewModel extends ViewModel {
return Transformations.distinctUntilChanged(LiveDataUtil.combineLatest(showScrollButtons, hasUnreadMentions, (a, b) -> a && b));
}
@NonNull LiveData<ChatWallpaper> getWallpaper() {
return wallpaper;
}
void setHasUnreadMentions(boolean hasUnreadMentions) {
this.hasUnreadMentions.setValue(hasUnreadMentions);
}

View File

@@ -17,7 +17,7 @@ class LastSeenHeader extends StickyHeaderDecoration {
private final long lastSeenTimestamp;
LastSeenHeader(ConversationAdapter adapter, long lastSeenTimestamp) {
super(adapter, false, false);
super(adapter, false, false, ConversationAdapter.HEADER_TYPE_LAST_SEEN);
this.adapter = adapter;
this.lastSeenTimestamp = lastSeenTimestamp;
}