Cara buat pemutar musik sumber daftar lagu dari penyimpanan internal dengan android studio
Table of Contents
Cara buat pemutar musik sumber daftar lagu dari penyimpanan internal
| aplikasi pemutar musik by javasetid |
Halo sahabat javasetid.com kali ini kita akan belajar cara buat aplikasi musik player yang bisa sobat gunakan untuk sebagai alternatif untuk penggunaan sendiri lebih praktis dan cocok untuk semua jenis android bisa juga digunakan untuk tablet ya, tanpa basa basi lagi kita langsung saja mulai prosesnya.
Langkah pertama silahkan buat projek baru dengan layout empty activity dan berikan nama sesuai projek sobat, setelah proses build projek selesai kita lanjut step berikutnya silahkan buka folder android manifest dan copas kan kode berikut ini.
1. AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Izin untuk layanan dan media -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.MANAGE_MEDIA"/>
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="35" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:requestLegacyExternalStorage="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/miola"
android:label="@string/app_name"
android:roundIcon="@drawable/miola"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
tools:targetApi="31">
<meta-data
android:name="unityads_game_id"
android:value="5892251" />
<meta-data
android:name="unityads_test_mode"
android:value="false" />
<!-- Aktivitas utama -->
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Deklarasi MusicService -->
<service android:name=".MusicService"
android:exported="false"
android:foregroundServiceType="mediaPlayback"
android:authorities="${applicationId}.unityadscontentprovider"
tools:ignore="Instantiatable" />
</application>
</manifest>
Setelah selesai kita lanjut kebagian java, silahkan buka foldernya lalu pilih java dan pilih MainActivity dan copaskan kode berikut.
2. MainActivity
package com.miolaplayer;
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import android.content.SharedPreferences; // Import SharedPreferences
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
// Import Unity Ads
import com.unity3d.ads.IUnityAdsInitializationListener;
import com.unity3d.ads.IUnityAdsLoadListener;
import com.unity3d.ads.IUnityAdsShowListener;
import com.unity3d.ads.UnityAds;
import com.unity3d.ads.UnityAdsShowOptions;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity implements
IUnityAdsInitializationListener {
private static final int REQUEST_PERMISSION = 1;
private RecyclerView recyclerView;
private ArrayList<String> songTitles;
private ArrayList<Uri> songUris;
private ArrayList<String> songArtists;
private ImageButton btnPlay, btnStop, btnNext, btnPrev, btnRepeat, btnShuffle;
private SeekBar seekBar, seekBarVolume;
private TextView txtCurrentSong, txtArtist, txtDuration;
private MediaPlayer mediaPlayer;
private int currentPositionIndex = 0;
private Handler handler = new Handler();
private AudioManager audioManager;
private boolean isRepeatOne = false;
private boolean isRepeatAll = false;
private boolean isShuffle = false;
// --- Unity Ads Variables ---
private String gameId = "5892251"; // Ganti dengan Game ID Unity Ads Anda
private String interstitialAdUnitId = "Interstitial_Android"; // Default Ad Unit ID
private Boolean testMode = true; // Set ke 'false' untuk produksi
// --- End Unity Ads Variables ---
// --- Frequency Capping Variables ---
private static final String PREFS_NAME = "MyMusicPlayerPrefs";
private static final String LAST_AD_SHOW_TIME_KEY = "lastAdShowTime";
private static final long AD_INTERVAL_MILLIS = 10 * 60 * 1000; // 10 menit dalam milidetik
private long lastAdShowTime = 0;
// --- End Frequency Capping Variables ---
private final Runnable updateSeekbar = new Runnable() {
@Override
public void run() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
seekBar.setProgress(mediaPlayer.getCurrentPosition());
txtDuration.setText(formatDuration(mediaPlayer.getCurrentPosition()) + " / " + formatDuration(mediaPlayer.getDuration()));
handler.postDelayed(this, 500);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
btnPlay = findViewById(R.id.btnPlay);
btnStop = findViewById(R.id.btnStop);
btnNext = findViewById(R.id.btnNext);
btnPrev = findViewById(R.id.btnPrev);
btnRepeat = findViewById(R.id.btnRepeat);
btnShuffle = findViewById(R.id.btnShuffle);
seekBar = findViewById(R.id.seekBar);
seekBarVolume = findViewById(R.id.seekBarVolume);
txtCurrentSong = findViewById(R.id.txtCurrentSong);
txtArtist = findViewById(R.id.txtArtist);
txtDuration = findViewById(R.id.txtDuration);
// Muat waktu terakhir iklan ditampilkan dari SharedPreferences
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
lastAdShowTime = prefs.getLong(LAST_AD_SHOW_TIME_KEY, 0);
// --- Inisialisasi Unity Ads ---
UnityAds.initialize(this, gameId, testMode, this);
// --- End Unity Ads Initialization ---
// Volume
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
seekBarVolume.setMax(maxVolume);
seekBarVolume.setProgress(currentVolume);
seekBarVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {}
@Override public void onStopTrackingTouch(SeekBar seekBar) {}
});
// Izin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_MEDIA_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{android.Manifest.permission.READ_MEDIA_AUDIO}, REQUEST_PERMISSION);
} else {
loadSongs();
}
} else {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION);
} else {
loadSongs();
}
}
// Tombol kontrol
btnPlay.setOnClickListener(v -> {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
btnPlay.setImageResource(android.R.drawable.ic_media_play);
} else if (mediaPlayer != null) {
mediaPlayer.start();
btnPlay.setImageResource(android.R.drawable.ic_media_pause);
handler.post(updateSeekbar);
} else if (!songUris.isEmpty()){
playSong(songUris.get(currentPositionIndex), songTitles.get(currentPositionIndex), songArtists.get(currentPositionIndex));
} else {
Toast.makeText(this, "Tidak ada lagu yang tersedia.", Toast.LENGTH_SHORT).show();
}
});
btnStop.setOnClickListener(v -> stopPlayback());
btnNext.setOnClickListener(v -> playNext());
btnPrev.setOnClickListener(v -> playPrevious());
btnRepeat.setOnClickListener(v -> {
if (!isRepeatOne && !isRepeatAll) {
isRepeatOne = true;
isRepeatAll = false;
Toast.makeText(this, "Repeat One", Toast.LENGTH_SHORT).show();
btnRepeat.setColorFilter(getResources().getColor(android.R.color.holo_orange_light));
} else if (isRepeatOne) {
isRepeatOne = false;
isRepeatAll = true;
Toast.makeText(this, "Repeat All", Toast.LENGTH_SHORT).show();
btnRepeat.setColorFilter(getResources().getColor(android.R.color.holo_blue_light));
} else {
isRepeatOne = false;
isRepeatAll = false;
Toast.makeText(this, "Repeat Off", Toast.LENGTH_SHORT).show();
btnRepeat.setColorFilter(getResources().getColor(android.R.color.white));
}
});
btnShuffle.setOnClickListener(v -> {
isShuffle = !isShuffle;
Toast.makeText(this, "Shuffle " + (isShuffle ? "ON" : "OFF"), Toast.LENGTH_SHORT).show();
if (isShuffle) {
btnShuffle.setColorFilter(getResources().getColor(android.R.color.holo_green_light));
} else {
btnShuffle.setColorFilter(getResources().getColor(android.R.color.white));
}
});
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && mediaPlayer != null) {
mediaPlayer.seekTo(progress);
}
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {}
@Override public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
// --- Unity Ads Initialization Listener ---
@Override
public void onInitializationComplete() {
Log.d("UnityAds", "Unity Ads initialization complete.");
// Periksa apakah sudah waktunya menampilkan iklan
long currentTime = System.currentTimeMillis();
if (currentTime - lastAdShowTime >= AD_INTERVAL_MILLIS) {
Log.d("UnityAds", "Time to show ad. Loading interstitial ad...");
loadInterstitialAd();
} else {
long timeLeft = AD_INTERVAL_MILLIS - (currentTime - lastAdShowTime);
Log.d("UnityAds", "Ad not due yet. Time left: " + TimeUnit.MILLISECONDS.toSeconds(timeLeft) + " seconds.");
}
}
@Override
public void onInitializationFailed(UnityAds.UnityAdsInitializationError error, String message) {
Log.e("UnityAds", "Unity Ads initialization failed: " + message);
Toast.makeText(this, "Gagal memuat iklan: " + message, Toast.LENGTH_LONG).show();
}
// --- End Unity Ads Initialization Listener ---
// --- Metode untuk Memuat dan Menampilkan Iklan Interstitial ---
private void loadInterstitialAd() {
if (!UnityAds.isInitialized()) {
Log.d("UnityAds", "Unity Ads not initialized yet. Cannot load ad.");
return;
}
UnityAds.load(interstitialAdUnitId, new IUnityAdsLoadListener() {
@Override
public void onUnityAdsAdLoaded(String adUnitId) {
Log.d("UnityAds", "Interstitial Ad loaded: " + adUnitId);
// Iklan berhasil dimuat, tampilkan segera
displayInterstitialAd();
}
@Override
public void onUnityAdsFailedToLoad(String adUnitId, UnityAds.UnityAdsLoadError error, String message) {
Log.e("UnityAds", "Interstitial Ad failed to load: " + message);
}
});
}
private void displayInterstitialAd() {
if (UnityAds.isInitialized()) {
UnityAds.show(this, interstitialAdUnitId, new UnityAdsShowOptions(), new IUnityAdsShowListener() {
@Override
public void onUnityAdsShowFailure(String adUnitId, UnityAds.UnityAdsShowError error, String message) {
Log.e("UnityAds", "Interstitial Ad show failed: " + message);
}
@Override
public void onUnityAdsShowStart(String adUnitId) {
Log.d("UnityAds", "Interstitial Ad show started: " + adUnitId);
}
@Override
public void onUnityAdsShowClick(String adUnitId) {
Log.d("UnityAds", "Interstitial Ad clicked: " + adUnitId);
}
@Override
public void onUnityAdsShowComplete(String adUnitId, UnityAds.UnityAdsShowCompletionState showCompletionState) {
Log.d("UnityAds", "Interstitial Ad show complete: " + adUnitId + " - State: " + showCompletionState);
// Setelah iklan ditampilkan (berhasil atau gagal), perbarui waktu terakhir iklan ditampilkan
lastAdShowTime = System.currentTimeMillis();
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
prefs.edit().putLong(LAST_AD_SHOW_TIME_KEY, lastAdShowTime).apply();
Log.d("UnityAds", "Last ad show time updated: " + lastAdShowTime);
}
});
} else {
Log.d("UnityAds", "Unity Ads SDK not initialized. Cannot show ad.");
}
}
// --- End Unity Ads Methods ---
private void loadSongs() {
songTitles = new ArrayList<>();
songUris = new ArrayList<>();
songArtists = new ArrayList<>();
Uri contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = {
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.ARTIST
};
Cursor cursor = getContentResolver().query(contentUri, projection,
MediaStore.Audio.Media.IS_MUSIC + "!= 0", null, MediaStore.Audio.Media.TITLE + " ASC");
if (cursor != null) {
int titleIndex = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int idIndex = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
int artistIndex = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
while (cursor.moveToNext()) {
long id = cursor.getLong(idIndex);
String title = cursor.getString(titleIndex);
String artist = cursor.getString(artistIndex);
Uri uri = Uri.withAppendedPath(contentUri, String.valueOf(id));
songTitles.add(title);
songUris.add(uri);
songArtists.add(artist != null && !artist.equals("<unknown>") ? artist : "Unknown Artist");
}
cursor.close();
}
MusicAdapterMediaStore adapter = new MusicAdapterMediaStore(this, songTitles, songUris, position -> {
currentPositionIndex = position;
playSong(songUris.get(position), songTitles.get(position), songArtists.get(position));
});
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
if (!songUris.isEmpty()) {
txtCurrentSong.setText(songTitles.get(currentPositionIndex));
txtArtist.setText(songArtists.get(currentPositionIndex));
} else {
txtCurrentSong.setText("No Song Available");
txtArtist.setText("");
}
}
private void playNext() {
if (songUris == null || songUris.isEmpty()) {
Toast.makeText(this, "Tidak ada lagu yang tersedia.", Toast.LENGTH_SHORT).show();
return;
}
if (isShuffle) {
currentPositionIndex = new Random().nextInt(songUris.size());
} else if (currentPositionIndex < songUris.size() - 1) {
currentPositionIndex++;
} else if (isRepeatAll) {
currentPositionIndex = 0;
} else {
stopPlayback();
return;
}
playSong(songUris.get(currentPositionIndex), songTitles.get(currentPositionIndex), songArtists.get(currentPositionIndex));
}
private void playPrevious() {
if (songUris == null || songUris.isEmpty()) {
Toast.makeText(this, "Tidak ada lagu yang tersedia.", Toast.LENGTH_SHORT).show();
return;
}
if (currentPositionIndex > 0) {
currentPositionIndex--;
} else {
currentPositionIndex = songUris.size() - 1;
}
playSong(songUris.get(currentPositionIndex), songTitles.get(currentPositionIndex), songArtists.get(currentPositionIndex));
}
private void playSong(Uri uri, String title, String artist) {
stopPlayback();
mediaPlayer = MediaPlayer.create(this, uri);
if (mediaPlayer != null) {
mediaPlayer.start();
seekBar.setMax(mediaPlayer.getDuration());
btnPlay.setImageResource(android.R.drawable.ic_media_pause);
handler.post(updateSeekbar);
txtCurrentSong.setText(title);
txtArtist.setText(artist);
Toast.makeText(this, "Playing: " + title, Toast.LENGTH_SHORT).show();
mediaPlayer.setOnCompletionListener(mp -> {
if (isRepeatOne) {
playSong(uri, title, artist);
} else {
playNext();
}
});
}
}
private void stopPlayback() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
seekBar.setProgress(0);
txtDuration.setText("00:00 / 00:00");
btnPlay.setImageResource(android.R.drawable.ic_media_play);
handler.removeCallbacks(updateSeekbar);
txtCurrentSong.setText("No Song Selected");
txtArtist.setText("Unknown Artist");
}
}
private String formatDuration(int millis) {
long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(minutes);
return String.format("%02d:%02d", minutes, seconds);
}
@Override
protected void onDestroy() {
stopPlayback();
super.onDestroy();
}
@Override
public void onBackPressed() {
moveTaskToBack(true);
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSION && grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadSongs();
} else {
Toast.makeText(this, "Izin diperlukan untuk membaca file audio.", Toast.LENGTH_SHORT).show();
}
}
}
silahkan buatkan class dan ketikan " MusicAdapterMediaStore " dan copaskan kode berikut ini
package com.miolaplayer;
import android.content.Context;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.io.IOException;
import java.util.List;
public class MusicAdapterMediaStore extends RecyclerView.Adapter<MusicAdapterMediaStore.ViewHolder> {
private final Context context;
private final List<String> songTitles;
private final List<Uri> songUris;
private final OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(int position);
}
public MusicAdapterMediaStore(Context context, List<String> songTitles, List<Uri> songUris, OnItemClickListener listener) {
this.context = context;
this.songTitles = songTitles;
this.songUris = songUris;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_song, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
String title = songTitles.get(position);
Uri uri = songUris.get(position);
holder.title.setText(title);
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(context, uri);
String artist = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
holder.artist.setText((artist != null && !artist.isEmpty()) ? artist : "Artis tidak diketahui");
if (durationStr != null) {
long durationMs = Long.parseLong(durationStr);
int minutes = (int) (durationMs / 1000) / 60;
int seconds = (int) (durationMs / 1000) % 60;
holder.duration.setText(String.format("%02d:%02d", minutes, seconds));
} else {
holder.duration.setText("00:00");
}
holder.icon.setImageResource(R.drawable.ic_music_note); // default icon
} catch (Exception e) {
holder.artist.setText("Artis tidak diketahui");
holder.duration.setText("00:00");
holder.icon.setImageResource(R.drawable.ic_music_note);
} finally {
try {
retriever.release();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
holder.itemView.setOnClickListener(v -> listener.onItemClick(position));
}
@Override
public int getItemCount() {
return songTitles.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView title, artist, duration;
ImageView icon;
public ViewHolder(@NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.song_title);
artist = itemView.findViewById(R.id.song_artist);
duration = itemView.findViewById(R.id.song_duration);
icon = itemView.findViewById(R.id.song_icon);
}
}
}
silahkan buatkan class lagi dan ketikan " MusicService " dan copaskan kode berikut ini
package com.miolaplayer;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.media.app.NotificationCompat.MediaStyle; // Perbaikan ini penting!
public class MusicService extends Service implements MediaPlayer.OnCompletionListener {
private MediaPlayer mediaPlayer;
private String songTitle = "Unknown";
private Uri songUri;
public static final String ACTION_PLAY = "com.miolaplayer.ACTION_PLAY";
public static final String ACTION_PAUSE = "com.miolaplayer.ACTION_PAUSE";
public static final String ACTION_STOP = "com.miolaplayer.ACTION_STOP"; // Tambahkan ACTION_STOP
private static final String CHANNEL_ID = "MiolaPlayerChannel";
private static final int NOTIFICATION_ID = 123;
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel(); // Buat saluran notifikasi untuk Android 8.0+
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
String action = intent.getAction();
if (action != null) {
switch (action) {
case ACTION_PLAY:
String uriString = intent.getStringExtra("song_uri");
String title = intent.getStringExtra("song_title");
if (uriString != null && title != null) {
songUri = Uri.parse(uriString);
songTitle = title;
playMusic();
}
break;
case ACTION_PAUSE:
pauseMusic();
break;
case ACTION_STOP: // Tangani aksi STOP
stopMusic();
break;
// Anda bisa menambahkan ACTION_NEXT, ACTION_PREVIOUS jika ingin
// mengontrol dari notifikasi, tapi ini akan membutuhkan BroadcastReceiver
// di MainActivity untuk memicu pemutaran lagu berikutnya/sebelumnya.
}
} else {
// Ini akan dijalankan saat startService pertama kali dipanggil dari MainActivity
// (tanpa aksi spesifik, hanya untuk memulai pemutaran lagu awal)
String uriString = intent.getStringExtra("song_uri");
String title = intent.getStringExtra("song_title");
if (uriString != null && title != null) {
songUri = Uri.parse(uriString);
songTitle = title;
playMusic();
}
}
}
// Pastikan layanan terus berjalan di latar belakang
return START_STICKY;
}
private void playMusic() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
try {
mediaPlayer = MediaPlayer.create(this, songUri);
if (mediaPlayer != null) {
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.start();
Log.d("MusicService", "Playing: " + songTitle);
showNotification(); // Tampilkan notifikasi saat musik diputar
}
} catch (Exception e) {
Log.e("MusicService", "Error playing music: " + e.getMessage());
e.printStackTrace();
}
}
private void pauseMusic() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
Log.d("MusicService", "Paused: " + songTitle);
showNotification(); // Perbarui notifikasi (misal, tampilkan tombol play)
}
}
private void stopMusic() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
stopForeground(true); // Hapus notifikasi dan hentikan foreground service
stopSelf(); // Hentikan layanan itu sendiri
Log.d("MusicService", "Music stopped.");
}
// Callback saat pemutaran selesai
@Override
public void onCompletion(MediaPlayer mp) {
Log.d("MusicService", "Song finished: " + songTitle);
// Di sini Anda bisa menambahkan logika untuk memutar lagu berikutnya
// Namun, ini akan lebih kompleks karena membutuhkan komunikasi kembali ke MainActivity
// Untuk demo ini, kita hanya akan menghentikan layanan.
stopMusic();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null; // Kita tidak menggunakan binding untuk layanan ini
}
@Override
public void onDestroy() {
super.onDestroy();
stopMusic(); // Pastikan musik berhenti saat layanan dihancurkan
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "MiolaPlayer Channel";
String description = "Channel for MiolaPlayer notifications";
int importance = NotificationManager.IMPORTANCE_LOW; // Low importance agar tidak terlalu mengganggu
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
private void showNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Aksi untuk tombol kontrol di notifikasi
Intent playPauseIntent = new Intent(this, MusicService.class);
playPauseIntent.setAction(mediaPlayer != null && mediaPlayer.isPlaying() ? ACTION_PAUSE : ACTION_PLAY);
PendingIntent playPausePendingIntent = PendingIntent.getService(
this, 0, playPauseIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
Intent stopIntent = new Intent(this, MusicService.class);
stopIntent.setAction(ACTION_STOP);
PendingIntent stopPendingIntent = PendingIntent.getService(
this, 0, stopIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Ambil ikon seni album jika ada (opsional)
Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); // Ganti dengan ikon aplikasi Anda
// Anda bisa mencoba mendapatkan seni album yang sebenarnya dari MediaStore jika ada
// Bangun notifikasi
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(songTitle)
.setContentText("MiolaPlayer is playing...")
.setSmallIcon(R.drawable.ic_music_note) // Ganti dengan ikon notifikasi Anda
.setLargeIcon(largeIcon)
.setContentIntent(pendingIntent)
.setOnlyAlertOnce(true) // Notifikasi tidak berbunyi/bergetar setiap update
.setPriority(NotificationCompat.PRIORITY_LOW) // Prioritas rendah
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) // Tampilkan di lock screen
.addAction(android.R.drawable.ic_media_previous, "Previous", null) // Tombol Previous (fungsi belum diimplementasikan di service)
.addAction(mediaPlayer != null && mediaPlayer.isPlaying() ? android.R.drawable.ic_media_pause : android.R.drawable.ic_media_play,
mediaPlayer != null && mediaPlayer.isPlaying() ? "Pause" : "Play",
playPausePendingIntent) // Tombol Play/Pause
.addAction(android.R.drawable.ic_media_next, "Next", null) // Tombol Next (fungsi belum diimplementasikan di service)
.addAction(android.R.drawable.ic_delete, "Stop", stopPendingIntent) // Tombol Stop
.setStyle(new MediaStyle()
.setShowActionsInCompactView(1, 2) // Tampilkan tombol Play/Pause dan Stop di tampilan ringkas
.setMediaSession(null)); // MediaSession opsional untuk kontrol yang lebih canggih
startForeground(NOTIFICATION_ID, builder.build());
}
}
silahkan buatkan class lagi dan ketikan " StopServiceReceiver " dan copaskan kode berikut ini
5. StopServiceReceiver
package com.miolaplayer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class StopServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
context.stopService(new Intent(context, MusicService.class));
}
}
Nah pada step ini kita sudah selesai untuk bagian java nya maka kita lanjut bagian "RES Folder" untuk res folder silahkan bagian Drawable pastikan tambahkan kode Drawable source file berikut :
a. baseline_13mp_24
b. baseline_brightness_1_24
c. baseline_repeat_on_24
d. ic_music_note
e. ic_stop
f. rounded_3mp_24
g. sharp_autostop_24
h. icon aplikasi berupa gambar jpg atau png
6. Kode bagian Drawable
bagian a :
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,18.5h-1.5L10.5,14h-1v3L8,17v-3L7,14v4.5L5.5,18.5v-5c0,-0.55 0.45,-1 1,-1L11,12.5c0.55,0 1,0.45 1,1v5zM15.5,18.5L14,18.5v-6h3.5c0.55,0 1,0.45 1,1L18.5,16c0,0.55 -0.45,1 -1,1h-2v1.5zM10,5.5v6L8.5,11.5L8.5,7L7,7L7,5.5h3zM16.5,10.5c0,0.55 -0.45,1 -1,1L12,11.5L12,10h3L15,9h-2L13,8h2L15,7h-3L12,5.5h3.5c0.55,0 1,0.45 1,1v4zM15.5,14L17,14v1.5h-1.5z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:fillType="evenOdd" android:pathData="M21,1L3,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,3c0,-1.1 -0.9,-2 -2,-2zM7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>
bagian e :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>
bagian f :
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M520,400L430,400Q417,400 408.5,408.5Q400,417 400,430Q400,443 408.5,451.5Q417,460 430,460L540,460Q557,460 568.5,448.5Q580,437 580,420L580,260Q580,243 568.5,231.5Q557,220 540,220L430,220Q417,220 408.5,228.5Q400,237 400,250Q400,263 408.5,271.5Q417,280 430,280L520,280L520,320L460,320Q452,320 446,326Q440,332 440,340Q440,348 446,354Q452,360 460,360L520,360L520,400ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM200,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200ZM300,560L340,560L340,650Q340,663 348.5,671.5Q357,680 370,680Q383,680 391.5,671.5Q400,663 400,650L400,560L440,560L440,710Q440,723 448.5,731.5Q457,740 470,740Q483,740 491.5,731.5Q500,723 500,710L500,540Q500,523 488.5,511.5Q477,500 460,500L280,500Q263,500 251.5,511.5Q240,523 240,540L240,710Q240,723 248.5,731.5Q257,740 270,740Q283,740 291.5,731.5Q300,723 300,710L300,560ZM600,680L680,680Q697,680 708.5,668.5Q720,657 720,640L720,540Q720,523 708.5,511.5Q697,500 680,500L570,500Q557,500 548.5,508.5Q540,517 540,530L540,710Q540,723 548.5,731.5Q557,740 570,740Q583,740 591.5,731.5Q600,723 600,710L600,680ZM600,620L600,560L660,560L660,620L600,620Z"/>
</vector>
bagian g :
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M360,600L360,360L600,360L600,600L360,600ZM480,920Q372,920 277.5,870.5Q183,821 120,732L120,840L40,840L40,600L280,600L280,680L182,680Q233,755 311.5,797.5Q390,840 480,840Q595,840 688.5,774Q782,708 820,599L898,617Q853,753 738,836.5Q623,920 480,920ZM42,440Q49,373 74,311.5Q99,250 143,198L200,255Q168,296 148,342.5Q128,389 123,440L42,440ZM256,199L199,142Q252,98 313,72.5Q374,47 440,42L440,122Q389,127 343,147Q297,167 256,199ZM705,199Q664,167 617.5,147Q571,127 520,122L520,42Q587,48 648.5,73Q710,98 762,142L705,199ZM838,440Q833,389 813,342.5Q793,296 761,255L818,198Q862,250 887,311.5Q912,373 918,440L838,440Z"/>
</vector>
setelah kode Drawable selesai kita lanjut ke bagian "Layout" silahkan sisipak kode activity_main.xml dan item_song.xml.
7 . Activity_Main
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#212121"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/headerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:background="#303030">
<TextView
android:id="@+id/txtCurrentSong"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="No Song Selected"
android:textColor="#FFFFFF"
android:textSize="22sp"
android:textStyle="bold"
android:gravity="center_horizontal"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:focusable="true"
android:focusableInTouchMode="true" />
<TextView
android:id="@+id/txtArtist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Unknown Artist"
android:textColor="#BDBDBD"
android:textSize="16sp"
android:gravity="center_horizontal"
android:layout_marginTop="4dp"
android:singleLine="true"
android:ellipsize="end" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/headerLayout"
android:layout_above="@id/controlsLayout"
android:padding="8dp"
android:scrollbars="vertical" />
<LinearLayout
android:id="@+id/controlsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:padding="16dp"
android:background="#303030"
android:elevation="8dp">
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progressTint="#FF5722"
android:thumbTint="#FF5722" />
<TextView
android:id="@+id/txtDuration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00 / 00:00"
android:textColor="#BDBDBD"
android:textSize="12sp"
android:layout_gravity="end"
android:layout_marginEnd="4dp"
android:layout_marginBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageButton
android:id="@+id/btnShuffle"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@android:drawable/ic_menu_sort_by_size"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Shuffle"
android:padding="8dp" />
<ImageButton
android:id="@+id/btnPrev"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@android:drawable/ic_media_previous"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Previous"
android:padding="8dp" />
<ImageButton
android:id="@+id/btnPlay"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@android:drawable/ic_media_play"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Play/Pause"
android:padding="8dp" />
<ImageButton
android:id="@+id/btnNext"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@android:drawable/ic_media_next"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Next"
android:padding="8dp" />
<ImageButton
android:id="@+id/btnRepeat"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@android:drawable/ic_popup_sync"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Repeat"
android:padding="8dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@android:drawable/ic_lock_silent_mode_off"
android:contentDescription="Volume Icon" />
<SeekBar
android:id="@+id/seekBarVolume"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:progressTint="#2196F3"
android:thumbTint="#2196F3" />
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@android:drawable/ic_lock_silent_mode"
android:contentDescription="Volume Max Icon" />
<ImageButton
android:id="@+id/btnStop"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@android:drawable/ic_delete"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Stop"
android:padding="8dp"
android:layout_marginStart="8dp"
android:visibility="gone" /> </LinearLayout>
</LinearLayout>
</RelativeLayout>
jangan lupa tambahkan "layout resource file"
8. item_song
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="12dp"
app:cardElevation="6dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp">
<ImageView
android:id="@+id/song_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_music_note"
android:scaleType="centerCrop"
android:background="@drawable/rounded_3mp_24" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/song_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Judul Lagu"
android:textSize="16sp"
android:textStyle="bold"
android:maxLines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/song_artist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Artis tidak diketahui"
android:textSize="14sp"
android:textColor="#777" />
<TextView
android:id="@+id/song_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"
android:textSize="12sp"
android:textColor="#999" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
setelah ini kita lanjut ke bagian "values" pada bagian color masukin kode berikut
9. color
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="biru">#0C24AF</color>
<color name="hijau">#4CAF50</color>
<color name="kuning">#FFEB3B</color>
<color name="ungu">#9C27B0</color>
<color name="abuabu">#565252</color>
<color name="orange">#FF5722</color>
<color name="merah">#CA0322</color>
</resources>
Setelah bagian color selesai maka kita lanjut ketahap berikutnya yaitu
10. build.gradle.kts
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.miolaplayer"
compileSdk = 35
defaultConfig {
applicationId = "com.miolaplayer"
minSdk = 25
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
implementation (libs.media)
implementation(libs.androidx.cardview)
implementation (libs.unity.ads.v480)
implementation(libs.appcompat)
implementation (libs.unity.ads.v4150)
implementation(libs.unity.ads)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
[versions]
agp = "8.8.0"
cardviewVersion = "1.0.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
appcompat = "1.7.1"
material = "1.12.0"
activity = "1.10.1"
constraintlayout = "2.2.1"
media = "1.7.0"
unity3dUnityAds = "4.8.0"
unityads = "4.9.2"
unityAdsVersion = "4.15.0"
[libraries]
androidx-cardview = { module = "androidx.cardview:cardview", version.ref = "cardviewVersion" }
media = { module = "androidx.media:media", version.ref = "media" }
unity-ads = { group = "com.unity3d.ads", name = "unity-ads", version.ref = "unityads" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
unity-ads-v4150 = { module = "com.unity3d.ads:unity-ads", version.ref = "unityAdsVersion" }
unity-ads-v480 = { module = "com.unity3d.ads:unity-ads", version.ref = "unity3dUnityAds" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Nah, jika ke 10 bagian ini sudah kamu terapkan maka sudah bisa menciptakan aplikasi pemutar musik tersebut
KESIMPULAN
menbuat aplikasi sendiri dapat dilakukan dengan menggunakana android studio, untuk itu bagi kamu yang baru belajar bisa gunakan source code ini sebagai pijakan untuk membuat aplikasi lainnya, jangan malas belajar ya dan buat kamu yang butuh sc ini dapat kirim pesan ke admin melalu kolom komentar atau whatsApp ya.
Posting Komentar