diff options
Diffstat (limited to 'java/com/android/dialer/voicemail/listui')
7 files changed, 315 insertions, 23 deletions
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java index 61fed52e6..955c7daee 100644 --- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java +++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java @@ -21,28 +21,49 @@ import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; +import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ViewHolder; import android.util.ArrayMap; import android.util.ArraySet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.android.dialer.calllogutils.CallLogDates; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.time.Clock; import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener; import com.android.dialer.voicemail.model.VoicemailEntry; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.Set; /** {@link RecyclerView.Adapter} for the new voicemail call log fragment. */ -final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHolder> +final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> implements NewVoicemailViewHolderListener { + /** IntDef for the different types of rows that can be shown in the call log. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({RowType.HEADER, RowType.VOICEMAIL_ENTRY}) + @interface RowType { + /** Header that displays "Today" or "Older". */ + int HEADER = 1; + /** A row representing a voicemail entry. */ + int VOICEMAIL_ENTRY = 2; + } + private final Cursor cursor; private final Clock clock; + + /** {@link Integer#MAX_VALUE} when the "Today" header should not be displayed. */ + private final int todayHeaderPosition; + /** {@link Integer#MAX_VALUE} when the "Older" header should not be displayed. */ + private final int olderHeaderPosition; + private final FragmentManager fragmentManager; /** A valid id for {@link VoicemailEntry} is greater than 0 */ private int currentlyExpandedViewHolderId = -1; @@ -73,6 +94,33 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol this.clock = clock; this.fragmentManager = fragmentManager; initializeMediaPlayerListeners(); + + // Calculate header adapter positions by reading cursor. + long currentTimeMillis = clock.currentTimeMillis(); + if (cursor.moveToNext()) { + long firstTimestamp = VoicemailCursorLoader.getTimestamp(cursor); + if (CallLogDates.isSameDay(currentTimeMillis, firstTimestamp)) { + this.todayHeaderPosition = 0; + int adapterPosition = 2; // Accounted for "Today" header and first row. + while (cursor.moveToNext()) { + long timestamp = VoicemailCursorLoader.getTimestamp(cursor); + + if (CallLogDates.isSameDay(currentTimeMillis, timestamp)) { + adapterPosition++; + } else { + this.olderHeaderPosition = adapterPosition; + return; + } + } + this.olderHeaderPosition = Integer.MAX_VALUE; // Didn't find any "Older" rows. + } else { + this.todayHeaderPosition = Integer.MAX_VALUE; // Didn't find any "Today" rows. + this.olderHeaderPosition = 0; + } + } else { // There are no rows, just need to set these because they are final. + this.todayHeaderPosition = Integer.MAX_VALUE; + this.olderHeaderPosition = Integer.MAX_VALUE; + } } private void initializeMediaPlayerListeners() { @@ -82,41 +130,123 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol } @Override - public NewVoicemailViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + public ViewHolder onCreateViewHolder(ViewGroup viewGroup, @RowType int viewType) { + LogUtil.enterBlock("NewVoicemailAdapter.onCreateViewHolder"); LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); - View view = inflater.inflate(R.layout.new_voicemail_entry, viewGroup, false); - NewVoicemailViewHolder newVoicemailViewHolder = new NewVoicemailViewHolder(view, clock, this); - newVoicemailViewHolderSet.add(newVoicemailViewHolder); - return newVoicemailViewHolder; + View view; + switch (viewType) { + case RowType.HEADER: + view = inflater.inflate(R.layout.new_voicemail_entry_header, viewGroup, false); + return new NewVoicemailHeaderViewHolder(view); + case NewVoicemailAdapter.RowType.VOICEMAIL_ENTRY: + view = inflater.inflate(R.layout.new_voicemail_entry, viewGroup, false); + NewVoicemailViewHolder newVoicemailViewHolder = + new NewVoicemailViewHolder(view, clock, this); + newVoicemailViewHolderSet.add(newVoicemailViewHolder); + return newVoicemailViewHolder; + default: + throw Assert.createUnsupportedOperationFailException("Unsupported view type: " + viewType); + } } + // TODO(uabdullah): a bug - Clean up logging in this function, here for debugging during + // development. @Override - public void onBindViewHolder(NewVoicemailViewHolder viewHolder, int position) { + public void onBindViewHolder(ViewHolder viewHolder, int position) { + LogUtil.enterBlock("NewVoicemailAdapter.onBindViewHolder, pos:" + position); + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + printHashSet(); + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + printHashMap(); + + if (viewHolder instanceof NewVoicemailHeaderViewHolder) { + LogUtil.i( + "NewVoicemailAdapter.onBindViewHolder", "view holder at pos:%d is a header", position); + NewVoicemailHeaderViewHolder headerViewHolder = (NewVoicemailHeaderViewHolder) viewHolder; + @RowType int viewType = getItemViewType(position); + if (position == todayHeaderPosition) { + headerViewHolder.setHeader(R.string.new_voicemail_header_today); + } else if (position == olderHeaderPosition) { + headerViewHolder.setHeader(R.string.new_voicemail_header_older); + } else { + throw Assert.createIllegalStateFailException( + "Unexpected view type " + viewType + " at position: " + position); + } + return; + } + + LogUtil.i( + "NewVoicemailAdapter.onBindViewHolder", + "view holder at pos:%d is a not a header", + position); + + NewVoicemailViewHolder newVoicemailViewHolder = (NewVoicemailViewHolder) viewHolder; + + int previousHeaders = 0; + if (todayHeaderPosition != Integer.MAX_VALUE && position > todayHeaderPosition) { + previousHeaders++; + } + if (olderHeaderPosition != Integer.MAX_VALUE && position > olderHeaderPosition) { + previousHeaders++; + } + + LogUtil.i( + "NewVoicemailAdapter.onBindViewHolder", + "view holder at pos:%d, prevHeaderCount:%d", + position, + previousHeaders); + // Remove if the viewholder is being recycled. - if (newVoicemailViewHolderArrayMap.containsKey(viewHolder.getViewHolderId())) { - // TODO(uabdullah): Remove the logging, only here for debugging during development. + if (newVoicemailViewHolderArrayMap.containsKey(newVoicemailViewHolder.getViewHolderId())) { + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. LogUtil.i( "NewVoicemailAdapter.onBindViewHolder", - "Removing from hashset:%d, hashsetSize:%d", - viewHolder.getViewHolderId(), - newVoicemailViewHolderArrayMap.size()); + "Removing from hashset:%d, hashsetSize:%d, currExpanded:%d", + newVoicemailViewHolder.getViewHolderId(), + newVoicemailViewHolderArrayMap.size(), + currentlyExpandedViewHolderId); - newVoicemailViewHolderArrayMap.remove(viewHolder.getViewHolderId()); + newVoicemailViewHolderArrayMap.remove(newVoicemailViewHolder.getViewHolderId()); + printHashSet(); + printHashMap(); } - viewHolder.reset(); - cursor.moveToPosition(position); - viewHolder.bindViewHolderValuesFromAdapter( + newVoicemailViewHolder.reset(); + cursor.moveToPosition(position - previousHeaders); + newVoicemailViewHolder.bindViewHolderValuesFromAdapter( cursor, fragmentManager, mediaPlayer, position, currentlyExpandedViewHolderId); + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + LogUtil.i( + "NewVoicemailAdapter.onBindViewHolder", + "Adding to hashset:%d, hashsetSize:%d, pos:%d, currExpanded:%d", + newVoicemailViewHolder.getViewHolderId(), + newVoicemailViewHolderArrayMap.size(), + position, + currentlyExpandedViewHolderId); + // Need this to ensure correct getCurrentlyExpandedViewHolder() value - newVoicemailViewHolderArrayMap.put(viewHolder.getViewHolderId(), viewHolder); + newVoicemailViewHolderArrayMap.put( + newVoicemailViewHolder.getViewHolderId(), newVoicemailViewHolder); + + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + printHashSet(); + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + printHashMap(); // If the viewholder is playing the voicemail, keep updating its media player view (seekbar, // duration etc.) - if (viewHolder.isViewHolderExpanded() && mediaPlayer.isPlaying()) { + if (newVoicemailViewHolder.isViewHolderExpanded() && mediaPlayer.isPlaying()) { + LogUtil.i( + "NewVoicemailAdapter.onBindViewHolder", + "Adding to hashset:%d, hashsetSize:%d, pos:%d, currExpanded:%d", + newVoicemailViewHolderSet.size(), + newVoicemailViewHolderArrayMap.size(), + position, + currentlyExpandedViewHolderId); + Assert.checkArgument( - viewHolder + newVoicemailViewHolder .getViewHolderVoicemailUri() .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri()), "only the expanded view holder can be playing."); @@ -126,10 +256,48 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol .getViewHolderVoicemailUri() .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri())); - recursivelyUpdateMediaPlayerViewOfExpandedViewHolder(viewHolder); + recursivelyUpdateMediaPlayerViewOfExpandedViewHolder(newVoicemailViewHolder); } // Updates the hashmap with the most up-to-date state of the viewholder. - newVoicemailViewHolderArrayMap.put(viewHolder.getViewHolderId(), viewHolder); + newVoicemailViewHolderArrayMap.put( + newVoicemailViewHolder.getViewHolderId(), newVoicemailViewHolder); + + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + printHashSet(); + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + printHashMap(); + } + + private void printHashMap() { + LogUtil.i( + "NewVoicemailAdapter.printHashMap", + "hashMapSize: %d, currentlyExpandedViewHolderId:%d", + newVoicemailViewHolderArrayMap.size(), + currentlyExpandedViewHolderId); + + if (!newVoicemailViewHolderArrayMap.isEmpty()) { + String ids = ""; + for (int id : newVoicemailViewHolderArrayMap.keySet()) { + ids = id + String.valueOf(id) + " "; + } + LogUtil.i("NewVoicemailAdapter.printHashMap", "ids are " + ids); + } + } + + private void printHashSet() { + LogUtil.i( + "NewVoicemailAdapter.printHashSet", + "hashSetSize: %d, currentlyExpandedViewHolderId:%d", + newVoicemailViewHolderSet.size(), + currentlyExpandedViewHolderId); + + if (!newVoicemailViewHolderSet.isEmpty()) { + String viewHolderID = ""; + for (NewVoicemailViewHolder vh : newVoicemailViewHolderSet) { + viewHolderID = viewHolderID + vh.getViewHolderId() + " "; + } + LogUtil.i("NewVoicemailAdapter.printHashSet", "ids are " + viewHolderID); + } } /** @@ -316,6 +484,11 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol */ private void recursivelyUpdateMediaPlayerViewOfExpandedViewHolder( NewVoicemailViewHolder expandedViewHolderPossiblyPlaying) { + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + LogUtil.i( + "NewVoicemailAdapter.recursivelyUpdateMediaPlayerViewOfExpandedViewHolder", + "currentlyExpanded:%d", + currentlyExpandedViewHolderId); // It's possible that by the time this is run, the expanded view holder has been // scrolled out of view (and possibly recycled) @@ -368,7 +541,7 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol mediaPlayer .getLastPlayedOrPlayingVoicemailUri() .equals(getCurrentlyExpandedViewHolder().getViewHolderVoicemailUri())); - // TODO(uabdullah): Remove this, here for debugging during development. + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. LogUtil.i( "NewVoicemailAdapter.recursivelyUpdateMediaPlayerViewOfExpandedViewHolder", "recursely update the player, currentlyExpanded:%d", @@ -544,12 +717,36 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol "no view holder found in newVoicemailViewHolderArrayMap size:%d for %d", newVoicemailViewHolderArrayMap.size(), currentlyExpandedViewHolderId); + // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. + printHashSet(); + printHashMap(); return null; } } @Override public int getItemCount() { - return cursor.getCount(); + LogUtil.enterBlock("NewVoicemailAdapter.getItemCount"); + int numberOfHeaders = 0; + if (todayHeaderPosition != Integer.MAX_VALUE) { + numberOfHeaders++; + } + if (olderHeaderPosition != Integer.MAX_VALUE) { + numberOfHeaders++; + } + return cursor.getCount() + numberOfHeaders; + } + + @RowType + @Override + public int getItemViewType(int position) { + LogUtil.enterBlock("NewVoicemailAdapter.getItemViewType"); + if (todayHeaderPosition != Integer.MAX_VALUE && position == todayHeaderPosition) { + return RowType.HEADER; + } + if (olderHeaderPosition != Integer.MAX_VALUE && position == olderHeaderPosition) { + return RowType.HEADER; + } + return RowType.VOICEMAIL_ENTRY; } } diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailHeaderViewHolder.java b/java/com/android/dialer/voicemail/listui/NewVoicemailHeaderViewHolder.java new file mode 100644 index 000000000..6bd8e86ce --- /dev/null +++ b/java/com/android/dialer/voicemail/listui/NewVoicemailHeaderViewHolder.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dialer.voicemail.listui; + +import android.support.annotation.StringRes; +import android.support.annotation.VisibleForTesting; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.view.View; +import android.widget.TextView; + +/** ViewHolder for {@link NewVoicemailAdapter} to display "Today" or "Older" divider row. */ +final class NewVoicemailHeaderViewHolder extends ViewHolder { + + private final TextView headerTextView; + + NewVoicemailHeaderViewHolder(View view) { + super(view); + headerTextView = view.findViewById(R.id.new_voicemail_header_text); + } + + void setHeader(@StringRes int header) { + headerTextView.setText(header); + } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + String getHeaderText() { + return headerTextView.getText().toString(); + } +} diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java b/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java index 02b05db03..d5b17a19d 100644 --- a/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java +++ b/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java @@ -92,8 +92,18 @@ final class NewVoicemailViewHolder extends RecyclerView.ViewHolder implements On int position, int currentlyExpandedViewHolderId) { + LogUtil.i( + "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", + "view holder at pos:%d, adapterPos:%d, cursorPos:%d, cursorSize:%d", + position, + getAdapterPosition(), + cursor.getPosition(), + cursor.getCount()); + voicemailEntryOfViewHolder = VoicemailCursorLoader.toVoicemailEntry(cursor); viewHolderId = voicemailEntryOfViewHolder.id(); + LogUtil.i( + "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", "viewholderId:%d", viewHolderId); viewHolderVoicemailUri = Uri.parse(voicemailEntryOfViewHolder.voicemailUri()); primaryTextView.setText( VoicemailEntryText.buildPrimaryVoicemailText(context, voicemailEntryOfViewHolder)); diff --git a/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java b/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java index 3112d95ac..6a55483a4 100644 --- a/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java +++ b/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java @@ -97,4 +97,8 @@ final class VoicemailCursorLoader extends CursorLoader { .setCallType(cursor.getInt(CALL_TYPE)) .build(); } + + static long getTimestamp(Cursor cursor) { + return cursor.getLong(TIMESTAMP); + } } diff --git a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_header.xml b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_header.xml new file mode 100644 index 000000000..84fcc377e --- /dev/null +++ b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_header.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2017 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:height="48dp" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:id="@+id/new_voicemail_header_text" + style="@style/SecondaryText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/voicemail_header_margin_start" + android:layout_centerVertical="true"/> +</RelativeLayout> diff --git a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml index e37bc65fe..52fad4917 100644 --- a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml +++ b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml @@ -36,4 +36,6 @@ <!-- TODO(uabdullah): Work with UX on these values so that the touch target is not too small --> <dimen name="voicemail_media_player_height">56dp</dimen> <dimen name="voicemail_media_player_width">0dp</dimen> + + <dimen name="voicemail_header_margin_start">16dp</dimen> </resources> diff --git a/java/com/android/dialer/voicemail/listui/res/values/strings.xml b/java/com/android/dialer/voicemail/listui/res/values/strings.xml index 508f67436..53c525279 100644 --- a/java/com/android/dialer/voicemail/listui/res/values/strings.xml +++ b/java/com/android/dialer/voicemail/listui/res/values/strings.xml @@ -27,4 +27,11 @@ <!-- String used to display the default staring point of a voicemail--> <string name="voicemail_media_player_inital_start_position" translatable="false">00:00</string> + + <!-- Header in voicemail tab to group calls from the current day. [CHAR LIMIT=30] --> + <string name="new_voicemail_header_today">Today</string> + + <!-- Header in voicemail tab to group calls from before the current day. [CHAR LIMIT=30] --> + <string name="new_voicemail_header_older">Older</string> + </resources>
\ No newline at end of file |