My songadapter:
public class songAdapter extends RecyclerView.Adapter<songAdapter.ViewHolder> {
ArrayList<songInfo> songs;
Context context;
OnitemClickListener onitemClickListener;
public songAdapter(ArrayList<songInfo> songs, Context context) {
this.songs = songs;
this.context = context;
}
public interface OnitemClickListener{
void onItemclick(ViewHolder holder, View v, songInfo obj, int position);
}
public void setOnitemClickListener(OnitemClickListener onitemClickListener)
{
this.onitemClickListener=onitemClickListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView= LayoutInflater.from(context).inflate(R.layout.raw_songs,parent,false);
return new ViewHolder(mView);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final songInfo song=songs.get(position);
holder.songName.setText(song.songName);
holder.artistName.setText(song.artistName);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(onitemClickListener!=null)
{
onitemClickListener.onItemclick(holder,view,song,position);
}
}
});
}
#Override
public int getItemCount() {
return songs.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView songName,artistName;
public ViewHolder(View itemView) {
super(itemView);
songName=(TextView)itemView.findViewById(R.id.songname);
artistName=(TextView)itemView.findViewById(R.id.artistName);
}
}
}
this is my mainactivity
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
private ArrayList<songInfo> songs=new ArrayList<songInfo>();
songAdapter songAdapter;
MediaPlayer mediaPlayer;
SeekBar seekBar;
Cursor cursor;
private Handler myHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView=(RecyclerView)findViewById(R.id.recyclerView);
seekBar = (SeekBar) findViewById(R.id.seekBar);
songAdapter=new songAdapter(songs, this);
recyclerView.setAdapter(songAdapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
linearLayoutManager.getOrientation());
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addItemDecoration(dividerItemDecoration);
songAdapter.setOnitemClickListener(new songAdapter.OnitemClickListener() {
#Override
public void onItemclick(songAdapter.ViewHolder holder, View v, songInfo obj, int position) {
mediaPlayer.start();
System.gc();
int songPath = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
cursor.moveToPosition(position);
final String filename = cursor.getString(songPath);
if(mediaPlayer.isPlaying()){
mediaPlayer.reset();
mediaPlayer.start();
}else {
Runnable runnable = new Runnable() {
#Override
public void run() {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(filename);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
seekBar.setProgress(0);
seekBar.setMax(mediaPlayer.getDuration());
Log.d("Prog", "run: " + mediaPlayer.getDuration());
}
});
// b.setText("Stop");
}catch (Exception e){}
}
};
myHandler.postDelayed(runnable,100);
}
}
});
checkUserPermission();
Thread t = new runThread();
t.start();
}
public class runThread extends Thread {
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("Runwa", "run: " + 1);
if (mediaPlayer != null) {
seekBar.post(new Runnable() {
#Override
public void run() {
seekBar.setProgress(mediaPlayer.getCurrentPosition());
}
});
Log.d("Runwa", "run: " + mediaPlayer.getCurrentPosition());
}
}
}
}
private void checkUserPermission(){
if(Build.VERSION.SDK_INT>=23){
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},123);
return;
}
}
loadSongs();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode){
case 123:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
loadSongs();
}else{
Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show();
checkUserPermission();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void loadSongs(){
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Audio.Media.IS_MUSIC+"!=0";
cursor = getContentResolver().query(uri,null,selection,null,null);
if(cursor != null){
if(cursor.moveToFirst()){
do{
String name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
int songPath = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
songInfo s = new songInfo(name,artist,songPath);
songs.add(s);
}while (cursor.moveToNext());
}
// cursor.close();
songAdapter = new songAdapter(songs, MainActivity.this);
}
}
public void onDestroy() {
super.onDestroy();
cursor.close();
}
}
I fetched song from sd card and can see them on screen,but when i try to play those songs my app crashes with the following error.Attempt to invoke virtual method 'void android.media.MediaPlayer.start()' on a null object reference.
I dont know what to do please help.
This is my logcat
Process: com.example.murarilal.musicmania, PID: 26119
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.media.MediaPlayer.start()' on a null object reference at com.example.murarilal.musicmania.MainActivity$1.onItemclick(MainActivity.java:54)
at
com.example.murarilal.musicmania.songAdapter$1.onClick(songAdapter.java:54)**
The problem is that you are trying to access an null instance of MediaPlayer,
Attempt to invoke virtual method 'void
android.media.MediaPlayer.start()' on a null object reference.
so before execute mediaPlayer.start(); inside the method onClick() you need to initialize your MediaPlayer instance:
MediaPlayer mediaPlayer = new MediaPlayer();
for example:
private MediaPlayer mediaPlayer; //* Create variable type MediaPlayer .
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
...
mediaPlayer = new MediaPlayer(); //* Initialize MediaPlayer!
...
...
songAdapter.setOnitemClickListener(new songAdapter.OnitemClickListener() {
#Override
public void onItemclick(songAdapter.ViewHolder holder, View v, songInfo obj, int position) {
mediaPlayer.start(); //*Now you can use the MediaPlayer instance.
...
...
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());
I'm making an app that play mp3 file from phone storage using service. I have an activity that has a small view to interact with the songs and a fragment that contains a full media function to interact with the songs will be added in if I tap on the small view told. The problem is I cannot get data from service to pass in the fragment to display data or interact between fragment and service.
Here's my main activity 1st photo
Here's my fragment after touch the field on 1st photo fragment
My code in MainActivity
public class MainActivity extends AppCompatActivity {
private List<AudioModel> mainList;
private ListView mainListView;
private MusicAdapter adapter;
private int REQUEST_CODE_PERMISSION = 2703;
private MusicPlayerService musicPlayerService;
public static Intent intentService;
private boolean boundService = false;
public static TextView txtMainTen, txtMainTacGia;
private ImageButton btnMainPlay, btnMainNext;
private int vitri = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainList = new ArrayList<>();
if(intentService == null){
intentService = new Intent(this, MusicPlayerService.class);
bindService(intentService, serviceConnection, Context.BIND_AUTO_CREATE);
startService(intentService);
}
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_PERMISSION);
mainListView = findViewById(R.id.listSong);
adapter = new MusicAdapter(MainActivity.this, R.layout.item_song, mainList);
mainListView.setAdapter(adapter);
mainListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
MainAudioCreate(position);
vitri = position;
}
});
txtMainTen = findViewById(R.id.txtPlaying);
txtMainTacGia = findViewById(R.id.txtAuthor);
btnMainPlay = findViewById(R.id.btnPlayBottom);
btnMainNext = findViewById(R.id.btnNextBottom);
btnMainPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (musicPlayerService.mediaPlayer.isPlaying()){
musicPlayerService.pause();
btnMainPlay.setImageResource(R.drawable.button_play);
}
else{
musicPlayerService.resume();
btnMainPlay.setImageResource(R.drawable.button_pause);
}
}
});
btnMainNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
vitri++;
if (vitri > mainList.size() - 1){
vitri = 0;
}
MainAudioCreate(vitri);
}
});
}
private void MainAudioCreate(int position){
musicPlayerService.setVitri(position);
musicPlayerService.play();
btnMainPlay.setImageResource(R.drawable.button_pause);
txtMainTen.setText(musicPlayerService.serviceList.get(position).getName());
txtMainTacGia.setText(musicPlayerService.serviceList.get(position).getArtist());
}
public void AddFragment(View view){
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = null;
int container = 0;
String tag = "";
switch (view.getId()) {
case R.id.layoutClose:
fragment = new FragmentClose();
container = R.id.frameContent;
tag = "fragClose";
break;
case R.id.layoutList:
fragment = new FragmentPlaylist();
container = R.id.frameContent;
tag = "fragList";
break;
case R.id.bottomPlayerTouchable:
fragment = new FragmentPlayer();
container = R.id.mainFrame;
tag = "fragPlayer";
break;
}
fragmentTransaction.add(container, fragment, tag);
fragmentTransaction.addToBackStack("fragment");
fragmentTransaction.commit();
}
public void GetSongFromStorage(Context context, List<AudioModel> list){
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
String[] projection = {MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.TITLE,};
Cursor c = context.getContentResolver().query(uri, projection, null, null, MediaStore.Audio.Media.TITLE + " ASC");
if (c != null){
while (c.moveToNext()) {
AudioModel audioModel = new AudioModel();
String path = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
mmr.setDataSource(path);
String album = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
String artist = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
String name = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
byte[] image = mmr.getEmbeddedPicture();
String displayName = path.substring(path.lastIndexOf("/") + 1);
audioModel.setName(name);
audioModel.setAlbum(album);
audioModel.setArtist(artist);
audioModel.setPath(path);
audioModel.setDisplayname(displayName);
if (image != null) audioModel.setImgPath(image);
list.add(audioModel);
}
adapter.notifyDataSetChanged();
c.close();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_PERMISSION){
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
mainList.clear();
GetSongFromStorage(MainActivity.this, mainList);
}
else{
Toast.makeText(this, "Chưa cho phép", Toast.LENGTH_SHORT).show();
}
}
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicPlayerService.MusicBinder binder = (MusicPlayerService.MusicBinder) service;
musicPlayerService = binder.getService();
musicPlayerService.setList(mainList);
boundService = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
boundService = false;
}
};
}
My Service code
public class MusicPlayerService extends Service {
MediaPlayer mediaPlayer;
List<AudioModel> serviceList;
int vitri;
private final IBinder musicBind = new MusicBinder();
public class MusicBinder extends Binder {
MusicPlayerService getService(){
return MusicPlayerService.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return musicBind;
}
#Override
public boolean onUnbind(Intent intent) {
mediaPlayer.stop();
mediaPlayer.release();
return false;
}
public void initMusicPlayer(){
mediaPlayer.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
#Override
public void onCreate() {
super.onCreate();
vitri = 0;
mediaPlayer = new MediaPlayer();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
public void setList(List<AudioModel> mp3List){
serviceList = mp3List;
}
public void play(){
mediaPlayer.reset();
createSong();
}
public void pause(){
mediaPlayer.pause();
}
public void resume(){
mediaPlayer.start();
}
public void createSong(){
mediaPlayer = MediaPlayer.create(this, Uri.parse(serviceList.get(vitri).getPath()));
mediaPlayer.start();
}
public void setVitri(int pos){
vitri = pos;
}
}
My Fragment that will be added in code
public class FragmentPlayer extends Fragment {
ImageButton btnBackPlayer;
TextView txtTitlePlayer, txtTimeCurrent, txtTimeTotal;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_player, container, false);
btnBackPlayer = view.findViewById(R.id.btnBackPlayer);
txtTitlePlayer = view.findViewById(R.id.txtTitlePlayer);
txtTimeCurrent = view.findViewById(R.id.txt_timeCurrent);
txtTimeTotal = view.findViewById(R.id.txt_timeTotal);
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
btnBackPlayer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().popBackStack();
}
});
return view;
}
}
You can use a broadcast in your service and set broadcast listener in your fragment.
I tried all the possible options that I just found.
I can’t send ArrayList to another activity via the Parcelable interface.
To implement a media player
String format information it sends.
I would be very grateful, maybe there are some other options. This is the first time I'm asking a question here
Here is my activity that I use to send, the code is still very raw
public class MainActivity extends AppCompatActivity {
ScheduledExecutorService scheduledExecutorService;
public MediaPlayer mediaPlayer;
private SeekBar seekBar;
private ImageView btn_Play;
private ImageView btn_Next;
private ImageView btn_Pre;
private TextView setCurrentDuration, setTotalDuration;
private ArrayList<SoundInfo> audioList = new ArrayList<>();
SoundAdapter adapter = new SoundAdapter();
public Handler handler = new Handler();
AudioEffect audioEffect;
private int currentPosition = 0;
private SoundInfo soundInfo = new SoundInfo(this);
SoundEffects soundEffects;
boolean mediaPauseStat = false;
Context context;
PageFragmentOne pageFragmentOne;
//public MainActivity(PageFragmentOne pageFragmentOne) {
// this.pageFragmentOne = pageFragmentOne;
//}
MediaManager mediaManager = new MediaManager(this, adapter, audioList);
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Старт сервиса
//Intent i = new Intent(this, MediaPlaybackService.class);
//i.putExtra("play", "письмо от главного активити");
//startService(i);
//mediaManager.LoadSounds();
/**
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
FloatingActionButton fab = 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();
}
});
**/
////////////////////////////////////////////////////////////////////////////////////////////
setCurrentDuration = findViewById(R.id.current_Duration);
setTotalDuration = findViewById(R.id.total_Duration);
seekBar = findViewById(R.id.seekBar);
RecyclerView list = findViewById(R.id.my_recycler_view);
list.setAdapter(adapter);
list.setLayoutManager(new LinearLayoutManager(this));
// Оформление отображения адаптера
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, LinearLayout.VERTICAL);
dividerItemDecoration.setDrawable(getResources().getDrawable(R.drawable.list_item_divider, null));
list.addItemDecoration(dividerItemDecoration);
///////////////////////////////////////////////
soundInfo.setMassive(audioList);
adapter.setOnItemClickListener(new SoundAdapter.OnItemClickListener() {
#Override
public void onClick(View v, final SoundInfo obj, final int position, final ImageView onNext) {
final SoundInfo path = audioList.get(position);
String audioPath = obj.getData();
prepareMedia(position);
// Задаем текующию позицию трека
currentPosition = position;
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
// TODO Auto-generated method stub
mediaPlayer.start();
// Всегда должен быть после старта плеера, что бы не вылазило 238
updateProgressBar();
//seekBar.setProgress(0);
seekBar.setMax(mediaPlayer.getDuration());
btn_Play.setImageResource(R.drawable.btn_pause);
}
});
// Перемотка аудио
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// Слишком часто обновляет
}
#Override
public void onStartTrackingTouch(final SeekBar seekBar) {
if (mediaPlayer != null) {
soundInfo.setMediaPauseStat(false);
soundInfo.setMediaRewind(false);
mediaPlayer.pause();
seekBar.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mediaPlayer.seekTo(seekBar.getProgress());
return false;
}
});
}
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (mediaPlayer != null) {
soundInfo.setMediaPauseStat(true);
//soundInfo.setMediaRewind(true);
mediaPlayer.start();
}
}
});
// Кнопка проигрывания и паузы
btn_Play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!mediaPlayer.isPlaying()) {
onPlay();
} else {
onPaused();
}
}
});
// Следующий трек
btn_Next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onNext();
}
});
// Предыдущий трек
btn_Pre.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onPre();
}
});
}
});
init();
}
private MyBroadcastReceiver myReceiver;
#Override
public void onResume() {
super.onResume();
myReceiver = new MyBroadcastReceiver();
final IntentFilter intentFilter = new IntentFilter("YourAction");
LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver, intentFilter);
}
#Override
public void onPause() {
super.onPause();
if (myReceiver != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
myReceiver = null;
}
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// String yourValue = b.getString("ser");
Bundle bundle = intent.getExtras();
}
}
public void onMainMediaPlayList(View view) {
final Intent intent2 = new Intent(getApplicationContext(), MainMediaPlayList.class);
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("MEDIA_MASSIVE", audioList);
intent2.putExtras(bundle);
startActivity(intent2);
//Intent i = new Intent(MainActivity.this, MediaPlaybackService.class);
//startService(i);
////////////////////////////////////////////////////////////////////////////////////////////
}
public void init() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Разрешение не предоставляется
// Должны ли мы показать объяснение?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
// Показать объяснение пользователю * асинхронно* -- не блокировать
// этот поток ждет ответа пользователя! После пользователя
// увидев объяснение, попробуйте еще раз запросить разрешение.
} else {
// Никаких объяснений не требуется; запросить разрешение
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MODIFY_AUDIO_SETTINGS,
Manifest.permission.RECORD_AUDIO},
101);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS является
// app-определенная константа int. Метод обратного вызова получает
// результат запроса.
}
} else {
// Разрешение уже предоставлено
//loadSounds();
mediaManager.LoadSounds();
}
//bPlayPause = findViewById(R.id.bPlayPause);
//lv = (ListView) findViewById(R.id.lvPlayList);
btn_Play = findViewById(R.id.btn_Play);
btn_Next = findViewById(R.id.btn_Next);
btn_Pre = findViewById(R.id.btn_Pre);
}
Here is my SoundInfo
public class SoundInfo implements Parcelable {
private String data, artist, title;
private Context context;
private String pathData;
public SoundInfo(MainActivity context) {
this.context = context;
}
public SoundInfo() {
}
public SoundInfo(MainMediaPlayList context) {
this.context = context;
}
public SoundInfo(MediaPlaybackService mediaPlaybackService, String star) {
this.context = mediaPlaybackService;
this.star = star;
}
protected SoundInfo(Parcel in) {
data = in.readString();
artist = in.readString();
title = in.readString();
pathData = in.readString();
mediaPauseStat = in.readByte() != 0;
ismediaRewind = in.readByte() != 0;
audioList = in.createTypedArrayList(SoundInfo.CREATOR);
star = in.readString();
}
public static final Creator<SoundInfo> CREATOR = new Creator<SoundInfo>() {
#Override
public SoundInfo createFromParcel(Parcel in) {
return new SoundInfo(in);
}
#Override
public SoundInfo[] newArray(int size) {
return new SoundInfo[size];
}
};
public void SoundInfo(String data, String artist, String title) {
this.data = data;
this.artist = artist;
this.title = title;
}
public SoundInfo(String data, String artist, String title) {
this.data = data;
this.artist = artist;
this.title = title;
}
public String getData() {
return data;
}
public String getArtist() {
return artist;
}
public String getTitle() {
return title;
}
public void setData(String artist) {
this.data = data;
}
public void setArtist(String artist) {
this.artist = artist;
}
public void setTitle(String title) {
this.title = title;
}
public void setpathData(String pathData) {
this.pathData = pathData;
}
public String getpathData() {
return pathData;
}
// Запись в память для отображения времени
boolean mediaPauseStat = true;
boolean ismediaRewind = true;
public void setMediaPauseStat(boolean mediaPauseStat) {
this.mediaPauseStat = mediaPauseStat;
}
public boolean getMediaPauseStat() {
return mediaPauseStat;
}
public void setMediaRewind(boolean isMediaRewind) {
this.ismediaRewind = ismediaRewind;
}
public boolean getMediaRewind() {
return ismediaRewind;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
// Массив
private ArrayList<SoundInfo> audioList = new ArrayList<SoundInfo>();
public ArrayList<SoundInfo> getMassive() {
return audioList;
}
public void setMassive(ArrayList<SoundInfo> audioList) {
this.audioList = audioList;
}
private String star;
public String getStar() {
return star;
}
public void setStar(String star) {
this.star = star;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data);
dest.writeString(artist);
dest.writeString(title);
dest.writeString(pathData);
dest.writeByte((byte) (mediaPauseStat ? 1 : 0));
dest.writeByte((byte) (ismediaRewind ? 1 : 0));
dest.writeTypedList(audioList);
dest.writeString(star);
}
}
Here is my adater which I use
public class SoundAdapter extends RecyclerView.Adapter<SoundAdapter.ViewHolder> {
private ArrayList<SoundInfo> audioList = new ArrayList<SoundInfo>();
Context context;
//private String[] items;
private OnItemClickListener onItemClickListener;
public void setItems(Context context, ArrayList<SoundInfo> audioList) {
this.context = context;
this.audioList = audioList;
//audioList.addAll(items);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_audio, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.bind(audioList.get(position));
holder.clickFile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final SoundInfo path = audioList.get(position);
onItemClickListener.onClick(view, path, position, holder.onNext);
}
});
}
#Override
public int getItemCount() {
return audioList == null ? 0 : audioList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView data, title, artist;
private ImageView onNext;
private View clickFile;
ViewHolder(View view) {
super(view);
title = view.findViewById(R.id.tvSoundTitle);
artist = view.findViewById(R.id.tvSoundArtist);
clickFile = view.findViewById(R.id.clickFile);
//data = view.findViewById(R.id.item_title);
//onNext = view.findViewById(R.id.imageNext);
}
public void bind(SoundInfo soundInfo) {
title.setText(soundInfo.getTitle());
artist.setText(soundInfo.getArtist());
//clickFile.setText(soundInfo.getData());
}
}
// Передача данных в основной активити
public interface OnItemClickListener {
void onClick(View view, SoundInfo obj, int position, ImageView onNext);
}
}
Here is the second activity that an ArrayList gets.
Bundle bundle = getIntent().getExtras();
ArrayList<SoundInfo> audioList = bundle.getParcelableArrayList("MEDIA_MASSIVE");
adapter.setItems(MainMediaPlayList.this, audioList);
adapter.notifyDataSetChanged();
// загрузка адаптера
RecyclerView list = findViewById(R.id.my_recycler_view);
list.setAdapter(adapter);
list.setLayoutManager(new LinearLayoutManager(MainMediaPlayList.this));
When you open a new activity, the application crashes
does not display any errors in the log, the problem is as I understand that intent can not find
audioList as it seems to me
If I remove the method everything loads
public void onMainMediaPlayList(View view) {
final Intent intent2 = new Intent(getApplicationContext(), MainMediaPlayList.class);
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("MEDIA_MASSIVE", audioList);
intent2.putExtras(bundle);
startActivity(intent2);
Here I took off the launch log, it may somehow help
2020-03-08 18:09:43.406 15730-15730/com.shimmer.myapplication W/r.myapplicatio: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (greylist, reflection, allowed)
2020-03-08 18:09:43.406 15730-15730/com.shimmer.myapplication W/r.myapplicatio: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (greylist, reflection, allowed)
2020-03-08 18:09:43.431 15730-15730/com.shimmer.myapplication I/OverScrollerOptimization: start init SmartSlideOverScroller and get the overscroller config
2020-03-08 18:09:43.431 15730-15730/com.shimmer.myapplication I/OverScrollerOptimization: get the overscroller config
2020-03-08 18:09:43.466 15730-15730/com.shimmer.myapplication D/HwFrameworkSecurityPartsFactory: HwFrameworkSecurityPartsFactory in.
2020-03-08 18:09:43.466 15730-15730/com.shimmer.myapplication I/HwFrameworkSecurityPartsFactory: add HwFrameworkSecurityPartsFactory to memory.
2020-03-08 18:09:43.506 15730-15730/com.shimmer.myapplication D/ActivityThread: add activity client record, r= ActivityRecord{e3185e4 token=android.os.BinderProxy#837d3d7 {com.shimmer.myapplication/com.shimmer.myapplication.MainActivity}} token= android.os.BinderProxy#837d3d7
I don't think so, You can send an ArrayList like this.
I recommend you sending Class instead of sending an Arraylist.
So, just create a class like AudiolistHolder and put your ArrayList inside this class and don't forget to implement Parcelable interface for this class, too, and send this class between activities this should solve your problem. I always use this way.
EDIT:
You can use this to send your data
Intent intent2 = new Intent(this, MainMediaPlayList.class);
MassiveAudioList m = new MassiveAudioList(audioList);
intent2.putExtra("MEDIA_MASSIVE”, m);
startActivity(intent2);
and you can use this in your second activity to retrieve data
MassiveAudioList massiveAudio=getIntent().getParcelableExtra("MEDIA_MASSIVE");
ArrayList<SoundInfo> audioList = massiveAudio.getMassiveAudioList();
Have you tried like this with arraylist
I think it can be,
In MainActivity.java
ArrayList<String> audioList= new ArrayList<>();
audioList.add("ChipThrills");
Intent i = new Intent(MainActivity.this, Secondactivity.class);
i.putExtra("Musickey", audioList);
startActivity(i);
In SecondActivity.java
ArrayList<String> audioList = (ArrayList<String>) getIntent().getSerializableExtra("Musickey");
I am writing to you here because I did not understand how to add code to the comment
And how do I extract data by adding it to an array. If of course I did the right thing, I'm new to this field. Thanks.
public class MassiveAudioList implements Parcelable {
private ArrayList<SoundInfo> audioList = new ArrayList<>();
private Context context;
public MassiveAudioList(Context context) {
this.context = context;
}
public MassiveAudioList(ArrayList audioList) {
this.audioList = audioList;
}
protected MassiveAudioList(Parcel in) {
audioList = in.createTypedArrayList(SoundInfo.CREATOR);
}
public static final Creator<MassiveAudioList> CREATOR = new Creator<MassiveAudioList>() {
#Override
public MassiveAudioList createFromParcel(Parcel in) {
return new MassiveAudioList(in);
}
#Override
public MassiveAudioList[] newArray(int size) {
return new MassiveAudioList[size];
}
};
public ArrayList<SoundInfo> getMassiveAudioList() {
return audioList;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(audioList);
}
}
public void onMainMediaPlayList(View view) {
Log.d("TAG", "soundinfo" + soundInfo.getData());
final Intent intent2 = new Intent(getApplicationContext(), MainMediaPlayList.class);
massiveAudioList = new MassiveAudioList(this);
MassiveAudioList m = new MassiveAudioList(audioList);
Bundle bundle = new Bundle();
bundle.putParcelable("MEDIA_MASSIVE", m);
intent2.putExtras(bundle);
startActivity(intent2);
}
My app recyclerview having audio, imageview, textview as items. When scrolling the recyclerview, it is scrolled properly without any issue if item is not updated. But scrolling the recyclerview is not smooth while playing a audio item of recyclerview, it behaves as below attached gif image. While playing an audio, i am updating audio current playing time in textview. I am not sure what is issue is here. Kindly help me. Thanks.
Here is my code.
public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ArrayList<SessionHistoryMesssage> messageArrayList;
MediaPlayer mPlayer = null;
LocalService mService;
boolean mBound = false;
private RelativeLayout.LayoutParams params;
int currentlyPlayingPosition = -1;
public String mBasePath = "", imageDirectory = "", mSessionId = "", audioDirectory = "";
private ProgressDialog progressDialog;
public RetrofitRestAdapter retrofitRestAdapter;
private MessageAdapter messageListAdapter;
private SessionHistoryResponse sessionHistoryResponse;
public Context mContext;
private int mSelectionPosition = -1;
private int num = 0;
public boolean isSessionStarted=false;
public static Handler mHandler;
public int playposition = -1, sessionstartPosition = -1, alreadystartPosition = -1, sessionendPosition = -1;
public MessageAdapter(Context mContext
, ArrayList<SessionHistoryMesssage> messageArrayList, String mBasePath) {
this.mContext = mContext;
this.messageArrayList = messageArrayList;
this.mBasePath = mBasePath;
mHandler = new Handler();
Intent intent = new Intent(mContext, LocalService.class);
this.mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
// 1. Type a) SEND b) RECEIVE
// 2. Message a) AUDIO b) IMAGE
#Override
public int getItemViewType(int position) {
// Just as an example, return 0 or 2 depending on position
// Note that unlike in ListView adapters, types don't have to be contiguous
Log.i("ViewType", messageArrayList.get(position).getmMediaType() + "");
Log.i("sent by", messageArrayList.get(position).getmSentBy() + "");
return Integer.parseInt(messageArrayList.get(position).getmSentBy());
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
VHItemAudio dataObjectHolder = null;
if (Integer.parseInt(Utility.readPatientId(mContext)) == viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_a_conversation_item, parent, false);
dataObjectHolder = new VHItemAudio(view);
return dataObjectHolder;
} else {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_b_conversation_item, parent, false);
dataObjectHolder = new VHItemAudio(view);
return dataObjectHolder;
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final VHItemAudio vhItemHolder = (VHItemAudio) holder;
vhItemHolder.bind(messageArrayList.get(position), position);
}
public class VHItemAudio extends RecyclerView.ViewHolder {
ImageView playPauseImageView, imgDownloadAudio, imgDownloadImage;
TextView txtImageSendTime, runningTimerTextView, txtAudioSendTime, txtSessionStart;
SeekBar seekBar;
ImageView imageView;
RelativeLayout layoutAudio, layoutImage, layoutdownloadImage, layoutSessionStart;
ProgressBar progressImageDownload, progressAudioDownload;
private ImageView imageShareImageView,recordAudioImageView,imgEndSession;
public VHItemAudio(View itemView) {
super(itemView);
playPauseImageView = (ImageView) itemView.findViewById(R.id.playPauseImageView);
txtImageSendTime = (TextView) itemView.findViewById(R.id.txtImageSendTime);
runningTimerTextView = (TextView) itemView.findViewById(R.id.runningTimerTextView);
seekBar = (SeekBar) itemView.findViewById(R.id.seekBar);
imgDownloadAudio = (ImageView) itemView.findViewById(R.id.imgDownloadAudio);
imgDownloadImage = (ImageView) itemView.findViewById(R.id.imgDownloadImage);
imageView = (ImageView) itemView.findViewById(R.id.imageView);
txtAudioSendTime = (TextView) itemView.findViewById(R.id.txtAudioSendTime);
layoutAudio = (RelativeLayout) itemView.findViewById(R.id.layoutAudio);
layoutImage = (RelativeLayout) itemView.findViewById(R.id.layoutImage);
layoutdownloadImage = (RelativeLayout) itemView.findViewById(R.id.layoutdownloadImage);
progressImageDownload = (ProgressBar) itemView.findViewById(R.id.progressImageDownload);
progressAudioDownload = (ProgressBar) itemView.findViewById(R.id.progressAudioDownload);
layoutSessionStart = (RelativeLayout) itemView.findViewById(R.id.layoutSessionStart);
txtSessionStart = (TextView) itemView.findViewById(R.id.txtSessionStart);
Log.i("", "Adding Listener");
imageShareImageView= (ImageView) ((PatientActivity)PatientActivity.patientContext).findViewById(R.id.imageShareImageView);
recordAudioImageView= (ImageView) ((PatientActivity) PatientActivity.patientContext).findViewById(R.id.recordAudioImageView);
imgEndSession= (ImageView) ((PatientActivity) PatientActivity.patientContext).findViewById(R.id.imgEndSession);
}
public void bind(final SessionHistoryMesssage sessionHistoryMesssage, final int position) {
if (sessionHistoryMesssage.getmSessionStart().equalsIgnoreCase("yes")
&& Integer.parseInt(sessionHistoryMesssage.getmTransToId())
== Integer.parseInt(Utility.readPatientId(mContext))) {
sessionstartPosition = position;
isSessionStarted=true;
} else if (sessionHistoryMesssage.getmSessionStart().equalsIgnoreCase("already started")) {
alreadystartPosition = position;
} else if (sessionHistoryMesssage.getmSessionStart().equalsIgnoreCase("end")) {
sessionendPosition = position;
} else {
}
if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("audio")) {
layoutAudio.setVisibility(View.VISIBLE);
layoutImage.setVisibility(View.GONE);
seekBar.setFocusable(false);
txtAudioSendTime.setText(Utility.convertDateTime(sessionHistoryMesssage.getmSentTime()));
audioDirectory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/A/Audio/";
File file = new File(Environment.getExternalStorageDirectory()
+ "/A/Audio/" + messageArrayList.get(position).getmMediaValue());
if (file.exists()) {
imgDownloadAudio.setVisibility(View.GONE);
playPauseImageView.setVisibility(View.VISIBLE);
playPauseImageView.setImageResource(R.drawable.play_icon);
} else {
imgDownloadAudio.setVisibility(View.VISIBLE);
playPauseImageView.setVisibility(View.GONE);
imgDownloadAudio.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imgDownloadAudio.setVisibility(View.GONE);
Log.v("basepath", mBasePath);
new DownloadFile(mBasePath + messageArrayList.get(position).getmMediaPath() +
messageArrayList.get(position).getmMediaValue(),
audioDirectory + messageArrayList.get(position).getmMediaValue()
, imgDownloadAudio, progressAudioDownload).execute();
}
});
}
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (playposition == position) {
int seekBarPosition = seekBar.getProgress();
mPlayer.seekTo(seekBarPosition);
}
}
});
playPauseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!mService.isPlaying()) {
playposition = position;
mService.playAudio(audioDirectory + messageArrayList.get(position).getmMediaValue());
notifyDataSetChanged();
} else {
if (playposition == position) {
playposition = -1;
mHandler.removeCallbacks(null);
mService.stopAudio();
notifyDataSetChanged();
} else {
playposition = position;
mService.playAudio(audioDirectory + messageArrayList.get(position).getmMediaValue());
notifyDataSetChanged();
}
}
}
});
} else if (messageArrayList.get(position).getmMediaType().equalsIgnoreCase("photo")) {
layoutImage.setVisibility(View.VISIBLE);
layoutAudio.setVisibility(View.GONE);
txtImageSendTime.setText(Utility.convertDateTime(messageArrayList.get(position).getmSentTime()));
imageDirectory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/A/Image/";
Log.v("image path123", mBasePath + messageArrayList.get(position).getmMediaPath()
+ messageArrayList.get(position).getmMediaValue());
try {
File file = new File(Environment.getExternalStorageDirectory()
+ "/A/Image/" + messageArrayList.get(position).getmMediaValue());
if (!file.exists()) {
layoutdownloadImage.setVisibility(View.VISIBLE);
imageView.setVisibility(View.GONE);
} else {
layoutdownloadImage.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);
imageView.setImageBitmap(BitmapFactory.decodeFile(file.getAbsolutePath()));
}
layoutdownloadImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mSessionId = messageArrayList.get(position).getmSessionId();
imgDownloadImage.setVisibility(View.GONE);
new DownloadImage(mBasePath + messageArrayList.get(position).getmMediaPath()
+ sessionHistoryMesssage.getmMediaValue(),
sessionHistoryMesssage.getmMediaValue(),
imgDownloadImage, progressImageDownload
).execute();
}
});
imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showImageDialog(position);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
if (sessionstartPosition == position) {
layoutSessionStart.setVisibility(View.VISIBLE);
layoutAudio.setVisibility(View.GONE);
layoutImage.setVisibility(View.GONE);
txtSessionStart.setText("Start session");
if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("audio")) {
params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
params.addRule(RelativeLayout.BELOW, R.id.layoutAudio);
} else if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("photo")) {
params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
params.addRule(RelativeLayout.BELOW, R.id.layoutImage);
} else {
}
if(!isSessionStarted) {
imageShareImageView.setVisibility(View.GONE);
recordAudioImageView.setVisibility(View.GONE);
imgEndSession.setVisibility(View.GONE);
isSessionStarted=true;
}
txtSessionStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
retrofitRestAdapter = new RetrofitRestAdapter();
Map<String, String> sessionstartParams = new HashMap<String, String>();
sessionstartParams.put("session_id", sessionHistoryMesssage.getmSessionId());
final ProgressDialog progressDialog = new ProgressDialog(mContext);
progressDialog.setTitle("Please wait...");
progressDialog.show();
retrofitRestAdapter.sessionStart(sessionstartParams, new Callback<SessionHistoryResponse>() {
#Override
public void onResponse(Call<SessionHistoryResponse> call, Response<SessionHistoryResponse> response) {
Log.v("session start response", new Gson().toJson(response.body()));
progressDialog.dismiss();
txtSessionStart.setEnabled(false);
txtSessionStart.setClickable(false);
imageShareImageView.setVisibility(View.VISIBLE);
recordAudioImageView.setVisibility(View.VISIBLE);
imgEndSession.setVisibility(View.VISIBLE);
}
#Override
public void onFailure(Call<SessionHistoryResponse> call, Throwable t) {
t.printStackTrace();
progressDialog.dismiss();
imageShareImageView.setVisibility(View.GONE);
recordAudioImageView.setVisibility(View.GONE);
imgEndSession.setVisibility(View.GONE);
}
});
}
});
} else {
}
if (position == alreadystartPosition) {
layoutSessionStart.setClickable(false);
txtSessionStart.setClickable(false);
imageShareImageView.setVisibility(View.VISIBLE);
recordAudioImageView.setVisibility(View.VISIBLE);
imgEndSession.setVisibility(View.VISIBLE);
} else {
}
if (position == sessionendPosition) {
layoutSessionStart.setClickable(false);
txtSessionStart.setClickable(false);
// layoutSessionStart.setVisibility(View.VISIBLE);
txtSessionStart.setText("End session");
if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("audio")) {
params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
params.addRule(RelativeLayout.BELOW, R.id.layoutAudio);
} else if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("photo")) {
params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
params.addRule(RelativeLayout.BELOW, R.id.layoutImage);
} else {
}
imageShareImageView.setVisibility(View.GONE);
recordAudioImageView.setVisibility(View.GONE);
imgEndSession.setVisibility(View.GONE);
} else {
}
if (position == playposition) {
if (mBound) {
Log.v("service", "bound");
mHandler.removeCallbacks(null);
seekBar.setMax(mService.getrunningTime() / 1000);
Log.v("duration", mService.getrunningTime() + "");
playPauseImageView.setImageResource(R.drawable.stop_icon);
((PatientActivity) mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
if (mService.isPlaying()) {
num = mService.getDuration();
seekBar.setProgress(num / 1000);
runningTimerTextView.setText(convertMilliToMinutes(num));
// vhItemHolder.seekBar.setFocusable(false);
mHandler.postDelayed(this, 50);
} else {
playposition = -1;
mHandler.removeCallbacks(null);
seekBar.setProgress(0);
seekBar.setFocusable(false);
runningTimerTextView.setText("00:00");
playPauseImageView.setImageResource(R.drawable.play_icon);
}
}
});
} else {
Log.v("service", "not bound");
}
} else {
mHandler.removeCallbacks(null);
playPauseImageView.setImageResource(R.drawable.play_icon);
runningTimerTextView.setText("00:00");
seekBar.setMax(0);
}
}
}
Service class
public class LocalService extends Service {
boolean mBound = false;
private final IBinder mBinder = new LocalBinder();
private int timer;
private MediaPlayer mediaPlayer = new MediaPlayer();
private boolean isStopped = true;
// Random number generator
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public int getrunningTime() {
return mediaPlayer.getDuration();
}
public int getDuration() {
// Log.v("player duration",timer+"");
if (mediaPlayer != null) {
if (!isStopped) {
Log.v("playing", (mediaPlayer.getCurrentPosition()) + "");
return mediaPlayer.getCurrentPosition();
} else {
// mediaPlayer.reset();
mHandler.removeCallbacks(null);
return 0;
}
} else {
Log.v("not playing", (mediaPlayer.getCurrentPosition()) + "");
mHandler.removeCallbacks(null);
return 0;
}
}
#Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
isStopped = false;
//playAudio();
Log.v("max duration", timer + "");
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
//mediaPlayer.stop();
isStopped = true;
mediaPlayer.reset();
//mediaPlayer.release();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean isPlaying() {
if (mediaPlayer != null) {
try {
if (mediaPlayer.isPlaying()) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
} else {
return false;
}
}
public void playAudio(String path) {
Log.v("audio path",path);
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void stopAudio() {
if (mediaPlayer != null) {
mediaPlayer.stop();
// mediaPlayer.reset();
isStopped = true;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void unbindService(ServiceConnection conn) {
super.unbindService(conn);
mediaPlayer.reset();
mediaPlayer.release();
}
#Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.reset();
mediaPlayer.release();
}
}
setHasStableIds(true); Add this in your adapter constructor and Override these two methodes in adapter.
#Override
public long getItemId(int pos) {
return pos;
}
#Override
public int getItemViewType(int pos) {
//return pos;
return Integer.parseInt(messageArrayList.get(position).getmSentBy());
}
It might help you...
Because you are calling notifyDataSetChanged() and it's updating the whole recyclerview whenever you do that. You better call notifyItemChanged(position) to update the certain item that you are supposed to update.
((PatientActivity) mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
if (mService.isPlaying()) {
num = mService.getDuration();
seekBar.setProgress(num / 1000);
runningTimerTextView.setText(convertMilliToMinutes(num));
// vhItemHolder.seekBar.setFocusable(false);
mHandler.postDelayed(this, 50);
} else {
playposition = -1;
mHandler.removeCallbacks(null);
seekBar.setProgress(0);
seekBar.setFocusable(false);
runningTimerTextView.setText("00:00");
playPauseImageView.setImageResource(R.drawable.play_icon);
}
}
});
can you convert that into something like.
if (position == playposition) {
if (mBound) {
Log.v("service", "bound");
mHandler.removeCallbacks(null);
seekBar.setMax(mService.getrunningTime() / 1000);
Log.v("duration", mService.getrunningTime() + "");
playPauseImageView.setImageResource(R.drawable.stop_icon);
if (mService.isPlaying()) {
num = mService.getDuration();
seekBar.setProgress(num / 1000);
runningTimerTextView.setText(convertMilliToMinutes(num));
// vhItemHolder.seekBar.setFocusable(false);
notifyItemChanged(position);
} else {
playposition = -1;
mHandler.removeCallbacks(null);
seekBar.setProgress(0);
seekBar.setFocusable(false);
runningTimerTextView.setText("00:00");
playPauseImageView.setImageResource(R.drawable.play_icon);
notifyItemChanged(position);
}
}
}
Try to remove the whole usage of the runnable.
I am trying to implement a list with videos like vine or Instagram app. Where they play video plays when list item is shown or fully visible and video pauses when list item gets hided. I am using textureview with media player to play a video from url and added it as list item in recyclerview. Following is my code.
VideosAdapter Class:
public class VideosAdapter extends RecyclerView.Adapter<VideosAdapter.ViewHolder> {
Context context;
private ArrayList<String> urls;
public static class ViewHolder extends RecyclerView.ViewHolder {
public LinearLayout layout;
public TextView textView;
public ViewHolder(View v) {
super(v);
layout = (LinearLayout) v.findViewById(R.id.linearLayout);
textView = (TextView) v.findViewById(R.id.textView);
}
}
public VideosAdapter(Context context, ArrayList<String> urls) {
this.context = context;
this.urls = urls;
}
// Create new views (invoked by the layout manager)
#Override
public VideosAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_main, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String url = urls.get(position);
holder.textView.setText(url);
playVideo(holder, url);
}
#Override
public int getItemCount() {
return urls.size();
}
private void playVideo(ViewHolder holder, String url)
{
final CustomVideoPlayer vid = new CustomVideoPlayer(String.valueOf(url), context);
holder.layout.addView(vid);
holder.layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
vid.changePlayState();
}
});
}
}
CustomVideoPlayer Class:
public class CustomVideoPlayer extends TextureView implements TextureView.SurfaceTextureListener
{
Context context;
String url;
MediaPlayer mp;
Surface surface;
SurfaceTexture s;
public CustomVideoPlayer(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
}
public CustomVideoPlayer(String ur, Context context)
{
super(context);
this.setSurfaceTextureListener(this);
this.url = ur;
this.context = context;
}
#Override
public void onSurfaceTextureAvailable(final SurfaceTexture surface, int arg1, int arg2) {
this.s = surface;
Log.d("url", this.url);
startVideo(surface);
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
return true;
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,int arg2) {
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
}
public void setVideo(String url)
{
this.url = url;
}
public void startVideo(SurfaceTexture t)
{
this.surface = new Surface(t);
this.mp = new MediaPlayer();
this.mp.setSurface(this.surface);
try {
Uri uri = Uri.parse(this.url);
this.mp.setDataSource(url);
this.mp.prepareAsync();
this.mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true);
mp.start();
}
});
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
} catch (SecurityException e1) {
e1.printStackTrace();
} catch (IllegalStateException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
try {
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
public void changePlayState()
{
if(this.mp.isPlaying())
this.mp.pause();
else
this.mp.start();
}
}
When i run this code there are multiple issues in it.
1) First two items/videos buffers and play fine. But when i scroll it does not load third video and first video also gets removed from the list.
2) On scroll videos/list items starts buffering again for the item that was already buffered.
3) On fast scroll list gets too laggy and get stuck and crashes.
Attached is the image of logcat that i get while list scroll and video playing.
Can anyone guide me through this? What is the right way to create a list like vine app?
I was able to achieve that by first downloading the videos from url and then playing it with custom player. Here is how i did in case if anyone else needed that:
1) Get all url's need to be played
2) Start downloading videos (in queue) from urls in local storage and keep a flag in preferences (that a video is already downloaded or not)
3) Assign urls to Adapter in which initialize object of video player controller that handles video playbacks
4) Set addOnScrollListener to check which position/video is currently visible and check if video is already downloaded or not if yes then play it.
Following is complete code:
MainActivity
public class MainActivity extends ActionBarActivity implements IVideoDownloadListener {
private static String TAG = "MainActivity";
private Context context;
private RecyclerView mRecyclerView;
private ProgressBar progressBar;
private VideosAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private ArrayList<Video> urls;
VideosDownloader videosDownloader;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = MainActivity.this;
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
urls = new ArrayList<Video>();
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new VideosAdapter(MainActivity.this, urls);
mRecyclerView.setAdapter(mAdapter);
videosDownloader = new VideosDownloader(context);
videosDownloader.setOnVideoDownloadListener(this);
if(Utils.hasConnection(context))
{
getVideoUrls();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
LinearLayoutManager layoutManager = ((LinearLayoutManager) recyclerView.getLayoutManager());
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
Video video;
if (urls != null && urls.size() > 0)
{
if (findFirstCompletelyVisibleItemPosition >= 0) {
video = urls.get(findFirstCompletelyVisibleItemPosition);
mAdapter.videoPlayerController.setcurrentPositionOfItemToPlay(findFirstCompletelyVisibleItemPosition);
mAdapter.videoPlayerController.handlePlayBack(video);
}
else
{
video = urls.get(firstVisiblePosition);
mAdapter.videoPlayerController.setcurrentPositionOfItemToPlay(firstVisiblePosition);
mAdapter.videoPlayerController.handlePlayBack(video);
}
}
}
}
});
}
else
Toast.makeText(context, "No internet available", Toast.LENGTH_LONG).show();
}
#Override
public void onVideoDownloaded(Video video) {
mAdapter.videoPlayerController.handlePlayBack(video);
}
private void getVideoUrls()
{
Video video1 = new Video("0", "1", "http://techslides.com/demos/sample-videos/small.mp4");
urls.add(video1);
Video video2 = new Video("1", "2", "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4");
urls.add(video2);
Video video3 = new Video("2", "3", "http://sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4");
urls.add(video3);
Video video4 = new Video("3", "4", "http://dev.exiv2.org/attachments/341/video-2012-07-05-02-29-27.mp4");
urls.add(video4);
Video video5 = new Video("4", "5", "http://techslides.com/demos/sample-videos/small.mp4");
urls.add(video5);
Video video6 = new Video("5", "6", "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4");
urls.add(video6);
Video video7 = new Video("6", "7", "http://sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4");
urls.add(video7);
mAdapter.notifyDataSetChanged();
progressBar.setVisibility(View.GONE);
videosDownloader.startVideosDownloading(urls);
}
}
VideosAdapter
public class VideosAdapter extends RecyclerView.Adapter<VideosAdapter.ViewHolder> {
private static String TAG = "VideosAdapter";
Context context;
private ArrayList<Video> urls;
public VideoPlayerController videoPlayerController;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public ProgressBar progressBar;
public RelativeLayout layout;
public ViewHolder(View v) {
super(v);
layout = (RelativeLayout) v.findViewById(R.id.layout);
textView = (TextView) v.findViewById(R.id.textView);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
}
}
public VideosAdapter(Context context, final ArrayList<Video> urls) {
this.context = context;
this.urls = urls;
videoPlayerController = new VideoPlayerController(context);
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_main, parent, false);
Configuration configuration = context.getResources().getConfiguration();
int screenWidthDp = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier.
int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier.
ViewHolder viewHolder = new ViewHolder(v);
int screenWidthPixels = Utils.convertDpToPixel(screenWidthDp, context);
RelativeLayout.LayoutParams rel_btn = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, screenWidthPixels);
viewHolder.layout.setLayoutParams(rel_btn);
return viewHolder;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Video video = urls.get(position);
holder.textView.setText("Video " + video.getId());
final VideoPlayer videoPlayer = new VideoPlayer(context);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
videoPlayer.setLayoutParams(params);
holder.layout.addView(videoPlayer);
videoPlayerController.loadVideo(video, videoPlayer, holder.progressBar);
videoPlayer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
videoPlayer.changePlayState();
}
});
}
#Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
Log.d(TAG, "onViewRecycledCalled");
holder.layout.removeAllViews();
}
#Override
public int getItemCount() {
return urls.size();
}
}
VideosDownloader
public class VideosDownloader {
private static String TAG = "VideosDownloader";
Context context;
FileCache fileCache;
IVideoDownloadListener iVideoDownloadListener;
public VideosDownloader(Context context) {
this.context = context;
fileCache = new FileCache(context);
}
/////////////////////////////////////////////////////////////////
// Start downloading all videos from given urls
public void startVideosDownloading(final ArrayList<Video> videosList)
{
Thread thread = new Thread(new Runnable() {
#Override
public void run()
{
for(int i=0; i<videosList.size(); i++)
{
final Video video = videosList.get(i);
String id = video.getId();
String url = video.getUrl();
String isVideoDownloaded = Utils.readPreferences(context, video.getUrl(), "false");
boolean isVideoAvailable = Boolean.valueOf(isVideoDownloaded);
if(!isVideoAvailable)
{
//Download video from url
String downloadedPath = downloadVideo(url);
//Log.i(TAG, "Vides downloaded at: " + downloadedPath);
Activity activity = (Activity) context;
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
Utils.savePreferences(context, video.getUrl(), "true");
iVideoDownloadListener.onVideoDownloaded(video);
}
});
}
}
}
});
thread.start();
}
/////////////////////////////////////////////////////////////////
private String downloadVideo(String urlStr)
{
URL url = null;
File file = null;
try
{
file = fileCache.getFile(urlStr);
url = new URL(urlStr);
long startTime = System.currentTimeMillis();
URLConnection ucon = null;
ucon = url.openConnection();
InputStream is = ucon.getInputStream();
BufferedInputStream inStream = new BufferedInputStream(is, 1024 * 5);
FileOutputStream outStream = new FileOutputStream(file);
byte[] buff = new byte[5 * 1024];
//Read bytes (and store them) until there is nothing more to read(-1)
int len;
while ((len = inStream.read(buff)) != -1) {
outStream.write(buff, 0, len);
}
//clean up
outStream.flush();
outStream.close();
inStream.close();
}
catch (MalformedURLException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
return file.getAbsolutePath();
}
public void setOnVideoDownloadListener(IVideoDownloadListener iVideoDownloadListener) {
this.iVideoDownloadListener = iVideoDownloadListener;
}
}
VideoPlayerController
public class VideoPlayerController {
private static String TAG = "VideoPlayerController";
Context context;
FileCache fileCache;
int currentPositionOfItemToPlay = 0;
Video currentPlayingVideo;
private Map<String, VideoPlayer> videos = Collections.synchronizedMap(new WeakHashMap<String, VideoPlayer>());
private Map<String, ProgressBar> videosSpinner = Collections.synchronizedMap(new WeakHashMap<String, ProgressBar>());
public VideoPlayerController(Context context) {
this.context = context;
fileCache = new FileCache(context);
}
public void loadVideo(Video video, VideoPlayer videoPlayer, ProgressBar progressBar) {
//Add video to map
videos.put(video.getIndexPosition(), videoPlayer);
videosSpinner.put(video.getIndexPosition(), progressBar);
handlePlayBack(video);
}
//This method would check two things
//First if video is downloaded or its local path exist
//Second if the videoplayer of this video is currently showing in the list or visible
public void handlePlayBack(Video video)
{
//Check if video is available
if(isVideoDownloaded(video))
{
// then check if it is currently at a visible or playable position in the listview
if(isVideoVisible(video))
{
//IF yes then playvideo
playVideo(video);
}
}
}
private void playVideo(final Video video)
{
//Before playing it check if this video is already playing
if(currentPlayingVideo != video)
{
//Start playing new url
if(videos.containsKey(video.getIndexPosition()))
{
final VideoPlayer videoPlayer2 = videos.get(video.getIndexPosition());
String localPath = fileCache.getFile(video.getUrl()).getAbsolutePath();
if(!videoPlayer2.isLoaded)
{
videoPlayer2.loadVideo(localPath, video);
videoPlayer2.setOnVideoPreparedListener(new IVideoPreparedListener() {
#Override
public void onVideoPrepared(Video mVideo) {
//Pause current playing video if any
if(video.getIndexPosition() == mVideo.getIndexPosition())
{
if(currentPlayingVideo!=null)
{
VideoPlayer videoPlayer1 = videos.get(currentPlayingVideo.getIndexPosition());
videoPlayer1.pausePlay();
}
videoPlayer2.mp.start();
currentPlayingVideo = mVideo;
}
}
});
}
else
{
//Pause current playing video if any
if(currentPlayingVideo!=null)
{
VideoPlayer videoPlayer1 = videos.get(currentPlayingVideo.getIndexPosition());
videoPlayer1.pausePlay();
}
boolean isStarted = videoPlayer2.startPlay();
{
//Log.i(TAG, "Started playing Video Index: " + video.getIndexPosition());
//Log.i(TAG, "Started playing Video: " + video.getUrl());
}
currentPlayingVideo = video;
}
}
}
else
{
//Log.i(TAG, "Already playing Video: " + video.getUrl());
}
}
private boolean isVideoVisible(Video video) {
//To check if the video is visible in the listview or it is currently at a playable position
//we need the position of this video in listview and current scroll position of the listview
int positionOfVideo = Integer.valueOf(video.getIndexPosition());
if(currentPositionOfItemToPlay == positionOfVideo)
return true;
return false;
}
private boolean isVideoDownloaded(Video video) {
String isVideoDownloaded = Utils.readPreferences(context, video.getUrl(), "false");
boolean isVideoAvailable = Boolean.valueOf(isVideoDownloaded);
if(isVideoAvailable)
{
//If video is downloaded then hide its progress
hideProgressSpinner(video);
return true;
}
showProgressSpinner(video);
return false;
}
private void showProgressSpinner(Video video) {
ProgressBar progressBar = videosSpinner.get(video.getIndexPosition());
if(progressBar!=null)
progressBar.setVisibility(View.VISIBLE);
}
private void hideProgressSpinner(Video video) {
ProgressBar progressBar = videosSpinner.get(video.getIndexPosition());
if(progressBar!=null && progressBar.isShown())
{
progressBar.setVisibility(View.GONE);
Log.i(TAG, "ProgressSpinner Hided Index: " + video.getIndexPosition());
}
}
public void setcurrentPositionOfItemToPlay(int mCurrentPositionOfItemToPlay) {
currentPositionOfItemToPlay = mCurrentPositionOfItemToPlay;
}
}
VideoPlayer
public class VideoPlayer extends TextureView implements TextureView.SurfaceTextureListener {
private static String TAG = "VideoPlayer";
/**This flag determines that if current VideoPlayer object is first item of the list if it is first item of list*/
boolean isFirstListItem;
boolean isLoaded;
boolean isMpPrepared;
IVideoPreparedListener iVideoPreparedListener;
Video video;
String url;
MediaPlayer mp;
Surface surface;
SurfaceTexture s;
public VideoPlayer(Context context) {
super(context);
}
public VideoPlayer(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public void loadVideo(String localPath, Video video) {
this.url = localPath;
this.video = video;
isLoaded = true;
if (this.isAvailable()) {
prepareVideo(getSurfaceTexture());
}
setSurfaceTextureListener(this);
}
#Override
public void onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height) {
isMpPrepared = false;
prepareVideo(surface);
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if(mp!=null)
{
mp.stop();
mp.reset();
mp.release();
mp = null;
}
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
public void prepareVideo(SurfaceTexture t)
{
this.surface = new Surface(t);
mp = new MediaPlayer();
mp.setSurface(this.surface);
try {
mp.setDataSource(url);
mp.prepareAsync();
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
isMpPrepared = true;
mp.setLooping(true);
iVideoPreparedListener.onVideoPrepared(video);
}
});
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
} catch (SecurityException e1) {
e1.printStackTrace();
} catch (IllegalStateException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
try {
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
#Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
}
public boolean startPlay()
{
if(mp!=null)
if(!mp.isPlaying())
{
mp.start();
return true;
}
return false;
}
public void pausePlay()
{
if(mp!=null)
mp.pause();
}
public void stopPlay()
{
if(mp!=null)
mp.stop();
}
public void changePlayState()
{
if(mp!=null)
{
if(mp.isPlaying())
mp.pause();
else
mp.start();
}
}
public void setOnVideoPreparedListener(IVideoPreparedListener iVideoPreparedListener) {
this.iVideoPreparedListener = iVideoPreparedListener;
}
}
IVideoDownloadListener
public interface IVideoDownloadListener {
public void onVideoDownloaded(Video video);
}
IVideoPreparedListener
public interface IVideoPreparedListener {
public void onVideoPrepared(Video video);
}
Why don't you add the custom video view in the layout file 'view_main' itself.
Check the visibility of the video view and play only if the view is visible.
public static boolean isViewVisible(View subView, View parentView) {
Rect scrollBounds = new Rect();
parentView.getHitRect(scrollBounds);
if (subView.getLocalVisibleRect(scrollBounds)) {
return true;
}
return false;
}
Code for checking visiblity. Call this in scroll state changed listener when the scroll state is idle.
Also you will have to use an AsyncTask for downloading videos ,but only download one video at a time or you might get out of memory error.
You should maintain a cache of videos locally by downloading them at backend and play one video at a time from local memory to keep the list scroll smooth.