Loading java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java +220 −23 Original line number Original line Diff line number Diff line Loading @@ -21,28 +21,49 @@ import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnPreparedListener; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.ArraySet; import android.util.ArraySet; import android.view.LayoutInflater; import android.view.LayoutInflater; import android.view.View; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import com.android.dialer.calllogutils.CallLogDates; import com.android.dialer.common.Assert; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.time.Clock; import com.android.dialer.time.Clock; import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener; import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener; import com.android.dialer.voicemail.model.VoicemailEntry; import com.android.dialer.voicemail.model.VoicemailEntry; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.Set; /** {@link RecyclerView.Adapter} for the new voicemail call log fragment. */ /** {@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 { 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 Cursor cursor; private final Clock clock; 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; private final FragmentManager fragmentManager; /** A valid id for {@link VoicemailEntry} is greater than 0 */ /** A valid id for {@link VoicemailEntry} is greater than 0 */ private int currentlyExpandedViewHolderId = -1; private int currentlyExpandedViewHolderId = -1; Loading Loading @@ -73,6 +94,33 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol this.clock = clock; this.clock = clock; this.fragmentManager = fragmentManager; this.fragmentManager = fragmentManager; initializeMediaPlayerListeners(); 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() { private void initializeMediaPlayerListeners() { Loading @@ -82,41 +130,123 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol } } @Override @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()); LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); View view = inflater.inflate(R.layout.new_voicemail_entry, viewGroup, false); View view; NewVoicemailViewHolder newVoicemailViewHolder = new NewVoicemailViewHolder(view, clock, this); 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); newVoicemailViewHolderSet.add(newVoicemailViewHolder); return 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 @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. // Remove if the viewholder is being recycled. if (newVoicemailViewHolderArrayMap.containsKey(viewHolder.getViewHolderId())) { if (newVoicemailViewHolderArrayMap.containsKey(newVoicemailViewHolder.getViewHolderId())) { // TODO(uabdullah): Remove the logging, only here for debugging during development. // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. LogUtil.i( LogUtil.i( "NewVoicemailAdapter.onBindViewHolder", "NewVoicemailAdapter.onBindViewHolder", "Removing from hashset:%d, hashsetSize:%d", "Removing from hashset:%d, hashsetSize:%d, currExpanded:%d", viewHolder.getViewHolderId(), newVoicemailViewHolder.getViewHolderId(), newVoicemailViewHolderArrayMap.size()); newVoicemailViewHolderArrayMap.size(), currentlyExpandedViewHolderId); newVoicemailViewHolderArrayMap.remove(viewHolder.getViewHolderId()); newVoicemailViewHolderArrayMap.remove(newVoicemailViewHolder.getViewHolderId()); printHashSet(); printHashMap(); } } viewHolder.reset(); newVoicemailViewHolder.reset(); cursor.moveToPosition(position); cursor.moveToPosition(position - previousHeaders); viewHolder.bindViewHolderValuesFromAdapter( newVoicemailViewHolder.bindViewHolderValuesFromAdapter( cursor, fragmentManager, mediaPlayer, position, currentlyExpandedViewHolderId); 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 // 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, // If the viewholder is playing the voicemail, keep updating its media player view (seekbar, // duration etc.) // 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( Assert.checkArgument( viewHolder newVoicemailViewHolder .getViewHolderVoicemailUri() .getViewHolderVoicemailUri() .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri()), .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri()), "only the expanded view holder can be playing."); "only the expanded view holder can be playing."); Loading @@ -126,10 +256,48 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol .getViewHolderVoicemailUri() .getViewHolderVoicemailUri() .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri())); .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri())); recursivelyUpdateMediaPlayerViewOfExpandedViewHolder(viewHolder); recursivelyUpdateMediaPlayerViewOfExpandedViewHolder(newVoicemailViewHolder); } } // Updates the hashmap with the most up-to-date state of the viewholder. // 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); } } } /** /** Loading Loading @@ -316,6 +484,11 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol */ */ private void recursivelyUpdateMediaPlayerViewOfExpandedViewHolder( private void recursivelyUpdateMediaPlayerViewOfExpandedViewHolder( NewVoicemailViewHolder expandedViewHolderPossiblyPlaying) { 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 // It's possible that by the time this is run, the expanded view holder has been // scrolled out of view (and possibly recycled) // scrolled out of view (and possibly recycled) Loading Loading @@ -368,7 +541,7 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol mediaPlayer mediaPlayer .getLastPlayedOrPlayingVoicemailUri() .getLastPlayedOrPlayingVoicemailUri() .equals(getCurrentlyExpandedViewHolder().getViewHolderVoicemailUri())); .equals(getCurrentlyExpandedViewHolder().getViewHolderVoicemailUri())); // TODO(uabdullah): Remove this, here for debugging during development. // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. LogUtil.i( LogUtil.i( "NewVoicemailAdapter.recursivelyUpdateMediaPlayerViewOfExpandedViewHolder", "NewVoicemailAdapter.recursivelyUpdateMediaPlayerViewOfExpandedViewHolder", "recursely update the player, currentlyExpanded:%d", "recursely update the player, currentlyExpanded:%d", Loading Loading @@ -544,12 +717,36 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol "no view holder found in newVoicemailViewHolderArrayMap size:%d for %d", "no view holder found in newVoicemailViewHolderArrayMap size:%d for %d", newVoicemailViewHolderArrayMap.size(), newVoicemailViewHolderArrayMap.size(), currentlyExpandedViewHolderId); currentlyExpandedViewHolderId); // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. printHashSet(); printHashMap(); return null; return null; } } } } @Override @Override public int getItemCount() { 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; } } } } java/com/android/dialer/voicemail/listui/NewVoicemailHeaderViewHolder.java 0 → 100644 +43 −0 Original line number Original line Diff line number Diff line /* * 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(); } } java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -92,8 +92,18 @@ final class NewVoicemailViewHolder extends RecyclerView.ViewHolder implements On int position, int position, int currentlyExpandedViewHolderId) { 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); voicemailEntryOfViewHolder = VoicemailCursorLoader.toVoicemailEntry(cursor); viewHolderId = voicemailEntryOfViewHolder.id(); viewHolderId = voicemailEntryOfViewHolder.id(); LogUtil.i( "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", "viewholderId:%d", viewHolderId); viewHolderVoicemailUri = Uri.parse(voicemailEntryOfViewHolder.voicemailUri()); viewHolderVoicemailUri = Uri.parse(voicemailEntryOfViewHolder.voicemailUri()); primaryTextView.setText( primaryTextView.setText( VoicemailEntryText.buildPrimaryVoicemailText(context, voicemailEntryOfViewHolder)); VoicemailEntryText.buildPrimaryVoicemailText(context, voicemailEntryOfViewHolder)); Loading java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -97,4 +97,8 @@ final class VoicemailCursorLoader extends CursorLoader { .setCallType(cursor.getInt(CALL_TYPE)) .setCallType(cursor.getInt(CALL_TYPE)) .build(); .build(); } } static long getTimestamp(Cursor cursor) { return cursor.getLong(TIMESTAMP); } } } java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_header.xml 0 → 100644 +29 −0 Original line number Original line Diff line number Diff line <?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> Loading
java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java +220 −23 Original line number Original line Diff line number Diff line Loading @@ -21,28 +21,49 @@ import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnPreparedListener; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.ArraySet; import android.util.ArraySet; import android.view.LayoutInflater; import android.view.LayoutInflater; import android.view.View; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import com.android.dialer.calllogutils.CallLogDates; import com.android.dialer.common.Assert; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.time.Clock; import com.android.dialer.time.Clock; import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener; import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener; import com.android.dialer.voicemail.model.VoicemailEntry; import com.android.dialer.voicemail.model.VoicemailEntry; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.Set; /** {@link RecyclerView.Adapter} for the new voicemail call log fragment. */ /** {@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 { 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 Cursor cursor; private final Clock clock; 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; private final FragmentManager fragmentManager; /** A valid id for {@link VoicemailEntry} is greater than 0 */ /** A valid id for {@link VoicemailEntry} is greater than 0 */ private int currentlyExpandedViewHolderId = -1; private int currentlyExpandedViewHolderId = -1; Loading Loading @@ -73,6 +94,33 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol this.clock = clock; this.clock = clock; this.fragmentManager = fragmentManager; this.fragmentManager = fragmentManager; initializeMediaPlayerListeners(); 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() { private void initializeMediaPlayerListeners() { Loading @@ -82,41 +130,123 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol } } @Override @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()); LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); View view = inflater.inflate(R.layout.new_voicemail_entry, viewGroup, false); View view; NewVoicemailViewHolder newVoicemailViewHolder = new NewVoicemailViewHolder(view, clock, this); 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); newVoicemailViewHolderSet.add(newVoicemailViewHolder); return 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 @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. // Remove if the viewholder is being recycled. if (newVoicemailViewHolderArrayMap.containsKey(viewHolder.getViewHolderId())) { if (newVoicemailViewHolderArrayMap.containsKey(newVoicemailViewHolder.getViewHolderId())) { // TODO(uabdullah): Remove the logging, only here for debugging during development. // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. LogUtil.i( LogUtil.i( "NewVoicemailAdapter.onBindViewHolder", "NewVoicemailAdapter.onBindViewHolder", "Removing from hashset:%d, hashsetSize:%d", "Removing from hashset:%d, hashsetSize:%d, currExpanded:%d", viewHolder.getViewHolderId(), newVoicemailViewHolder.getViewHolderId(), newVoicemailViewHolderArrayMap.size()); newVoicemailViewHolderArrayMap.size(), currentlyExpandedViewHolderId); newVoicemailViewHolderArrayMap.remove(viewHolder.getViewHolderId()); newVoicemailViewHolderArrayMap.remove(newVoicemailViewHolder.getViewHolderId()); printHashSet(); printHashMap(); } } viewHolder.reset(); newVoicemailViewHolder.reset(); cursor.moveToPosition(position); cursor.moveToPosition(position - previousHeaders); viewHolder.bindViewHolderValuesFromAdapter( newVoicemailViewHolder.bindViewHolderValuesFromAdapter( cursor, fragmentManager, mediaPlayer, position, currentlyExpandedViewHolderId); 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 // 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, // If the viewholder is playing the voicemail, keep updating its media player view (seekbar, // duration etc.) // 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( Assert.checkArgument( viewHolder newVoicemailViewHolder .getViewHolderVoicemailUri() .getViewHolderVoicemailUri() .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri()), .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri()), "only the expanded view holder can be playing."); "only the expanded view holder can be playing."); Loading @@ -126,10 +256,48 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol .getViewHolderVoicemailUri() .getViewHolderVoicemailUri() .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri())); .equals(mediaPlayer.getLastPlayedOrPlayingVoicemailUri())); recursivelyUpdateMediaPlayerViewOfExpandedViewHolder(viewHolder); recursivelyUpdateMediaPlayerViewOfExpandedViewHolder(newVoicemailViewHolder); } } // Updates the hashmap with the most up-to-date state of the viewholder. // 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); } } } /** /** Loading Loading @@ -316,6 +484,11 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol */ */ private void recursivelyUpdateMediaPlayerViewOfExpandedViewHolder( private void recursivelyUpdateMediaPlayerViewOfExpandedViewHolder( NewVoicemailViewHolder expandedViewHolderPossiblyPlaying) { 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 // It's possible that by the time this is run, the expanded view holder has been // scrolled out of view (and possibly recycled) // scrolled out of view (and possibly recycled) Loading Loading @@ -368,7 +541,7 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol mediaPlayer mediaPlayer .getLastPlayedOrPlayingVoicemailUri() .getLastPlayedOrPlayingVoicemailUri() .equals(getCurrentlyExpandedViewHolder().getViewHolderVoicemailUri())); .equals(getCurrentlyExpandedViewHolder().getViewHolderVoicemailUri())); // TODO(uabdullah): Remove this, here for debugging during development. // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. LogUtil.i( LogUtil.i( "NewVoicemailAdapter.recursivelyUpdateMediaPlayerViewOfExpandedViewHolder", "NewVoicemailAdapter.recursivelyUpdateMediaPlayerViewOfExpandedViewHolder", "recursely update the player, currentlyExpanded:%d", "recursely update the player, currentlyExpanded:%d", Loading Loading @@ -544,12 +717,36 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<NewVoicemailViewHol "no view holder found in newVoicemailViewHolderArrayMap size:%d for %d", "no view holder found in newVoicemailViewHolderArrayMap size:%d for %d", newVoicemailViewHolderArrayMap.size(), newVoicemailViewHolderArrayMap.size(), currentlyExpandedViewHolderId); currentlyExpandedViewHolderId); // TODO(uabdullah): a bug Remove logging, temporarily here for debugging. printHashSet(); printHashMap(); return null; return null; } } } } @Override @Override public int getItemCount() { 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; } } } }
java/com/android/dialer/voicemail/listui/NewVoicemailHeaderViewHolder.java 0 → 100644 +43 −0 Original line number Original line Diff line number Diff line /* * 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(); } }
java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -92,8 +92,18 @@ final class NewVoicemailViewHolder extends RecyclerView.ViewHolder implements On int position, int position, int currentlyExpandedViewHolderId) { 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); voicemailEntryOfViewHolder = VoicemailCursorLoader.toVoicemailEntry(cursor); viewHolderId = voicemailEntryOfViewHolder.id(); viewHolderId = voicemailEntryOfViewHolder.id(); LogUtil.i( "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", "viewholderId:%d", viewHolderId); viewHolderVoicemailUri = Uri.parse(voicemailEntryOfViewHolder.voicemailUri()); viewHolderVoicemailUri = Uri.parse(voicemailEntryOfViewHolder.voicemailUri()); primaryTextView.setText( primaryTextView.setText( VoicemailEntryText.buildPrimaryVoicemailText(context, voicemailEntryOfViewHolder)); VoicemailEntryText.buildPrimaryVoicemailText(context, voicemailEntryOfViewHolder)); Loading
java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -97,4 +97,8 @@ final class VoicemailCursorLoader extends CursorLoader { .setCallType(cursor.getInt(CALL_TYPE)) .setCallType(cursor.getInt(CALL_TYPE)) .build(); .build(); } } static long getTimestamp(Cursor cursor) { return cursor.getLong(TIMESTAMP); } } }
java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_header.xml 0 → 100644 +29 −0 Original line number Original line Diff line number Diff line <?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>