Search code examples
androidandroid-livedata

Android MVVM live data orientation change


I am new to Android Development. Trying to retain the state when the screen orientation changes. I have a model with Songs with the field url. I want to play the mp3 file at the url. The playing is working but restarts when the screen orientation changes.

I don't know if Live Data is suitable for this problem.

public class MainActivity extends AppCompatActivity {

    private SongViewModel songViewModel;
    
    @SuppressLint("NotifyDataSetChanged")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.single_song_item);

        //define viewmodel
        songViewModel = new ViewModelProvider(this).get(SongViewModel.class);
        // get song through viewModel
        songViewModel.getLiveSongData().observe(this, songList -> {
            String url = songList.get(0).getUrl();
            ArgAudio audio = ArgAudio.createFromURL("test", "test", url);
            ArgPlayerSmallView argMusicPlayer = (ArgPlayerSmallView) findViewById(R.id.argmusicplayer);
            argMusicPlayer.play(audio);

        });


    }

}
public class SongRepository
{
    MutableLiveData<List<Song>> songListMutableLiveData;
    FirebaseFirestore mFirestore;
    MutableLiveData<Song> songMutableLiveData;

    public SongRepository() {
        this.songListMutableLiveData = new MutableLiveData<>();
        //define firestore
        mFirestore = FirebaseFirestore.getInstance();
        //define songlist
        songMutableLiveData = new MutableLiveData<>();

    }


    //get song from firebase firestore
    public MutableLiveData<List<Song>> getSongListMutableLiveData() {
        Log.i("TAG", "getSongListMutableLiveData: ");

        mFirestore.collectionGroup("songs").addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot value, @Nullable FirebaseFirestoreException error) {
                List<Song> songList = new ArrayList<>();
                for (QueryDocumentSnapshot doc : value) {
                    if (doc != null) {
                        songList.add(doc.toObject(Song.class));
                    }
                }
                songListMutableLiveData.postValue(songList);
            }
        });
        return songListMutableLiveData;
    }

}
public class SongViewModel extends ViewModel {
    MutableLiveData<List<Song>> songListMutableLiveData;
    FirebaseFirestore mFirestore;
    SongRepository songRepository;


    public SongViewModel() {
        songRepository = new SongRepository();
        mFirestore = FirebaseFirestore.getInstance();
                songListMutableLiveData = songRepository.getSongListMutableLiveData();



    }
    public MutableLiveData<List<Song>> getLiveSongData()
    {
        return songListMutableLiveData;
    }

}

Solution

  • To retain the state when the screen orientation changes, you can use a combination of LiveData and ViewModel.

    LiveData is suitable for this problem as it allows you to observe changes in data and automatically update UI components when the data changes. The ViewModel will survive configuration changes like screen orientation changes.

    Here's how you can modify your code to retain the song and its state across screen orientation changes:

    Create a ViewModel that holds the currently playing song and its state.

    public class SongViewModel extends ViewModel {
        private MutableLiveData<Song> currentSongLiveData = new MutableLiveData<>();
    
        public LiveData<Song> getCurrentSongLiveData() {
            return currentSongLiveData;
        }
    
        public void setCurrentSong(Song song) {
            currentSongLiveData.setValue(song);
        }
    }
    

    In your MainActivity, use the ViewModel to retain the currently playing song and its state.

    public class MainActivity extends AppCompatActivity {
        
            private SongViewModel songViewModel;
            private ArgPlayerSmallView argMusicPlayer;
        
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.single_song_item);
        
                // Initialize the ViewModel
                songViewModel = new ViewModelProvider(this).get(SongViewModel.class);
        
                // Initialize your ArgPlayerSmallView
                argMusicPlayer = findViewById(R.id.argmusicplayer);
        
                // Observe changes to the current song
                songViewModel.getCurrentSongLiveData().observe(this, song -> {
                    if (song != null) {
                        String url = song.getUrl();
                        ArgAudio audio = ArgAudio.createFromURL("test", "test", url);
                        argMusicPlayer.play(audio);
                    }
                });
        
                // Load and set the initial song (you can change this part according to your logic)
                List<Song> songList = songViewModel.getLiveSongData().getValue();
                if (songList != null && !songList.isEmpty()) {
                    songViewModel.setCurrentSong(songList.get(0));
                }
            }
        }
    

    Update your ViewModel to provide a method for setting the current song.

    When you want to change the currently playing song, use the ViewModel to set the new song.

    // Example of changing the currently playing song
    Song newSong = /* get the new song */;
    songViewModel.setCurrentSong(newSong);
    

    By using LiveData and the ViewModel, you can ensure that the currently playing song is retained across screen orientation changes, and your music player state remains intact.