I'd like to implement streaming video list app. I used a RecyclerView to display my list item. Item type includes 4 type: Article, Status, Photo and Video. We should only focus on Video type. Here is my code for RecyclerView's adapter:
public class FollowedPostAdapter extends RecyclerView.Adapter implements OnFollowTagCallback, OnLikeCallback {
private Context context;
private List<PostItem> newsFeedList;
public RecyclerView recyclerView;
public LinearLayoutManager linearLayoutManager;
private HashMap<Integer, DemoPlayer> playerList;
private int visibleThreshold = 5;
// private int previousTotal = 0;
private int visibleItemCount, firstVisibleItem, totalItemCount;
private boolean loading;
private OnRecyclerViewLoadMoreListener loadMoreListener;
private final String text_comment;
private final String mReadMoreHtml;
private long userId;
public FollowedPostAdapter(Context context, RecyclerView recyclerView, List<PostItem> newsFeedList) {
this.context = context;
playerList = new HashMap<>();
this.newsFeedList = newsFeedList;
this.recyclerView = recyclerView;
this.linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder;
if (viewType == Constants.VIEWTYPE_ARTICLE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_article_item, parent, false);
viewHolder = new ArticleViewHolder(view);
} else if (viewType == Constants.VIEWTYPE_STATUS) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_status_item, parent, false);
viewHolder = new StatusViewHolder(view);
} else if (viewType == Constants.VIEWTYPE_PHOTO) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_photo_item, parent, false);
viewHolder = new PhotoViewHolder(view);
} else if (viewType == Constants.VIEWTYPE_VIDEO) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_video_item, parent, false);
viewHolder = new VideoViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.progressbar_item, parent, false);
viewHolder = new ProgressBarViewHolder(view);
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ArticleViewHolder) {
// code
} else if (holder instanceof StatusViewHolder) {
// code
} else if (holder instanceof PhotoViewHolder) {
// code
} else if (holder instanceof VideoViewHolder) {
PostItem item = newsFeedList.get(position);
VideoViewHolder mHolder = (VideoViewHolder) holder;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mHolder.videoView.getLayoutParams();
lp.height = (int) (ScreenHelper.getScreenWidth((Activity) context) * Constants.POST_IMAGE_RATIO);
mHolder.videoView.setLayoutParams(lp);
mHolder.setup(item);
Picasso.with(context).load(item.imageCover).error(R.drawable.ic_user_avatar).placeholder(R.drawable.ic_user_avatar).into(mHolder.iv_avatar);
if (item.tags != null && item.tags.size() > 0) {
// get first tag as main tag
generateTagViews(mHolder.tag_flow_layout, item.tags.subList(0, 1), position);
mHolder.tag_flow_layout.setVisibility(View.VISIBLE);
mHolder.tag_flow_layout.setVisibility(View.VISIBLE);
// mHolder.indicator.setVisibility(View.VISIBLE);
} else {
mHolder.tag_flow_layout.setVisibility(View.GONE);
// mHolder.indicator.setVisibility(View.GONE);
}
if (item.time_created != null) {
mHolder.tv_time.setText(item.time_created);
//mHolder.indicator.setVisibility(View.VISIBLE);
mHolder.tv_time.setVisibility(View.VISIBLE);
} else {
//mHolder.indicator.setVisibility(View.GONE);
mHolder.tv_time.setVisibility(View.GONE);
}
if (item.description_short.isEmpty())
mHolder.tv_description.setVisibility(View.GONE);
else {
mHolder.tv_description.setText(item.description_short);
mHolder.tv_description.setVisibility(View.VISIBLE);
}
mHolder.btn_comment.setText(String.valueOf(item.count_comment));
mHolder.btn_like.setText(String.valueOf(item.count_like));
mHolder.btn_unlike.setText(String.valueOf(item.count_unlike));
mHolder.btn_share.setText(String.valueOf(item.count_share));
if (item.tags.size() != 0) {
int tagId = item.tags.get(0).tag_id;
setFollowButtonActive(mHolder.btn_follow, TagHelper.isTagFollowed(tagId));
} else
setFollowButtonActive(mHolder.btn_follow, false);
}
}
#Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
super.onViewRecycled(holder);
if (holder instanceof VideoViewHolder) {
DemoPlayer player = playerList.get(holder.getAdapterPosition());
if (player != null) {
player.release();
playerList.remove(holder.getAdapterPosition());
}
}
}
public void pauseAllPlayers() {
for (int i = 0; i <= newsFeedList.size(); i++) {
DemoPlayer player = playerList.get(i);
if (player != null) {
if (player.getPlayerControl().isPlaying())
player.getPlayerControl().pause();
RecyclerView.ViewHolder holder = recyclerView.findViewHolderForLayoutPosition(i);
if (holder != null && holder instanceof VideoViewHolder) {
((VideoViewHolder) holder).btn_play.setVisibility(View.VISIBLE);
}
}
}
}
public void refreshData() {
notifyDataSetChanged();
}
public class ArticleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
//
}
public class StatusViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// code
}
public class PhotoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// code
}
public class VideoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, SurfaceHolder.Callback, AudioCapabilitiesReceiver.Listener, DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener {
#Bind(R.id.iv_avatar)
ImageView iv_avatar;
#Bind(R.id.tag_flow_layout)
FlowLayout tag_flow_layout;
#Bind(R.id.tv_time)
TextView tv_time;
#Bind(R.id.btn_follow)
FancyButton btn_follow;
#Bind(R.id.btn_comment)
FancyButton btn_comment;
#Bind(R.id.btn_like)
FancyButton btn_like;
#Bind(R.id.btn_unlike)
FancyButton btn_unlike;
#Bind(R.id.btn_share)
FancyButton btn_share;
#Bind(R.id.root)
FrameLayout videoView;
#Bind(R.id.btn_play)
ImageView btn_play;
#Bind(R.id.tv_description)
TextView tv_description;
// player's variable
private EventLogger eventLogger;
// private VideoControllerView mediaController;
private View shutterView;
private AspectRatioFrameLayout videoFrame;
private SurfaceView surfaceView;
private SubtitleLayout subtitleLayout;
private DemoPlayer player;
private boolean playerNeedsPrepare;
private long playerPosition = 0;
private Uri contentUri;
private int contentType;
private String contentId;
public static final int TYPE_DASH = 0;
public static final int TYPE_SS = 1;
public static final int TYPE_HLS = 2;
public static final int TYPE_OTHER = 3;
private static final String EXT_DASH = ".mpd";
private static final String EXT_SS = ".ism";
private static final String EXT_HLS = ".m3u8";
private AudioCapabilitiesReceiver audioCapabilitiesReceiver;
public VideoViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
iv_avatar.setOnClickListener(this);
btn_follow.setOnClickListener(this);
btn_comment.setOnClickListener(this);
btn_like.setOnClickListener(this);
btn_unlike.setOnClickListener(this);
btn_share.setOnClickListener(this);
// player's setup
View root = view.findViewById(R.id.root);
root.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
doPlayResume();
}
});
// root.setOnTouchListener(new View.OnTouchListener() {
// #Override
// public boolean onTouch(View view, MotionEvent motionEvent) {
// if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
// toggleControlsVisibility();
// } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
// view.performClick();
// }
// return true;
// }
// });
// root.setOnKeyListener(new View.OnKeyListener() {
// #Override
// public boolean onKey(View v, int keyCode, KeyEvent event) {
// if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE
// || keyCode == KeyEvent.KEYCODE_MENU) {
// return false;
// }
// return mediaController.dispatchKeyEvent(event);
// }
// });
shutterView = view.findViewById(R.id.shutter);
videoFrame = (AspectRatioFrameLayout) view.findViewById(R.id.video_frame);
surfaceView = (SurfaceView) view.findViewById(R.id.surface_view);
surfaceView.getHolder().addCallback(this);
subtitleLayout = (SubtitleLayout) view.findViewById(R.id.subtitles);
// mediaController = new VideoControllerView(context);
// mediaController.setAnchorView((ViewGroup) root);
audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(context, this);
audioCapabilitiesReceiver.register();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.iv_avatar:
viewTagDetail(getAdapterPosition());
break;
case R.id.btn_follow:
followTag((FancyButton) v, getAdapterPosition());
break;
case R.id.btn_comment:
commentPost(getAdapterPosition());
break;
case R.id.btn_like:
likePost(getAdapterPosition(), btn_like, btn_unlike);
break;
case R.id.btn_unlike:
unlikePost(getAdapterPosition(), btn_like, btn_unlike);
break;
case R.id.btn_share:
sharePost(getAdapterPosition());
break;
case R.id.btn_play:
doPlayResume();
break;
}
}
public void setup(PostItem item) {
releasePlayer();
player = playerList.get(getAdapterPosition());
contentUri = Uri.parse(item.link);
contentType = TYPE_OTHER;
contentId = String.valueOf(item.post_id);
configureSubtitleView();
if (player == null) {
preparePlayer(false);
} else {
player.setBackgrounded(false);
}
}
// public void saveCurrentPosition() {
// if (player != null)
// videoItemList.get(getAdapterPosition()).position = player.getCurrentPosition();
// }
public void doPlayResume() {
if (player == null) {
return;
}
if (player.getPlayerControl().isPlaying()) {
player.getPlayerControl().pause();
} else {
player.getPlayerControl().start();
}
showControls();
}
#Override
public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
if (player == null) {
return;
}
boolean backgrounded = player.getBackgrounded();
boolean playWhenReady = player.getPlayWhenReady();
releasePlayer();
preparePlayer(playWhenReady);
player.setBackgrounded(backgrounded);
}
private DemoPlayer.RendererBuilder getRendererBuilder() {
String userAgent = Util.getUserAgent(context, "ExoPlayerDemo");
switch (contentType) {
case TYPE_SS:
return new SmoothStreamingRendererBuilder(context, userAgent, contentUri.toString(),
new SmoothStreamingTestMediaDrmCallback());
case TYPE_DASH:
return new DashRendererBuilder(context, userAgent, contentUri.toString(),
new WidevineTestMediaDrmCallback(contentId));
case TYPE_HLS:
return new HlsRendererBuilder(context, userAgent, contentUri.toString());
case TYPE_OTHER:
return new ExtractorRendererBuilder(context, userAgent, contentUri);
default:
throw new IllegalStateException("Unsupported type: " + contentType);
}
}
private void preparePlayer(boolean playWhenReady) {
if (player == null) {
player = new DemoPlayer(getRendererBuilder());
playerList.put(getAdapterPosition(), player);
player.addListener(this);
player.setCaptionListener(this);
player.setMetadataListener(this);
player.seekTo(playerPosition);
playerNeedsPrepare = true;
// mediaController.setMediaPlayer(player.getPlayerControl());
// mediaController.setEnabled(true);
eventLogger = new EventLogger();
eventLogger.startSession();
player.addListener(eventLogger);
player.setInfoListener(eventLogger);
player.setInternalErrorListener(eventLogger);
}
if (playerNeedsPrepare) {
player.prepare();
playerNeedsPrepare = false;
}
player.setSurface(surfaceView.getHolder().getSurface());
player.setPlayWhenReady(playWhenReady);
}
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
eventLogger.endSession();
eventLogger = null;
btn_play.setVisibility(View.VISIBLE);
}
}
#Override
public void onStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_ENDED) {
showControls();
}
}
#Override
public void onError(Exception e) {
if (e instanceof UnsupportedDrmException) {
// Special case DRM failures.
UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e;
int stringId = Util.SDK_INT < 18 ? R.string.drm_error_not_supported
: unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.drm_error_unsupported_scheme : R.string.drm_error_unknown;
Toast.makeText(context, stringId, Toast.LENGTH_LONG).show();
}
playerNeedsPrepare = true;
showControls();
}
#Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
shutterView.setVisibility(View.GONE);
videoFrame.setAspectRatio(
height == 0 ? 1 : (width * pixelWidthHeightRatio) / height);
}
private boolean haveTracks(int type) {
return player != null && player.getTrackCount(type) > 0;
}
// private void toggleControlsVisibility() {
// if (mediaController.isShowing()) {
// mediaController.hide();
// } else {
// showControls();
// }
// }
private void showControls() {
// mediaController.show(5000);
if (player.getPlayerControl().isPlaying())
btn_play.setVisibility(View.GONE);
else
btn_play.setVisibility(View.VISIBLE);
}
#Override
public void onCues(List<Cue> cues) {
subtitleLayout.setCues(cues);
}
#Override
public void onId3Metadata(Map<String, Object> metadata) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (player != null) {
player.setSurface(holder.getSurface());
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (player != null) {
holder.lockCanvas();
player.blockingClearSurface();
}
}
private void configureSubtitleView() {
CaptionStyleCompat style;
float fontScale;
if (Util.SDK_INT >= 19) {
style = getUserCaptionStyleV19();
fontScale = getUserCaptionFontScaleV19();
} else {
style = CaptionStyleCompat.DEFAULT;
fontScale = 1.0f;
}
subtitleLayout.setStyle(style);
subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale);
}
#TargetApi(19)
private float getUserCaptionFontScaleV19() {
CaptioningManager captioningManager =
(CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
return captioningManager.getFontScale();
}
#TargetApi(19)
private CaptionStyleCompat getUserCaptionStyleV19() {
CaptioningManager captioningManager =
(CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle());
}
private int inferContentType(Uri uri, String fileExtension) {
String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension
: uri.getLastPathSegment();
if (lastPathSegment == null) {
return TYPE_OTHER;
} else if (lastPathSegment.endsWith(EXT_DASH)) {
return TYPE_DASH;
} else if (lastPathSegment.endsWith(EXT_SS)) {
return TYPE_SS;
} else if (lastPathSegment.endsWith(EXT_HLS)) {
return TYPE_HLS;
} else {
return TYPE_OTHER;
}
}
}
public class ProgressBarViewHolder extends RecyclerView.ViewHolder {
public ProgressBarViewHolder(View view) {
super(view);
}
}
public void unregisterEventBus() {
EventBus.getDefault().unregister(this);
}
}
When item type is video, I create an ExoPlayer instance to play this video and add this instance to a HashMap for later use (with key value is the item's position). If a video item is being recycled, I release the player and remove it from HashMap.
Everything looks good but a problem. I suppose the video item is at 0 position (now we can see the video preview in this item). I scroll down the RecyclerView just enough to hide the item 0. At this moment, the VideoViewHolder of item 0 is not recycled yet. Then, I scroll up to reveal item 0. The view is now blank, not show the video preview. I click item 0 to play the video, the player only plays audio (sound), not show video. After playing a few seconds, the video is now visible.
I debugged and found that after I scrolled down to hide video item, the SurfaceView is destroyed. When scroll up to reveal video item, the SurfaceView is created. I think this is the reason why VideoViewHolder shows blank view instead of the video preview.
Here is my question: How to display video preview after scroll back to video item?
Here is note from ExoPlayer documentation
SurfaceView rendering wasn’t properly synchronized with view animations until Android N. On earlier releases this could result in unwanted effects when a SurfaceView was placed into scrolling container, or when it was subjected to animation. Such effects included the SurfaceView’s contents appearing to lag slightly behind where it should be displayed, and the view turning black when subjected to animation.
To achieve smooth animation or scrolling of video prior to Android N, it’s therefore necessary to use TextureView rather than SurfaceView. If smooth animation or scrolling is not required then SurfaceView should be preferred
To enable TextureView surface_type you have to set it in your xml file like shown below
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="#+id/playerView"
android:layout_width="match_parent"
android:layout_height="200dp"
app:surface_type="texture_view"/>
Related
I heard that if we want to remove any item from our RecyclerView we can use this method for example:
private void removeItem(int position){
recordingArrayList.remove(position);
notifyItemRemoved(position);
notifyItemChanged(position, recordingArrayList.size());
notifyDataSetChanged();
}
But seems I missed something, here is my RecordingListActivity .java:
public class RecordingListActivity extends AppCompatActivity {
private RecyclerView recyclerViewRecordings;
private BottomNavigationView bottomNavigationView;
private BottomAppBar bottomAppBar;
private ArrayList<Recording> recordingArrayList;
private FloatingActionButton floatingActionButton;
private RecordingAdapter recordingAdapter;
private TextView textViewNoRecordings;
private TimeAgo timeAgo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recording_list);
timeAgo = new TimeAgo();
initViews();
fetchRecordings();
}
private void initViews() {
recordingArrayList = new ArrayList<Recording>();
floatingActionButton = findViewById(R.id.fab);
recyclerViewRecordings = findViewById(R.id.recyclerViewRecordings);
recyclerViewRecordings.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerViewRecordings.setHasFixedSize(true);
new ItemTouchHelper(simpleCallback).attachToRecyclerView(recyclerViewRecordings);
textViewNoRecordings = findViewById(R.id.textViewNoRecordings);
floatingActionButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v.getId() == R.id.fab){
openHome();
}
}
});
}
private void openHome(){
Intent home = new Intent(RecordingListActivity.this, MainActivity.class);
home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(home);
}
private void fetchRecordings() {
File root = android.os.Environment.getExternalStorageDirectory();
String path = root.getAbsolutePath() + "/AbdulhaleemRecorderApp/Audios";
Log.d("Files", "Path: " + path);
File directory = new File(path);
File[] files = directory.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
Log.d("Files", "FileName:" + files[i].getName());
String fileName = files[i].getName();
String recordingUri = root.getAbsolutePath() + "/AbdulhaleemRecorderApp/Audios/" + fileName;
Double size = (double) files[i].length() / 1024;
String time = timeAgo.getTimeAgo(files[i].lastModified());
String date = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
Recording recording = new Recording(recordingUri, fileName, date, size, time,false);
recordingArrayList.add(recording);
}
textViewNoRecordings.setVisibility(View.GONE);
recyclerViewRecordings.setVisibility(View.VISIBLE);
setAdaptertoRecyclerView();
} else {
textViewNoRecordings.setVisibility(View.VISIBLE);
recyclerViewRecordings.setVisibility(View.GONE);
}
}
private void setAdaptertoRecyclerView() {
recordingAdapter = new RecordingAdapter(this, recordingArrayList);
recyclerViewRecordings.setAdapter(recordingAdapter);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return false;
}
ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getBindingAdapterPosition();
removeItem(position);
}
};
private void removeItem(int position){
recordingArrayList.remove(position);
recordingAdapter.notifyItemRemoved(position);
recordingAdapter.notifyItemChanged(position, recordingArrayList.size());
recordingAdapter.notifyDataSetChanged();
}
here is my RecordingAdapter.java
public class RecordingAdapter extends RecyclerView.Adapter<RecordingAdapter.ViewHolder> {
private ArrayList<Recording> recordingArrayList;
private Context context;
private MediaPlayer mPlayer;
private boolean isPlaying = false;
private int last_index = -1;
public RecordingAdapter(Context context, ArrayList<Recording> recordingArrayList) {
this.context = context;
this.recordingArrayList = recordingArrayList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.recording_item_layout, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
setUpData(holder, position);
}
#Override
public int getItemCount() {
return recordingArrayList.size();
}
private void setUpData(ViewHolder holder, int position) {
Recording recording = recordingArrayList.get(position);
holder.textViewName.setText(recording.getFileName());
holder.textViewDate.setText(recording.getDate());
holder.textViewSize.setText(String.format("%.2f", (double) (recording.getSize())) + " KB");
holder.textViewTime.setText(recording.getTime());
if (recording.isPlaying()) {
holder.imageViewPlay.setImageResource(R.drawable.ic_pause);
TransitionManager.beginDelayedTransition((ViewGroup) holder.itemView);
holder.seekBar.setVisibility(View.VISIBLE);
holder.seekUpdation(holder);
} else {
holder.imageViewPlay.setImageResource(R.drawable.ic_play);
TransitionManager.beginDelayedTransition((ViewGroup) holder.itemView);
holder.seekBar.setVisibility(View.GONE);
}
holder.manageSeekBar(holder);
}
public class ViewHolder extends RecyclerView.ViewHolder {
SeekBar seekBar;
ImageView imageViewPlay;
ImageView imageViewShare;
ImageView imageViewEdit;
TextView textViewName;
TextView textViewDate;
TextView textViewSize;
TextView textViewTime;
TextView textViewPhoneNumber;
private String recordingUri;
private int lastProgress = 0;
private Handler mHandler = new Handler();
ViewHolder holder;
public ViewHolder(View itemView) {
super(itemView);
final Context vhContext = itemView.getContext();
seekBar = itemView.findViewById(R.id.seekBar);
imageViewPlay = itemView.findViewById(R.id.imageViewPlay);
imageViewShare = itemView.findViewById(R.id.imageViewShare);
imageViewEdit = itemView.findViewById(R.id.imageViewEdit);
textViewName = itemView.findViewById(R.id.textViewRecordingname);
textViewDate = itemView.findViewById(R.id.textViewDate);
textViewSize = itemView.findViewById(R.id.textViewSize);
textViewTime = itemView.findViewById(R.id.textViewTime);
textViewPhoneNumber = itemView.findViewById(R.id.textViewPhoneNumber);
imageViewShare.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
Recording recording = recordingArrayList.get(position);
recordingUri = recording.getUri();
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("audio/mp3");
share.putExtra(Intent.EXTRA_STREAM, recordingUri);
vhContext.startActivity(Intent.createChooser(share, "Share " + recording.getFileName() + " using:"));
}
});
imageViewPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = getAdapterPosition();
Recording recording = recordingArrayList.get(position);
recordingUri = recording.getUri();
if (isPlaying) {
stopPlaying();
if (position == last_index) {
recording.setPlaying(false);
stopPlaying();
notifyItemChanged(position);
} else {
markAllPaused();
recording.setPlaying(true);
notifyItemChanged(position);
startPlaying(recording, position);
last_index = position;
}
} else {
startPlaying(recording, position);
recording.setPlaying(true);
seekBar.setMax(mPlayer.getDuration());
Log.d("isPlayin", "False");
notifyItemChanged(position);
last_index = position;
}
}
});
}
public void manageSeekBar(ViewHolder holder) {
holder.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mPlayer != null && fromUser) {
mPlayer.seekTo(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
private void markAllPaused() {
for (int i = 0; i < recordingArrayList.size(); i++) {
recordingArrayList.get(i).setPlaying(false);
recordingArrayList.set(i, recordingArrayList.get(i));
}
notifyDataSetChanged();
}
Runnable runnable = new Runnable() {
#Override
public void run() {
seekUpdation(holder);
}
};
private void seekUpdation(ViewHolder holder) {
this.holder = holder;
if (mPlayer != null) {
int mCurrentPosition = mPlayer.getCurrentPosition();
holder.seekBar.setMax(mPlayer.getDuration());
holder.seekBar.setProgress(mCurrentPosition);
lastProgress = mCurrentPosition;
}
mHandler.postDelayed(runnable, 100);
}
private void stopPlaying() {
try {
mPlayer.release();
} catch (Exception e) {
e.printStackTrace();
}
mPlayer = null;
isPlaying = false;
}
private void startPlaying(final Recording audio, final int position) {
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(recordingUri);
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
Log.e("LOG_TAG", "prepare() failed");
}
//showing the pause button
seekBar.setMax(mPlayer.getDuration());
isPlaying = true;
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
audio.setPlaying(false);
notifyItemChanged(position);
}
});
}
when I swap the item it seems it deleted, but when I open another activity and come back again the deleted item comes back again like it is never deleted, what is the problem?
Corresponding to your code it's expected behaviour. So, your code fetches files data from device file system in onCreate() method of your activity and creates a list of representation of the files.
When recordingArrayList.remove(position); is invoked, it removes item from the representation list, but not file from file system.
When you call another activity and goes back, activity with the list is created again and onCreate() is called again and your code fetches again all the files from device file system and shows it in a list.
Possible solution can be: remove file from file system, if you have read-write access to this folder or save a list in your app. For example, in Room database and add to your file representation field isDeleted and use data from database to show in a list.
Hope it helps!
You are not saving the changes to the file and when you reload the activity, the array is fetched again from the file which hasn't been updated.
Your removeItem function should be in adapter class or after removing you should pass changed list to adapter and after that notify changes. In your case you actually not removing item from adapter (because list in activity and list in adapter are different (different instances)).
You just need to delete the file name from where you are reading the file too, cause when you remove an item from the list it isn't removed from the file. So when removing remove its element from the list too.
Try to keep your logic for adapter change in your adapter only.
I'll suggest you make a method name setData(ArrayList<Recording> recordingsList) in your recycler view.
In RecordingAdapter
public class RecordingAdapter extends RecyclerView.Adapter<RecordingAdapter.ViewHolder> {
private ArrayList<Recording> recordingArrayList;
private Context context;
public RecordingAdapter(Context context, ArrayList<Recording> recordingArrayList) {
this.context = context;
this.recordingArrayList = recordingArrayList;
}
// added
public setData(ArrayList<Recording> recordingsList) {
this.recordingArrayList = recordingsList;
notifyDataSetChanged();
}
}
And when you swipe to delete call the RecordingAdapter setData(RecordingLists) method
RecordingListActivity
ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getBindingAdapterPosition();
recordingArrayList.remove(position);
recordingAdapter.setData(recordingArrayList); // changed
}
};
First remove item from the list then refresh your adapter by notifyItemRangeChanged with new size of the list and most important you need to update the data which you are fetching for recycler-view.
recordingArrayList.remove(position);
notifyItemRangeChanged(position,recordingArrayList.size());
When I try to load more items when the user had scrolled to the end, a progress dialog is displayed and removed and then again displayed and then more items are displayed but progress dialog is still not removed from recycler view. And sometimes progress dialog is removed but still, it takes more time to display more items in recycler view. I'm using firestore addOnSuccessListener to retrieve items from cloud firestore and adding it to an arraylist and then adding native ads advanced to that arraylist and add the data to the recycler adapter.
Activity where recycler view items are displayed, MovieLists.java
public class MovieLists extends AppCompatActivity implements ItemInterface{
//nothing important just a comment
boolean runshowMovieOnce = true;
Query mQuery;
int loaderPosition;
DocumentSnapshot lastVisible;
String docId, catName, titleName;
ItemInterface itemInterface;
DatabaseReference listData;
String VideoId;
MovieListAdapter movieListAdapter;
RecyclerView movieLists;
// List of native ads that have been successfully loaded.
private List<UnifiedNativeAd> mNativeAds = new ArrayList<>();
List<Object> itemDatas = new ArrayList<>();
LinearLayoutManager layoutManager;
// The number of native ads to load.
public static final int NUMBER_OF_ADS = 2;
// The AdLoader used to load ads.
private AdLoader adLoader;
int index = 2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_lists);
MaterialToolbar mToolBar = (MaterialToolbar) findViewById(R.id.imagePreviewToolbar);
setSupportActionBar(mToolBar);
docId = getIntent().getStringExtra("documentId");
catName = getIntent().getStringExtra("catName");
titleName = getIntent().getStringExtra("titleName");
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.back_button);
getSupportActionBar().setTitle(titleName);
}
movieLists = (RecyclerView)findViewById(R.id.movie_lists);
layoutManager = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
itemInterface = this;
loadMovies();
}
#Override
public void onFirebaseLoadSuccess(List<Object> itemDataList) {
loadNativeAds();
}
#Override
public void onFirebaseLoadFailed(String message) {
}
#SuppressLint("MissingPermission")
private void loadNativeAds() {
AdLoader.Builder builder = new AdLoader.Builder(this, "ca-app-pub-3940256099942544/8135179316");
adLoader = builder.forUnifiedNativeAd(
new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
#Override
public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
// A native ad loaded successfully, check if the ad loader has finished loading
// and if so, insert the ads into the list.
mNativeAds.add(unifiedNativeAd);
if (!adLoader.isLoading()) {
insertAdsInMenuItems();
}
}
}).withAdListener(
new AdListener() {
#Override
public void onAdFailedToLoad(int errorCode) {
// A native ad failed to load, check if the ad loader has finished loading
// and if so, insert the ads into the list.
Log.e("MainActivity", "The previous native ad failed to load. Attempting to"
+ " load another.");
if (!adLoader.isLoading()) {
insertAdsInMenuItems();
}
}
}).build();
// Load the Native ads.
adLoader.loadAds(new AdRequest.Builder().build(), NUMBER_OF_ADS);
}
private void insertAdsInMenuItems() {
if (mNativeAds.size() <= 0) {
return;
}
int offset = 3;
for (UnifiedNativeAd ad : mNativeAds) {
if (index<itemDatas.size()) {
itemDatas.add(index, ad);
}
index = index + offset;
}
//index = itemDatas.size() - 3;
if (runshowMovieOnce){
showMoviesList();
}
runshowMovieOnce = false;
}
private void showMoviesList() {
movieLists.setLayoutManager(layoutManager);
movieListAdapter = new MovieListAdapter(movieLists,MovieLists.this,itemDatas);
movieLists.setHasFixedSize(true);
movieLists.setAdapter(movieListAdapter);
movieListAdapter.setLoadMore(new ILoadMore() {
#Override
public void onLoadMore() {
itemDatas.add(null);
movieLists.post(new Runnable() {
public void run() {
movieListAdapter.notifyItemInserted(itemDatas.size() - 1);
Log.d("load position insert", ""+(itemDatas.size()-1));
}
});
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
itemDatas.remove(itemDatas.size()-1);
movieListAdapter.notifyItemRemoved(itemDatas.size() - 1);
Log.d("load position rem", ""+(itemDatas.size()-1));
int previousListSize = itemDatas.size();
Log.d("previousListSize",""+previousListSize);
loadMovies();
int newListSize = itemDatas.size();
Log.d("newListSize",""+newListSize);
movieListAdapter.notifyDataSetChanged();
movieListAdapter.setLoaded();
}
},5000);
}
});
}
private void loadMovies() {
FirebaseFirestore mFirestore = FirebaseFirestore.getInstance();
CollectionReference mPostsCollection = mFirestore.collection(catName).document(docId).collection("listItem");
//Defining Query cursors
if (lastVisible == null){
mQuery = mPostsCollection.orderBy("pr", Query.Direction.ASCENDING)
.limit(6);
}else {
mQuery = mPostsCollection.orderBy("pr", Query.Direction.ASCENDING)
.startAfter(lastVisible)
.limit(6);
}
mQuery.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
for (QueryDocumentSnapshot documentSnapshot : documentSnapshots){
ItemData itemData = documentSnapshot.toObject(ItemData.class);
itemDatas.add(itemData);
Log.d("movie name",itemData.getVn());
}
// Get the last visible document
if (documentSnapshots.size() > 0)
lastVisible = documentSnapshots.getDocuments()
.get(documentSnapshots.size() -1);
onFirebaseLoadSuccess(itemDatas);
}
});
}
}
MovieListAdapter.java
public class MovieListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<Object> itemDatas;
Context context;
String urlStart = "https://i1.ytimg.com/vi/";
String urlLast = "/mqdefault.jpg";
private static final int MOVIE_ITEM_VIEW_TYPE = 0;
private static final int UNIFIED_NATIVE_AD_VIEW_TYPE = 1;
private final int VIEW_TYPE_LOADING = 2;
boolean isLoading;
ILoadMore loadMore;
int visibleThreshold ;
int lastVisibleItem,totalItemCount;
View itemV;
public MovieListAdapter(RecyclerView recyclerView, Context context, #NonNull List<Object> itemData) {
this.itemDatas = itemData;
this.context = context;
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager)recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
visibleThreshold = linearLayoutManager.getChildCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && (visibleThreshold + lastVisibleItem) >= totalItemCount){
if (loadMore != null){
loadMore.onLoadMore();
isLoading = true;
}
}
}
});
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE:
View unifiedNativeLayoutView = LayoutInflater.from(
parent.getContext()).inflate(R.layout.ad_unified,
parent, false);
return new UnifiedNativeAdViewHolder(unifiedNativeLayoutView);
case VIEW_TYPE_LOADING:
View viewL = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
return new LoadingViewHolder(viewL);
case MOVIE_ITEM_VIEW_TYPE:
// Fall through.
default: {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_movie_list, parent, false);
return new MovieListAdapter.MovieListsMyViewHolder(view);
}
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder myViewHolder, int i) {
//int viewType = getItemViewType(i);
if (myViewHolder instanceof MovieListAdapter.MovieListsMyViewHolder){
MovieListsMyViewHolder movieListsMyViewHolder = (MovieListsMyViewHolder) myViewHolder;
ItemData model = (ItemData)itemDatas.get(i);
movieListsMyViewHolder.txt_item_title.setText(model.getVn());
String urlImage = urlStart + model.getVi() + urlLast;
Log.e("urlImage", urlImage);
Glide.with(context)
.load(urlImage)
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.into(movieListsMyViewHolder.img_title);
movieListsMyViewHolder.setiItemClickListener(new IItemClickListener() {
#Override
public void onItemclickListener(View view, int position) {
ItemData itemData = new ItemData(model.getVi(),model.getVn(),model.getVd(),model.getPr(),model.getTc(),model.getVvc());
Log.d("Tmdb ID",String.valueOf(model.getTc()));
MovieDetails.startActivity(context, ((MovieListsMyViewHolder) myViewHolder).transformationLayout, itemData);
}
});
}else if (myViewHolder instanceof UnifiedNativeAdViewHolder){
Object recyclerViewItems = itemDatas.get(i);
UnifiedNativeAd nativeAd = (UnifiedNativeAd) recyclerViewItems;
populateNativeAdView(nativeAd, ((UnifiedNativeAdViewHolder) myViewHolder).getAdView());
}else if (myViewHolder instanceof LoadingViewHolder){
LoadingViewHolder loadingViewHolder = (LoadingViewHolder)myViewHolder;
loadingViewHolder.progressBar.setIndeterminate(true);
}
}
#Override
public int getItemCount() {
return itemDatas.size();
}
public class MovieListsMyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
AppCompatTextView txt_item_title;
ImageView img_title;
IItemClickListener iItemClickListener;
CardView itemCardView;
TransformationLayout transformationLayout;
public void setiItemClickListener(IItemClickListener iItemClickListener) {
this.iItemClickListener = iItemClickListener;
}
public MovieListsMyViewHolder(#NonNull View itemView) {
super(itemView);
itemV = itemView;
txt_item_title = (AppCompatTextView) itemView.findViewById(R.id.item_movie_Title);
img_title = (ImageView)itemView.findViewById(R.id.item_movie_Image);
//itemCardView = (CardView)itemView.findViewById(R.id.item_cardview);
//itemCardView.setOnClickListener(this);
transformationLayout = (TransformationLayout)itemView.findViewById(R.id.transformationMovieLayout);
transformationLayout.setOnClickListener(this);
}
#Override
public void onClick(View view){
iItemClickListener.onItemclickListener(view,getAdapterPosition());
}
}
private void populateNativeAdView(UnifiedNativeAd nativeAd,
UnifiedNativeAdView adView) {
// Some assets are guaranteed to be in every UnifiedNativeAd.
((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
((TextView) adView.getBodyView()).setText(nativeAd.getBody());
((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
NativeAd.Image icon = nativeAd.getIcon();
if (icon == null) {
adView.getIconView().setVisibility(View.INVISIBLE);
} else {
((ImageView) adView.getIconView()).setImageDrawable(icon.getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (nativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
} else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView) adView.getPriceView()).setText(nativeAd.getPrice());
}
if (nativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
} else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView) adView.getStoreView()).setText(nativeAd.getStore());
}
if (nativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
} else {
((RatingBar) adView.getStarRatingView())
.setRating(nativeAd.getStarRating().floatValue());
adView.getStarRatingView().setVisibility(View.VISIBLE);
}
if (nativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
} else {
((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser());
adView.getAdvertiserView().setVisibility(View.VISIBLE);
}
// Assign native ad object to the native view.
adView.setNativeAd(nativeAd);
}
#Override
public int getItemViewType(int position) {
//DocumentSnapshot itemData = getItem(position);
Object recyclerViewItem = itemDatas.get(position);
if (recyclerViewItem instanceof UnifiedNativeAd) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}else if (itemDatas.get(position) == null){
return VIEW_TYPE_LOADING;
}else {
return MOVIE_ITEM_VIEW_TYPE;
}
}
public void setLoadMore(ILoadMore loadMore){
this.loadMore = loadMore;
}
private class LoadingViewHolder extends RecyclerView.ViewHolder {
ProgressBar progressBar;
public LoadingViewHolder(#NonNull View itemView) {
super(itemView);
progressBar = itemView.findViewById(R.id.progressBar);
}
}
public void setLoaded(){
isLoading = false;
}
}
The short term answer: There's a lot going on in your code. If you just want to remove the loadingProgress, add a void viewsLoaded() method inside your LoadingViewHolder with progressbar.setVisibility(View.GONE), and inside onBindViewHolder method, on the if(myViewHolder instanceof LoadingViewHolder), call the viewsLoaded() method if viewHolder is not positioned as the last one on the Adapter.
The long term answer: If you want to enhance the performance, read about how to use DiffUtil.ItemCallback<> instead of using notifydatasetchanged(), which is used for simple small datasets. You have a list of objectData on your activity and another one on your adapter, read about using LiveData, databinding and MVVM pattern.
i suggest first of all you should get The linearLayoutManager inside The Scroll View Listener.
another suggestion is to not call movieListAdapter.notifyItemInserted(itemDatas.size() - 1); Async inside the Runnable because after you add a null into list The Adapter should be notify:
itemsData.add(null);
movieListAdapter.notifyItemInserted(itemsData.size() - 1);
another is in the line: itemDatas.remove(itemDatas.size()-1)
after you remove the item from list, The List size will decrease by one.
so this would be correct:
movieListAdapter.notifyItemRemoved(itemDatas.size());
i Hope these would help.
I am currently working on an app, that finds all MP3s on a users phone and then puts them into a list. This works very fine and is very quick, even with many songs. Now I populate a new list with an object for each item of the list to then display it inside my recyclerview. The problem is, that I have 700+ songs on my phone and this blocks the UI thread quite some time.
Now, I want to use the recyclerview to not load all items from the list into the objects all at once but rather only when they are about to be displayed - but I have NO clue over how to do this. Right now, all objects are build and then displayed in a very long scrollview from the recyclerview after the UI thread has been blocked for a good 30 seconds. Can please anyone help me? Here is my code:
namespace Media_Player
{
[Activity(Label = "Media_Player", MainLauncher = true)]
public class MainActivity : Activity
{
static public MediaPlayer mediaPlayer;
List<MP3object> mp3;
MediaMetadataRetriever reader;
public static Button btn_StartOrPause, btn_Stop;
public static TextView txt_CurrentSong;
public static bool stopIsActive = false, firstStart = true;
public static Android.Net.Uri CurrentActiveSongUri;
RecyclerView mRecyclerView;
RecyclerView.LayoutManager mLayoutManager;
PhotoAlbumAdapter mAdapter;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.test);
reader = new MediaMetadataRetriever();
PopulateMP3List(ReturnPlayableMp3(true));
mediaPlayer = new MediaPlayer();
InitRecView();
}
private void InitRecView()
{
// Instantiate the adapter and pass in its data source:
mAdapter = new PhotoAlbumAdapter(mp3);
// Get our RecyclerView layout:
mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
// Plug the adapter into the RecyclerView:
mRecyclerView.SetAdapter(mAdapter);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.SetLayoutManager(mLayoutManager);
}
private void PopulateMP3List(List<string> content)
{
mp3 = new List<MP3object>();
foreach (string obj in content)
{
WriteMetaDataToFileList(obj);
}
}
void WriteMetaDataToFileList(string obj)
{
reader.SetDataSource(obj);
//Write Mp3 as object to global list
MP3object ob = new MP3object();
{
if(reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle) != null)
{
ob.SongName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle);
}
else
{
ob.SongName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist) != null)
{
ob.ArtistName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist);
}
else
{
ob.ArtistName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum) != null)
{
ob.AlbumName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum);
}
else
{
ob.AlbumName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != null)
{
ob.Year = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear);
}
else
{
ob.Year = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != null)
{
ob.Year = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear);
}
else
{
ob.Year = Resources.GetString(Resource.String.Unknown);
}
ob.Mp3Uri = obj; // can never be unknown!
ob.DurationInSec = int.Parse(reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyDuration)) / 1000; // can never be unknown, div by 1000 to get sec not millis
}
mp3.Add(ob);
}
public List<string> ReturnPlayableMp3(bool sdCard)
{
List<string> res = new List<string>();
string phyle;
string path1 = null;
if(sdCard) // get mp3 from SD card
{
string baseFolderPath = "";
try
{
bool getSDPath = true;
Context context = Application.Context;
Java.IO.File[] dirs = context.GetExternalFilesDirs(null);
foreach (Java.IO.File folder in dirs)
{
bool IsRemovable = Android.OS.Environment.InvokeIsExternalStorageRemovable(folder);
bool IsEmulated = Android.OS.Environment.InvokeIsExternalStorageEmulated(folder);
if (getSDPath ? IsRemovable && !IsEmulated : !IsRemovable && IsEmulated)
baseFolderPath = folder.Path;
}
}
catch (Exception ex)
{
Console.WriteLine("GetBaseFolderPath caused the following exception: {0}", ex);
}
string xy = baseFolderPath.Remove(18); // This is result after this, but this hard coded solution could be a problem on different phones.: "/storage/05B6-2226/Android/data/Media_Player.Media_Player/files"
path1 = xy;
// path to SD card and MUSIC "/storage/05B6-2226/"
}
else // get Mp3 from internal storage
{
path1 = Android.OS.Environment.ExternalStorageDirectory.ToString();
}
var mp3Files = Directory.EnumerateFiles(path1, "*.mp3", SearchOption.AllDirectories);
foreach (string currentFile in mp3Files)
{
phyle = currentFile;
res.Add(phyle);
}
return res;
}
}
public class PhotoViewHolder : RecyclerView.ViewHolder
{
public ImageView Image { get; private set; }
public TextView Caption { get; private set; }
public PhotoViewHolder(View itemView) : base(itemView)
{
// Locate and cache view references:
Image = itemView.FindViewById<ImageView>(Resource.Id.imageView);
Caption = itemView.FindViewById<TextView>(Resource.Id.textView);
}
}
public class PhotoAlbumAdapter : RecyclerView.Adapter
{
public List<MP3object> mp3;
public PhotoAlbumAdapter(List<MP3object> mp3)
{
this.mp3 = mp3;
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.lay, parent, false);
PhotoViewHolder vh = new PhotoViewHolder(itemView);
return vh;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
PhotoViewHolder vh = holder as PhotoViewHolder;
vh.Caption.Text = mp3[position].SongName;
}
public override int ItemCount
{
get { return mp3.Count(); }
}
}
}
So getting the list of strings with the locations of the Mp3 works very quickly, but then "WriteMetaDataToFileList(obj)" kicks in, comming from "PopulateMP3List(List content)" and this is what takes so long. What I think I need is for the recyclerview to only build the first 20 objects, and when the user starts scrolling, builds the next 20 objects and attaches them to list for them to also be scrolled. Please help me out here :)
Here is an abstract class:
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
private LinearLayoutManager linearLayoutManager;
protected PaginationScrollListener(LinearLayoutManager linearLayoutManager) {
this.linearLayoutManager = linearLayoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = linearLayoutManager.getChildCount();
int totalItemCount = linearLayoutManager.getItemCount();
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0) {
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
public abstract boolean isLastPage();
public abstract boolean isLoading();
}
and In your adapter you must follow this pattern:
public class ConsultancyAdapter extends RecyclerView.Adapter<ConsultancyAdapter.ConsultancyVH> {
private static final int ITEM = 0;
private static final int LOADING = 1;
private boolean isLoadingAdded = false;
public ConsultancyAdapter(List<Consultancy> consultancies, ConsultancyAdapterListener listener) {
}
#NonNull
#Override
public ConsultancyVH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case ITEM:
viewHolder = getViewHolder(parent, layoutInflater);
break;
case LOADING:
View v2 = layoutInflater.inflate(R.layout.item_progress, parent, false);
viewHolder = new ConsultancyVH(v2);
break;
}
return (ConsultancyVH) viewHolder;
}
#NonNull
private RecyclerView.ViewHolder getViewHolder(ViewGroup parent, LayoutInflater inflater) {
RecyclerView.ViewHolder viewHolder;
View v1 = inflater.inflate(R.layout.item_consultancy, parent, false);
viewHolder = new ConsultancyVH(v1);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ConsultancyVH holder, int position) {
Consultancy consultancy = consultancies.get(position);
switch (getItemViewType(position)) {
case ITEM:
ConsultancyVH mySingeCounseller = holder;
holder.title.setText(consultancy.getTitle()); // set cardTitle
holder.fieldArea.setText(consultancy.getField_filedoctorskills());
break;
case LOADING:
break;
}
}
#Override
public int getItemCount() {
return consultancies.size();
}
#Override
public int getItemViewType(int position) {
return (position == consultancies.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
}
public void add(Consultancy mc) {
consultancies.add(mc);
notifyItemInserted(consultancies.size() - 1);
}
public void addAll(List<Consultancy> mcList) {
for (Consultancy mc : mcList) {
add(mc);
}
}
public void remove(Consultancy city) {
int position = consultancies.indexOf(city);
if (position > -1) {
consultancies.remove(position);
notifyItemRemoved(position);
}
}
public Consultancy getItem(int position) {
return consultancies.get(position);
}
public void clear() {
isLoadingAdded = false;
while (getItemCount() > 0) {
remove(getItem(0));
}
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public void addLoadingFooter() {
isLoadingAdded = true;
add(new Consultancy());
}
public void removeLoadingFooter() {
isLoadingAdded = false;
int position = consultancies.size() - 1;
Consultancy item = getItem(position);
if (item != null) {
consultancies.remove(position);
notifyItemRemoved(position);
}
}
public interface ConsultancyAdapterListener {
void onCaseClicked(int position, String nid, String fieldArea, String title);
}
protected class ConsultancyVH extends RecyclerView.ViewHolder {
private TextView title, fieldArea;
private CircleImageView iconProfile;
private MaterialRippleLayout caseButtonRipple;
public ConsultancyVH(View itemView) {
super(itemView);
caseButtonRipple = itemView.findViewById(R.id.case_button_ripple);
this.title = itemView.findViewById(R.id.docName);
this.fieldArea = itemView.findViewById(R.id.fieldArea);
this.iconProfile = itemView.findViewById(R.id.icon_profile);
}
}
}
and in your activity:
private void setScrollListener() {
recyclerView.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
loadNextPage();
}
#Override
public boolean isLastPage() {
return isLastPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
loadFirstPage();
}
and in my loadFirstPage i talk to a API and you need some your code:
private void loadFirstPage() {
CallData().enqueue(new DefaultRetrofitCallback<List<Consultancy>>() {
#Override
protected void onFailure(Throwable t) {
super.onFailure(t);
}
#Override
protected void onSuccess(List<Consultancy> response) {
swipeRefreshLayout.setRefreshing(false);
dataList = response;
adapter.addAll(dataList);
recyclerView.setAdapter(adapter);
if (!checkLast(response)) adapter.addLoadingFooter();
else isLastPage = true;
}
#Override
protected void onOtherStatus(Response<List<Consultancy>> response) {
super.onOtherStatus(response);
}
#Override
protected void always() {
super.always();
}
});
}
and loadNextPage:
private void loadNextPage() {
CallData().enqueue(new DefaultRetrofitCallback<List<Consultancy>>() {
#Override
protected void onFailure(Throwable t) {
super.onFailure(t);
}
#Override
protected void onSuccess(List<Consultancy> response) {
swipeRefreshLayout.setRefreshing(false);
adapter.removeLoadingFooter();
isLoading = false;
swipeRefreshLayout.setRefreshing(false);
adapter.addAll(response);
if (!checkLast(response)) adapter.addLoadingFooter();
else isLastPage = true;
}
#Override
protected void onOtherStatus(Response<List<Consultancy>> response) {
super.onOtherStatus(response);
}
#Override
protected void always() {
super.always();
}
});
}
Hi all I am now integrating youtube API in my app where I have to parse the list of video id's from API then I have to show the videos in a list view when we click on it the video has to be played Until this works fine
There after when the user selects the full-screen mode that is landscape mode I get some errors
public final class VideoGallery extends Fragment implements OnFullscreenListener {
public static Context context;
public static final int ANIMATION_DURATION_MILLIS = 300;
public static final int LANDSCAPE_VIDEO_PADDING_DP = 5;
public final int RECOVERY_DIALOG_REQUEST = 1;
public static VideoListFragment listFragment;
public static VideoFragment videoFragment;
View videoBox;
View view;
View closeButton;
boolean isFullscreen;
public static YouTubePlayer.OnFullscreenListener onFullscreenListener;
static boolean back = false;
FrameLayout relativeLayout;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getContext();
onFullscreenListener = this;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
relativeLayout = new FrameLayout(getActivity());
inflater.inflate(R.layout.video_list_demo, relativeLayout, true);
MainActivity.headerText.setText("Video Gallery");
listFragment = (VideoGallery.VideoListFragment) getChildFragmentManager().findFragmentById(R.id.list_fragment);
videoFragment = (VideoGallery.VideoFragment) getActivity().getFragmentManager().findFragmentById(R.id.video_fragment_container);
videoBox = relativeLayout.findViewById(R.id.video_box);
closeButton = relativeLayout.findViewById(R.id.close_button);
closeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
listFragment.getListView().clearChoices();
listFragment.getListView().requestLayout();
videoFragment.pause();
ViewPropertyAnimator animator = videoBox.animate()
.translationYBy(videoBox.getHeight())
.setDuration(ANIMATION_DURATION_MILLIS);
runOnAnimationEnd(animator, new Runnable() {
#Override
public void run() {
videoBox.setVisibility(View.INVISIBLE);
}
});
}
});
videoBox.setVisibility(View.INVISIBLE);
layout();
checkYouTubeApi();
return relativeLayout;
}
private void checkYouTubeApi() {
YouTubeInitializationResult errorReason =
YouTubeApiServiceUtil.isYouTubeApiServiceAvailable(context);
if (errorReason.isUserRecoverableError()) {
errorReason.getErrorDialog(getActivity(), RECOVERY_DIALOG_REQUEST).show();
} else if (errorReason != YouTubeInitializationResult.SUCCESS) {
String errorMessage =
String.format(getString(R.string.error_player), errorReason.toString());
Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RECOVERY_DIALOG_REQUEST) {
// Recreate the activity if user performed a recovery action
getActivity().recreate();
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
layout();
}
#Override
public void onFullscreen(boolean isFullscreen) {
this.isFullscreen = isFullscreen;
layout();
}
private void layout() {
boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
listFragment.getView().setVisibility(isFullscreen ? View.GONE : View.VISIBLE);
listFragment.setLabelVisibility(isPortrait);
closeButton.setVisibility(isPortrait ? View.VISIBLE : View.GONE);
if (isFullscreen) {
videoBox.setTranslationY(0); // Reset any translation that was applied in portrait.
setLayoutSize(videoFragment.getView(), MATCH_PARENT, MATCH_PARENT);
setLayoutSizeAndGravity(videoBox, MATCH_PARENT, MATCH_PARENT, Gravity.TOP | Gravity.LEFT);
} else if (isPortrait) {
setLayoutSize(listFragment.getView(), MATCH_PARENT, MATCH_PARENT);
setLayoutSize(videoFragment.getView(), MATCH_PARENT, WRAP_CONTENT);
setLayoutSizeAndGravity(videoBox, MATCH_PARENT, WRAP_CONTENT, Gravity.BOTTOM);
} else {
videoBox.setTranslationY(0); // Reset any translation that was applied in portrait.
int screenWidth = dpToPx(getResources().getConfiguration().screenWidthDp);
setLayoutSize(listFragment.getView(), screenWidth / 4, MATCH_PARENT);
int videoWidth = screenWidth - screenWidth / 4;
setLayoutSize(videoFragment.getView(), videoWidth, WRAP_CONTENT);
setLayoutSizeAndGravity(videoBox, videoWidth, WRAP_CONTENT,
Gravity.RIGHT | Gravity.CENTER_VERTICAL);
}
}
#TargetApi(16)
private void runOnAnimationEnd(ViewPropertyAnimator animator, final Runnable runnable) {
if (Build.VERSION.SDK_INT >= 16) {
animator.withEndAction(runnable);
} else {
animator.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
runnable.run();
}
});
}
}
public static final class VideoListFragment extends ListFragment {
private static List<VideoEntry> VIDEO_LIST;
private PageAdapter adapter;
private View videoBox;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
callApi();
}
private void callApi() {
new DoGet(this, "videogallery").execute(WebserviceEndpoints.VIDEO_GALLERY);
}
public void handleVideoGalleryResponse(String response) {
try {
List<VideoEntry> list = new ArrayList<VideoEntry>();
if (response.length() > 0) {
JSONObject jsonObject = new JSONObject(response);
JSONArray array = jsonObject.optJSONArray("items");
for (int i = 0; i < array.length(); i++) {
JSONObject data = array.optJSONObject(i);
JSONObject obj = data.optJSONObject("id");
JSONObject obj1 = data.optJSONObject("snippet");
String videoId = obj.optString("videoId");
String videoTitle = obj1.getString("title");
if (!videoId.equals("")) {
list.add(new VideoEntry(videoTitle, videoId));
}
}
VIDEO_LIST = Collections.unmodifiableList(list);
adapter = new PageAdapter(getActivity(), VIDEO_LIST);
setListAdapter(adapter);
}
} catch (Exception e) {
Log.d("", "handleResponse: ", e);
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
videoBox = getActivity().findViewById(R.id.video_box);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
String videoId = VIDEO_LIST.get(position).videoId;
back = false;
VideoFragment videoFragment = (VideoFragment) getActivity().getFragmentManager().findFragmentById(R.id.video_fragment_container);
videoFragment.setVideoId(videoId);
if (videoBox.getVisibility() != View.VISIBLE) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
videoBox.setTranslationY(videoBox.getHeight());
}
videoBox.setVisibility(View.VISIBLE);
}
if (videoBox.getTranslationY() > 0) {
videoBox.animate().translationY(0).setDuration(ANIMATION_DURATION_MILLIS);
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
VideoFragment f = (VideoFragment) getActivity().getFragmentManager().findFragmentById(R.id.video_fragment_container);
if (f != null)
getActivity().getFragmentManager().beginTransaction().remove(f).commit();
if (adapter != null)
adapter.releaseLoaders();
}
public void setLabelVisibility(boolean visible) {
if (adapter != null)
adapter.setLabelVisibility(visible);
}
}
private static final class PageAdapter extends BaseAdapter {
private final List<VideoEntry> entries;
private final List<View> entryViews;
private final Map<YouTubeThumbnailView, YouTubeThumbnailLoader> thumbnailViewToLoaderMap;
private final LayoutInflater inflater;
private final ThumbnailListener thumbnailListener;
private boolean labelsVisible;
public PageAdapter(Context context, List<VideoEntry> entries) {
this.entries = entries;
entryViews = new ArrayList<View>();
thumbnailViewToLoaderMap = new HashMap<YouTubeThumbnailView, YouTubeThumbnailLoader>();
inflater = LayoutInflater.from(context);
thumbnailListener = new ThumbnailListener();
labelsVisible = true;
}
public void releaseLoaders() {
for (YouTubeThumbnailLoader loader : thumbnailViewToLoaderMap.values()) {
loader.release();
}
}
public void setLabelVisibility(boolean visible) {
labelsVisible = visible;
for (View view : entryViews) {
view.findViewById(R.id.text).setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
#Override
public int getCount() {
return entries.size();
}
#Override
public VideoEntry getItem(int position) {
return entries.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
VideoEntry entry = entries.get(position);
// There are three cases here
if (view == null) {
// 1) The view has not yet been created - we need to initialize the YouTubeThumbnailView.
view = inflater.inflate(R.layout.video_list_item, parent, false);
YouTubeThumbnailView thumbnail = (YouTubeThumbnailView) view.findViewById(R.id.thumbnail);
thumbnail.setTag(entry.videoId);
thumbnail.initialize("AIzaSyB8BPeIbu5T1-flG4tpEdrHDPI3Rk2VTDY", thumbnailListener);
} else {
YouTubeThumbnailView thumbnail = (YouTubeThumbnailView) view.findViewById(R.id.thumbnail);
YouTubeThumbnailLoader loader = thumbnailViewToLoaderMap.get(thumbnail);
if (loader == null) {
// 2) The view is already created, and is currently being initialized. We store the
// current videoId in the tag.
thumbnail.setTag(entry.videoId);
} else {
// 3) The view is already created and already initialized. Simply set the right videoId
// on the loader.
thumbnail.setImageResource(R.drawable.no_thumbnail);
loader.setVideo(entry.videoId);
}
}
TextView label = ((TextView) view.findViewById(R.id.text));
label.setText(entry.text);
label.setVisibility(labelsVisible ? View.VISIBLE : View.GONE);
return view;
}
private final class ThumbnailListener implements
YouTubeThumbnailView.OnInitializedListener,
YouTubeThumbnailLoader.OnThumbnailLoadedListener {
#Override
public void onInitializationSuccess(
YouTubeThumbnailView view, YouTubeThumbnailLoader loader) {
loader.setOnThumbnailLoadedListener(this);
thumbnailViewToLoaderMap.put(view, loader);
view.setImageResource(R.drawable.no_thumbnail);
String videoId = (String) view.getTag();
loader.setVideo(videoId);
}
#Override
public void onInitializationFailure(
YouTubeThumbnailView view, YouTubeInitializationResult loader) {
view.setImageResource(R.drawable.no_thumbnail);
}
#Override
public void onThumbnailLoaded(YouTubeThumbnailView view, String videoId) {
}
#Override
public void onThumbnailError(YouTubeThumbnailView view, YouTubeThumbnailLoader.ErrorReason errorReason) {
view.setImageResource(R.drawable.no_thumbnail);
}
}
}
public static final class VideoFragment extends YouTubePlayerFragment
implements YouTubePlayer.OnInitializedListener {
private YouTubePlayer player;
private String videoId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialize("AIzaSyB8BPeIbu5T1-flG4tpEdrHDPI3Rk2VTDY", this);
}
#Override
public void onDestroy() {
if (player != null) {
player.release();
}
super.onDestroy();
}
public void setVideoId(String videoId) {
if (videoId != null && !videoId.equals(this.videoId)) {
this.videoId = videoId;
if (player != null) {
player.cueVideo(videoId);
}
}
}
public void pause() {
if (player != null) {
player.pause();
}
}
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player, boolean restored) {
this.player = player;
player.addFullscreenControlFlag(YouTubePlayer.FULLSCREEN_FLAG_CUSTOM_LAYOUT);
player.setOnFullscreenListener(onFullscreenListener);
if (!restored && videoId != null) {
player.cueVideo(videoId);
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult result) {
this.player = null;
}
}
private static final class VideoEntry {
private final String text;
private final String videoId;
public VideoEntry(String text, String videoId) {
this.text = text;
this.videoId = videoId;
}
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}
private static void setLayoutSize(View view, int width, int height) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = width;
params.height = height;
view.setLayoutParams(params);
}
private static void setLayoutSizeAndGravity(View view, int width, int height, int gravity) {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
params.width = width;
params.height = height;
params.gravity = gravity;
view.setLayoutParams(params);
}
}
This is my fragment
The layout file is
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/list_fragment"
class="com.mycristoparish.Fragments.VideoGallery$VideoListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize" />
<LinearLayout
android:id="#+id/video_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<ImageButton
android:id="#+id/close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="#android:drawable/btn_dialog" />
<fragment
android:id="#+id/video_fragment_container"
class="com.mycristoparish.Fragments.VideoGallery$VideoFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</merge>
When I try to play the video in full-screen mode I get the following error
java.lang.IllegalArgumentException: Binary XML file line #42: Duplicate id 0x7f0d0117, tag null, or parent id 0x7f0d0115 with another fragment for com.mycristoparish.Fragments.VideoGallery$VideoFragment
Please guide me how can I move further.
Thanks in advance.
The problem is that you are inflating a layout with a fragment inside a fragment.You cannot do it and so you get the error.However you can dynamically add them.
More details
Note: You cannot inflate a layout into a fragment when that layout includes a . Nested fragments are only supported when added to a fragment dynamically.
I am working on integrating some video into one of my Activities. It plays fine, but I have to click play for it to actually play. I searched the forums and the developer site and cannot find anything relating to autoplay or autostart. Any suggestions?
MainActivity.java
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
VideoView mVideoView = (VideoView) findViewById(R.id.videoView);
mVideoView.setVideoPath("file:///android_asset/video1.MP4");
mVideoView.setMediaController(new MediaController(this));
mVideoView.seekTo(1);
//mVideoView.requestFocus();
}
My contain_main.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment"
android:name="imaker.MediaAutoPlay.MainActivityFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:layout="#layout/fragment_main">
<VideoView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/videoView"
android:layout_height="fill_parent"
android:layout_width="fill_parent" />
</fragment>
mVideoView.start() for start video
to start video
or you can also use
mVideoView.seekTo(1)
Auto Play Video like same as Facebook Youtube popular apps Popular Apps
Build.Gradle app module
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
implementation 'org.jsoup:jsoup:1.10.3'
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
Auto Play
public class ExoPlayerRecyclerView extends RecyclerView {
private static final String TAG = "ExoPlayerRecyclerView";
private static final String AppName = "Android ExoPlayer";
/**
* PlayerViewHolder UI component
* Watch PlayerViewHolder class
*/
private ImageView mediaCoverImage, volumeControl;
private ProgressBar progressBar;
private View viewHolderParent;
private FrameLayout mediaContainer;
private PlayerView videoSurfaceView;
private SimpleExoPlayer videoPlayer;
/**
* variable declaration
*/
// Media List
private ArrayList<MediaObject> mediaObjects = new ArrayList<>();
private int videoSurfaceDefaultHeight = 0;
private int screenDefaultHeight = 0;
private Context context;
private int playPosition = -1;
private boolean isVideoViewAdded;
private RequestManager requestManager;
// controlling volume state
private VolumeState volumeState;
private OnClickListener videoViewClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
toggleVolume();
}
};
public ExoPlayerRecyclerView(#NonNull Context context) {
super(context);
init(context);
}
public ExoPlayerRecyclerView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
this.context = context.getApplicationContext();
Display display = ((WindowManager) Objects.requireNonNull(
getContext().getSystemService(Context.WINDOW_SERVICE))).getDefaultDisplay();
Point point = new Point();
display.getSize(point);
videoSurfaceDefaultHeight = point.x;
screenDefaultHeight = point.y;
videoSurfaceView = new PlayerView(this.context);
videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
//Create the player using ExoPlayerFactory
videoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
// Disable Player Control
videoSurfaceView.setUseController(false);
// Bind the player to the view.
videoSurfaceView.setPlayer(videoPlayer);
// Turn on Volume
setVolumeControl(VolumeState.ON);
addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mediaCoverImage != null) {
// show the old thumbnail
mediaCoverImage.setVisibility(VISIBLE);
}
// There's a special case when the end of the list has been reached.
// Need to handle that with this bit of logic
if (!recyclerView.canScrollVertically(1)) {
playVideo(true);
} else {
playVideo(false);
}
}
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(#NonNull View view) {
}
#Override
public void onChildViewDetachedFromWindow(#NonNull View view) {
if (viewHolderParent != null && viewHolderParent.equals(view)) {
resetVideoView();
}
}
});
videoPlayer.addListener(new Player.EventListener() {
#Override
public void onTimelineChanged(Timeline timeline, #Nullable Object manifest, int reason) {
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups,
TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
switch (playbackState) {
case Player.STATE_BUFFERING:
Log.e(TAG, "onPlayerStateChanged: Buffering video.");
if (progressBar != null) {
progressBar.setVisibility(VISIBLE);
}
break;
case Player.STATE_ENDED:
Log.d(TAG, "onPlayerStateChanged: Video ended.");
videoPlayer.seekTo(0);
break;
case Player.STATE_IDLE:
break;
case Player.STATE_READY:
Log.e(TAG, "onPlayerStateChanged: Ready to play.");
if (progressBar != null) {
progressBar.setVisibility(GONE);
}
if (!isVideoViewAdded) {
addVideoView();
}
break;
default:
break;
}
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
});
}
public void playVideo(boolean isEndOfList) {
int targetPosition;
if (!isEndOfList) {
int startPosition = ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
// if there is more than 2 list-items on the screen, set the difference to be 1
if (endPosition - startPosition > 1) {
endPosition = startPosition + 1;
}
// something is wrong. return.
if (startPosition < 0 || endPosition < 0) {
return;
}
// if there is more than 1 list-item on the screen
if (startPosition != endPosition) {
int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
targetPosition =
startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
} else {
targetPosition = startPosition;
}
} else {
targetPosition = mediaObjects.size() - 1;
}
Log.d(TAG, "playVideo: target position: " + targetPosition);
// video is already playing so return
if (targetPosition == playPosition) {
return;
}
// set the position of the list-item that is to be played
playPosition = targetPosition;
if (videoSurfaceView == null) {
return;
}
// remove any old surface views from previously playing videos
videoSurfaceView.setVisibility(INVISIBLE);
removeVideoView(videoSurfaceView);
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return;
}
PlayerViewHolder holder = (PlayerViewHolder) child.getTag();
if (holder == null) {
playPosition = -1;
return;
}
mediaCoverImage = holder.mediaCoverImage;
progressBar = holder.progressBar;
volumeControl = holder.volumeControl;
viewHolderParent = holder.itemView;
requestManager = holder.requestManager;
mediaContainer = holder.mediaContainer;
videoSurfaceView.setPlayer(videoPlayer);
viewHolderParent.setOnClickListener(videoViewClickListener);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
context, Util.getUserAgent(context, AppName));
String mediaUrl = mediaObjects.get(targetPosition).getUrl();
if (mediaUrl != null) {
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(mediaUrl));
videoPlayer.prepare(videoSource);
videoPlayer.setPlayWhenReady(true);
}
}
/**
* Returns the visible region of the video surface on the screen.
* if some is cut off, it will return less than the #videoSurfaceDefaultHeight
*/
private int getVisibleVideoSurfaceHeight(int playPosition) {
int at = playPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
Log.d(TAG, "getVisibleVideoSurfaceHeight: at: " + at);
View child = getChildAt(at);
if (child == null) {
return 0;
}
int[] location = new int[2];
child.getLocationInWindow(location);
if (location[1] < 0) {
return location[1] + videoSurfaceDefaultHeight;
} else {
return screenDefaultHeight - location[1];
}
}
// Remove the old player
private void removeVideoView(PlayerView videoView) {
ViewGroup parent = (ViewGroup) videoView.getParent();
if (parent == null) {
return;
}
int index = parent.indexOfChild(videoView);
if (index >= 0) {
parent.removeViewAt(index);
isVideoViewAdded = false;
viewHolderParent.setOnClickListener(null);
}
}
private void addVideoView() {
mediaContainer.addView(videoSurfaceView);
isVideoViewAdded = true;
videoSurfaceView.requestFocus();
videoSurfaceView.setVisibility(VISIBLE);
videoSurfaceView.setAlpha(1);
mediaCoverImage.setVisibility(GONE);
}
private void resetVideoView() {
if (isVideoViewAdded) {
removeVideoView(videoSurfaceView);
playPosition = -1;
videoSurfaceView.setVisibility(INVISIBLE);
mediaCoverImage.setVisibility(VISIBLE);
}
}
public void releasePlayer() {
if (videoPlayer != null) {
videoPlayer.release();
videoPlayer = null;
}
viewHolderParent = null;
}
public void onPausePlayer() {
if (videoPlayer != null) {
videoPlayer.stop(true);
}
}
private void toggleVolume() {
if (videoPlayer != null) {
if (volumeState == VolumeState.OFF) {
Log.d(TAG, "togglePlaybackState: enabling volume.");
setVolumeControl(VolumeState.ON);
} else if (volumeState == VolumeState.ON) {
Log.d(TAG, "togglePlaybackState: disabling volume.");
setVolumeControl(VolumeState.OFF);
}
}
}
//public void onRestartPlayer() {
// if (videoPlayer != null) {
// playVideo(true);
// }
//}
private void setVolumeControl(VolumeState state) {
volumeState = state;
if (state == VolumeState.OFF) {
videoPlayer.setVolume(0f);
animateVolumeControl();
} else if (state == VolumeState.ON) {
videoPlayer.setVolume(1f);
animateVolumeControl();
}
}
private void animateVolumeControl() {
if (volumeControl != null) {
volumeControl.bringToFront();
if (volumeState == VolumeState.OFF) {
requestManager.load(R.drawable.ic_volume_off)
.into(volumeControl);
} else if (volumeState == VolumeState.ON) {
requestManager.load(R.drawable.ic_volume_on)
.into(volumeControl);
}
volumeControl.animate().cancel();
volumeControl.setAlpha(1f);
volumeControl.animate()
.alpha(0f)
.setDuration(600).setStartDelay(1000);
}
}
public void setMediaObjects(ArrayList<MediaObject> mediaObjects) {
this.mediaObjects = mediaObjects;
}
/**
* Volume ENUM
*/
private enum VolumeState {
ON, OFF
}
}