MỤC TIÊU YÊU CẦU
Mục Tiêu
1 Giao diện người dùng thân thiện:
Thiết kế giao diện đơn giản, dễ sử dụng với các biểu tượng rõ ràng. Đảm bảo giao diện phù hợp với mọi đối tượng người dùng, từ người mới sử dụng đến người dùng kỳ cựu.
2 Chức năng cơ bản của ứng dụng:
Phát nhạc: Hỗ trợ phát các tập tin âm thanh ở nhiều định dạng phổ biến (MP3,
Tìm kiếm nhạc: Cho phép người dùng tìm kiếm theo nghệ sĩ, album, bài hát, thể loại, và hiển thị kết quả chính xác.
Danh sách phát yêu thích: Cho phép người dùng tạo danh sách nhạc yêu thích để dễ dàng truy cập. Điều chỉnh âm lượng và chất lượng âm thanh: Cung cấp thanh điều chỉnh âm lượng và tùy chọn chất lượng âm thanh.
Hỗ trợ tai nghe và loa ngoài: Tự động chuyển đổi giữa loa ngoài và tai nghe khi cắm hoặc rút tai nghe.
3 Tính năng nâng cao (tùy chọn):
Chia sẻ nhạc: Hỗ trợ chia sẻ bài hát với bạn bè qua mạng xã hội hoặc ứng dụng nhắn tin.
Gợi ý nhạc: Dựa trên sở thích của người dùng, gợi ý các bài hát phù hợp.
Hiển thị lời bài hát: Hiển thị lời bài hát khi người dùng phát nhạc.
Tính năng phụ trợ: Hẹn giờ tắt nhạc, điều khiển bằng giọng nói, vv.
4 Kiểm tra ổn định và hiệu suất:
Thử nghiệm ứng dụng trên các thiết bị di động phổ biến
(Android, iOS) để đảm bảo hoạt động ổn định và mượt mà.
Xử lý các vấn đề liên quan đến tốc độ phản hồi, tiêu thụ tài nguyên, và khả năng phát nhạc liên tục.
Tổng quát: Phát triển một ứng dụng nghe nhạc di động hoàn chỉnh, đáp ứng nhu cầu giải trí và nghe nhạc của người dùng.
Yêu Cầu
1 Yêu cầu về chức năng: Ứng dụng phải đáp ứng đầy đủ các chức năng cơ bản của một ứng dụng nghe nhạc di động như đã nêu ở mục trên.
Các chức năng nâng cao (nếu có) phải được triển khai một cách hoàn chỉnh và hiệu quả. Ứng dụng phải tương thích với nhiều phiên bản hệ điều hành Android phổ biến.
2 Yêu cầu về kỹ thuật: Ứng dụng được phát triển bằng ngôn ngữ lập trình Java.
Sử dụng Android Studio làm môi trường phát triển chính.
Thiết kế giao diện người dùng đẹp mắt, thân thiện và dễ sử dụng.
Sử dụng các thư viện và API phù hợp để tối ưu hóa hiệu suất và tính năng của ứng dụng.
Viết code tuân theo các quy tắc lập trình tốt nhất, đảm bảo tính bảo mật và dễ bảo trì.
3 Yêu cầu về tài liệu:
Báo cáo tài liệu đầy đủ, chi tiết về quá trình thiết kế, phát triển và thử nghiệm ứng dụng.
Bao gồm hướng dẫn sử dụng ứng dụng dành cho người dùng Cung cấp mã nguồn đầy đủ của ứng dụng.
ĐỐI TƯỢNG SỬ DỤNG
Nhà phát triển ứng dụng
Khả năng tùy chỉnh, mở rộng và tích hợp:
Nhà phát triển ứng dụng di động có quyền truy cập vào mã nguồn của ứng dụng, cho phép họ tùy chỉnh giao diện, bổ sung tính năng mới hoặc tích hợp ứng dụng với các hệ thống khác. Điều này mang lại lợi ích cho các nhà phát triển muốn tạo ra trải nghiệm người dùng độc đáo, đáp ứng nhu cầu cụ thể của họ hoặc kết nối ứng dụng với các dịch vụ và công cụ khác.
Người quản lý nội dung âm nhạc
Quản lý nội dung toàn diện:
Người quản lý nội dung âm nhạc có quyền truy cập vào các công cụ quản lý mạnh mẽ để tải lên, quản lý và phân loại các bài hát, album và nghệ sĩ.
Họ có thể thêm thông tin chi tiết về âm nhạc, chỉnh sửa metadata, tạo danh sách phát và sắp xếp nội dung theo nhiều tiêu chí khác nhau. Điều này giúp đảm bảo tính chính xác, nhất quán và dễ dàng tìm kiếm nội dung âm nhạc cho người dùng.
Quyền kiểm soát nội dung:
Người quản lý nội dung âm nhạc có thể kiểm soát quyền truy cập vào nội dung, cho phép họ giới hạn quyền truy cập vào một số nội dung nhất định hoặc cung cấp quyền truy cập độc quyền cho người dùng cụ thể. Điều này hữu ích cho việc quản lý bản quyền, phân phối nội dung độc quyền hoặc tạo các gói đăng ký trả phí.
Phân tích dữ liệu: Ứng dụng có thể cung cấp cho người quản lý nội dung âm nhạc dữ liệu về hiệu suất của nội dung, chẳng hạn như số lần phát, lượt tải xuống và lượt thích.
Dữ liệu này giúp họ hiểu rõ sở thích của người dùng, đánh giá hiệu quả của các chiến dịch marketing và đưa ra quyết định sáng suốt về việc quản lý nội dung.
Người dùng cuối (End Users)
Ứng dụng nghe nhạc di động có thể được sử dụng bởi rất nhiều đối tượng, bao gồm:
Học sinh, sinh viên: o Giải trí trong giờ nghỉ ngơi, thư giãn sau giờ học căng thẳng. o Nghe nhạc khi học tập, giúp tăng khả năng tập trung và ghi nhớ.
Người đi làm: o Nghe nhạc khi di chuyển trên đường đi làm, giúp giảm bớt căng thẳng và mệt mỏi. o Nghe nhạc trong giờ nghỉ trưa để thư giãn và giải trí.
Người cao tuổi: o Nghe nhạc để thư giãn, giảm bớt stress và cải thiện sức khỏe tinh thần. o Nghe nhạc quen thuộc để gợi nhớ về những kỷ niệm đẹp trong quá khứ.
Các vận động viên: o Nghe nhạc trước khi tập luyện để tăng cường động lực và tinh thần. o Nghe nhạc khi tập luyện để giúp cơ thể vận động nhịp nhàng và hiệu quả hơn.
Nghệ sĩ: o Nghe nhạc để lấy cảm hứng sáng tác. o Nghe nhạc của các nghệ sĩ khác để học hỏi và trau dồi kỹ năng âm nhạc.
Ngoài ra, ứng dụng này còn có thể được sử dụng cho nhiều mục đích khác nhau như:
Giáo dục: o Nghe nhạc tiếng Anh để học ngoại ngữ. o Nghe nhạc thiếu nhi để giáo dục trẻ em.
Kinh doanh: o Phát nhạc trong cửa hàng để tạo bầu không khí mua sắm thoải mái. o Sử dụng nhạc quảng cáo để thu hút khách hàng.
Y tế: o Nghe nhạc trị liệu để giảm stress, lo âu và cải thiện tâm trạng. o Nghe nhạc ru ngủ để giúp trẻ em dễ ngủ hơn.
Nhìn chung, ứng dụng nghe nhạc di động có thể được sử dụng bởi mọi người ở mọi lứa tuổi và mọi ngành nghề Với tính đa dạng và tiện lợi, ứng dụng nghe nhạc di động đã trở thành một phần không thể thiếu trong cuộc sống của nhiều người.
PHÂN TÍCH THIẾT KẾ HỆ THỐNG
GIAO DIỆN ỨNG DỤNG
Giao diện Đăng nhập, Đăng ký
Giao diện Quên mật khẩu và Đổi mật khẩu
Giao diện Trang chủ người dùng
Giao diện Chức năng người dùng
Giao diện Phát nhạc và List nhạc đang phát
Giao diện Thông báo phát nhạc
Giao diện Nhạc đang phát mà trở về màn hình chính
Giao diện Danh sách (List) tất cả nhạc của hệ thống
Giao diện Các bài hát nổi bật
Giao diện Các bài hát mới nhất
Giao diện Kết nối với các diễn đàn và mạng xã hội
Giao diện Đóng góp ý kiến về ứng dụng
Giao diện Người quản trị (admin)
Giao diện Thêm nhạc dành cho Người quản trị
Giao diện Cập nhật nhạc dành cho Người quản trị
CÁC CHỨC NĂNG CỦA ỨNG DỤNG
Kết nối với firebase (Sử lý trong hệ thống)
* Mô tả Để kết nối ứng dụng với Firebase ta cần khởi tạo 1 dự án trên Firebase và kết nối ứng dụng với Firebase thông qua tên của gói (name-package của dự án) và thêm file services.json của firebase bằng cách tải về và thêm vào thư mục có tên là app để thực hiện kết nối.
Tiếp theo cần dán đường link dự án trên Firebase vào file MyApplication File này được tạo ra để kết nối với Firebase Và lấy các liên kết đến các bảng dữ liệu trên Firebase. Để thực hiện các việc như lấy dữ liệu, thêm dữ liệu và xóa dữ liệu.
*Code package com.medium.music; import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.os.Build; import com.medium.music.prefs.DataStoreManager; import com.google.firebase.FirebaseApp; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; public class MyApplication extends Application {
// Firebase url public static final String FIREBASE_URL = "https://appmusic-f178f-default-rtdb.firebaseio.com"; public static final String CHANNEL_ID = "channel_music_basic_id"; private static final String CHANNEL_NAME = "channel_music_basic_name"; private FirebaseDatabase mFirebaseDatabase; public static MyApplication get(Context context) { return (MyApplication) context.getApplicationContext();
@Override public void onCreate() { super.onCreate();
FirebaseApp.initializeApp(this); mFirebaseDatabase = FirebaseDatabase.getInstance(FIREBASE_URL); createChannelNotification();
} private void createChannelNotification() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel);
} public DatabaseReference getSongsDatabaseReference() { return mFirebaseDatabase.getReference("/songs");
} public DatabaseReference getFeedbackDatabaseReference() { return mFirebaseDatabase.getReference("/feedback");
} public DatabaseReference getCountViewDatabaseReference(long songId) { return FirebaseDatabase.getInstance().getReference("/songs/" + songId + "/count");
Đăng ký
Nếu người dùng chưa có tài khoản thì thực hiện việc đăng ký tài khoản tại giao diện đăng ký Khi nhập đầy đủ thông tin yêu cầu Người dùng nhấn nút đăng ký, dữ liệu sẽ được gửi chức năng thêm người dùng, tại chức năng này sẽ thực hiện yêu cầu thêm người dùng vào cơ sở dữ liệu trên Firebase từ các dữ liệu do người dùng nhập
*Code package com.medium.music.activity; import android.os.Bundle; import android.widget.Toast; import com.medium.music.R; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.ActivitySignUpBinding; import com.medium.music.model.User; import com.medium.music.prefs.DataStoreManager; import com.medium.music.utils.StringUtil; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; public class SignUpActivity extends BaseActivity { private ActivitySignUpBinding mActivitySignUpBinding;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivitySignUpBinding =
26 setContentView(mActivitySignUpBinding.getRoot()); mActivitySignUpBinding.rdbUser.setChecked(true); mActivitySignUpBinding.imgBack.setOnClickListener(v -> onBackPressed()); mActivitySignUpBinding.layoutSignIn.setOnClickListener(v -> finish()); mActivitySignUpBinding.btnSignUp.setOnClickListener(v -> onClickValidateSignUp());
String strEmail = mActivitySignUpBinding.edtEmail.getText().toString().trim();
String strPassword = mActivitySignUpBinding.edtPassword.getText().toString().trim(); if (StringUtil.isEmpty(strEmail)) {
Toast.makeText(SignUpActivity.this, getString(R.string.msg_email_require),
} else if (StringUtil.isEmpty(strPassword)) {
Toast.makeText(SignUpActivity.this, getString(R.string.msg_password_require),
} else if (!StringUtil.isValidEmail(strEmail)) {
Toast.makeText(SignUpActivity.this, getString(R.string.msg_email_invalid),
} else { if (mActivitySignUpBinding.rdbAdmin.isChecked()) { if (! strEmail.contains(Constant.ADMIN_EMAIL_FORMAT)) {
Toast.makeText(SignUpActivity.this, getString(R.string.msg_email_invalid_admin),
} if (strEmail.contains(Constant.ADMIN_EMAIL_FORMAT)) { Toast.makeText(SignUpActivity.this, getString(R.string.msg_email_invalid_user),
27 private void signUpUser(String email, String password) { showProgressDialog(true);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(); firebaseAuth.createUserWithEmailAndPassword(email, password)
addOnCompleteListener(this, task -> { showProgressDialog(false); if (task.isSuccessful()) {
FirebaseUser user = firebaseAuth.getCurrentUser(); if (user != null) {
User(user.getEmail(), password); if (user.getEmail() != null && user.getEmail().contains(Constant.ADMIN_EMAIL_FORMAT)) { userObject.setAdmin(true);
Toast.makeText(SignUpActivity.this, getString(R.string.msg_sign_up_error),
Đăng nhập
Nếu người dùng đã có tài khoản thì thực hiện việc đăng nhập tài khoản tại giao diện đăng nhập Khi nhập đầy đủ thông tin yêu cầu Người dùng nhấn nút đăng nhập, dữ liệu sẽ được gửi đến chức xác thực người dùng, tại chức năng này sẽ thực hiện gửi yêu cầu xác thực người dùng có trong cơ sở dữ liệu trên Firebase không từ các dữ liệu do người dùng nhập Nếu thành công thì gừi dùng được đưa thẳng đến giao diện chính khi đăng nhập hoàn tất Nếu sai thì hiển thị thông báo người dùng sai tài khoản hoặc mật khẩu.
*Code package com.medium.music.activity; import android.os.Bundle; import android.widget.Toast; import com.medium.music.R; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.ActivitySignInBinding; import com.medium.music.model.User;
28 import com.medium.music.prefs.DataStoreManager; import com.medium.music.utils.StringUtil; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; public class SignInActivity extends BaseActivity { private ActivitySignInBinding mActivitySignInBinding;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivitySignInBinding ActivitySignInBinding.inflate(getLayoutInflater()); setContentView(mActivitySignInBinding.getRoot());
// mặc định khởi đầu là check người dùng mActivitySignInBinding.rdbUser.setChecked(true); mActivitySignInBinding.layoutSignUp.setOnClickListener( v -> GlobalFunction.startActivity(SignInActivity.this, SignUpActivity.class));
// mActivitySignInBinding.btnSignIn.setOnClickListener(v -> onClickValidateSignIn()); mActivitySignInBinding.tvForgotPassword.setOnClickListener(v -> onClickForgotPassword());
GlobalFunction.startActivity(this, ForgotPasswordActivity.class); } private void onClickValidateSignIn() {
String strEmail mActivitySignInBinding.edtEmail.getText().toString().trim();
String strPassword mActivitySignInBinding.edtPassword.getText().toString().trim(); if (StringUtil.isEmpty(strEmail)) {
Toast.makeText(SignInActivity.this, getString(R.string.msg_email_require), Toast.LENGTH_SHORT).show();
} else if (StringUtil.isEmpty(strPassword)) {
Toast.makeText(SignInActivity.this, getString(R.string.msg_password_require), Toast.LENGTH_SHORT).show(); } else if (!StringUtil.isValidEmail(strEmail)) {
Toast.makeText(SignInActivity.this, getString(R.string.msg_email_invalid), Toast.LENGTH_SHORT).show();
} else { if (mActivitySignInBinding.rdbAdmin.isChecked()) { if (!strEmail.contains(Constant.ADMIN_EMAIL_FORMAT)) { Toast.makeText(SignInActivity.this, getString(R.string.msg_email_invalid_admin), Toast.LENGTH_SHORT).show(); } else { signInUser(strEmail, strPassword);
} if (strEmail.contains(Constant.ADMIN_EMAIL_FORMAT)) {
Toast.makeText(SignInActivity.this, getString(R.string.msg_email_invalid_user), Toast.LENGTH_SHORT).show(); } else {
} private void signInUser(String email, String password) { showProgressDialog(true);
// thực hiện quá trình đăng nhập từ phía firebase khi truyền vào gmail và pass firebaseAuth.signInWithEmailAndPassword(email, password)
//code này thiết lập một lắng nghe để xử lý kết quả của tác vụ đăng nhập.
// task : Biến lưu trữ kết quả của tác vụ đăng nhập.
addOnCompleteListener(this, task -> { showProgressDialog(false); if (task.isSuccessful()) {
FirebaseUser user = firebaseAuth.getCurrentUser(); if (user != null) {
User userObject = new User(user.getEmail(), password);
//Kiểm tra xem email của người dùng có chứa định dạng email của quản trị viên (kiểm tra với hằng số Constant.ADMIN_EMAIL_FORMAT đã được định nghĩa) if (user.getEmail() != null && user.getEmail().contains(Constant.ADMIN_EMAIL_FORMAT)) { userObject.setAdmin(true);
GlobalFunction.startActivity(SignInActivity.this, MainActivity.class); finishAffinity(); // đóng các tác vụ khác đang chạy }
Toast.makeText(SignInActivity.this, getString(R.string.msg_sign_in_error),
Quên mật khẩu
Khi người dùng nhập sai mật khẩu và không thể nhớ mật khẩu đã đặt lúc này người dùng có thể nhấn quên mật khẩu Người dùng nhập địa chỉ gmail để nhận lại mật khẩu từ hệ thống gửi
package com.medium.music.activity; import android.os.Bundle; import android.widget.Toast;
30 import com.medium.music.R; import com.medium.music.databinding.ActivityForgotPasswordBinding; import com.medium.music.utils.StringUtil; import com.google.firebase.auth.FirebaseAuth; public class ForgotPasswordActivity extends BaseActivity { private ActivityForgotPasswordBinding mActivityForgotPasswordBinding;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivityForgotPasswordBinding ActivityForgotPasswordBinding.inflate(getLayoutInflater()); setContentView(mActivityForgotPasswordBinding.getRoot()); mActivityForgotPasswordBinding.imgBack.setOnClickListener(v -> onBackPressed()); mActivityForgotPasswordBinding.btnResetPassword.setOnClickListener(v -> onClickValidateResetPassword());
String strEmail mActivityForgotPasswordBinding.edtEmail.getText().toString().trim(); if (StringUtil.isEmpty(strEmail)) {
Toast.makeText(ForgotPasswordActivity.this, getString(R.string.msg_email_require), Toast.LENGTH_SHORT).show();
} else if (!StringUtil.isValidEmail(strEmail)) {
Toast.makeText(ForgotPasswordActivity.this, getString(R.string.msg_email_invalid), Toast.LENGTH_SHORT).show();
} private void resetPassword(String email) { showProgressDialog(true);
FirebaseAuth auth = FirebaseAuth.getInstance(); auth.sendPasswordResetEmail(email)
addOnCompleteListener(task -> { showProgressDialog(false); if (task.isSuccessful()) {
Toast.makeText(ForgotPasswordActivity.this, getString(R.string.msg_reset_password_successfully),
Toast.LENGTH_SHORT).show(); mActivityForgotPasswordBinding.edtEmail.setText("");
Lấy dữ liệu từ firebase và hiển thị hình ảnh lên màng hình chính của người dùng
Khi người dùng đăng nhập thành công vào giao diện chính (HomeScreen) dữ liệu từ database trên Firebase sẽ được tải xuống Quá trình này nhanh hay chậm phụ thuộc vào internet của thiết bị Để lấy dữ liệu từ Firebase tại code này cần phải kết nối đến file MyApplication đây là file thực hiện việc kết nối đến cơ sở dữ liệu trên Firebase (Mục kết nối file Firebase bên trên có nói rõ) Giao diện chính sẽ gọi đến file MyApplication rồi từ file MyApplication sẽ kết nối với cơ sở dữ liệu và lấy dữ liệu rồi trả về cho nơi yêu cầu lấy là tại giao diện HomeScreen này.
Code package com.medium.music.fragment; import android.os.Bundle; import android.os.Handler; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.viewpager2.widget.ViewPager2; import com.medium.music.MyApplication; import com.medium.music.R; import com.medium.music.activity.MainActivity; import com.medium.music.activity.PlayMusicActivity; import com.medium.music.adapter.BannerSongAdapter; import com.medium.music.adapter.SongAdapter; import com.medium.music.adapter.SongGridAdapter; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.FragmentHomeBinding; import com.medium.music.listener.IOnClickSongItemListener; import com.medium.music.model.Song; import com.medium.music.service.MusicService; import com.medium.music.utils.StringUtil; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class HomeFragment extends Fragment { private FragmentHomeBinding mFragmentHomeBinding; private List mListSong; private List mListSongBanner; private final Handler mHandlerBanner = new Handler();
32 private final Runnable mRunnableBanner = new Runnable() {
@Override public void run() { if (mListSongBanner == null || mListSongBanner.isEmpty()) { return;
} if (mFragmentHomeBinding.viewpager2.getCurrentItem() = mListSongBanner.size() - 1) { mFragmentHomeBinding.viewpager2.setCurrentItem(0); return;
} mFragmentHomeBindin g.viewpager2.setCurrentItem(mFragmentHomeBinding.viewpager2.getCurrentItem() + 1); }
@Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mFragmentHomeBinding = FragmentHomeBinding.inflate(inflater, container, false); getListSongFromFirebase(""); initListener(); return mFragmentHomeBinding.getRoot();
} private void initListener() { mFragmentHomeBinding.edtSearchName.addTextChangedListener(new
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
@Override public void afterTextChanged(Editable s) {
String strKey = s.toString().trim(); if (strKey.equals("") || strKey.length() == 0) { if (mListSong != null) mListSong.clear(); getListSongFromFirebase("");
}); mFragmentHomeBinding.imgSearch.setOnClickListener(view -> searchSong()); mFragmentHomeBinding.edtSearchName.setOnEditorActionListener((v, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_SEARCH) { searchSong();
}); mFragmentHomeBinding.layoutViewAllPopular.setOnClickListener(v -> { MainActivity mainActivity = (MainActivity) getActivity(); if (mainActivity != null) { mainActivity.openPopularSongsScreen();
}); mFragmentHomeBinding.layoutViewAllNewSongs.setOnClickListener(v -> { MainActivity mainActivity = (MainActivity) getActivity(); if (mainActivity != null) { mainActivity.openNewSongsScreen();
} private void getListSongFromFirebase(String key) { if (getActivity() == null) { return;
.get(getActivity()).getSongsDatabaseReference().addValueEventListener(new ValueEventListener() {
@Override public void onDataChange(@NonNull DataSnapshot snapshot) { mFragmentHomeBinding.layoutContent.setVisibility(View.VISIBLE); mListSong = new ArrayList(); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Song song = dataSnapshot.getValue(Song.class); if (song == null) { return;
} if (StringUtil.isEmpty(key)) { mListSong.add(0, song);
(GlobalFunction.getTextSearch(song.getTitle()).toLowerCase().trim()
.getTextSearch(key).toLowerCase().trim())) { mListSong.add(0, song);
@Override public void onCancelled(@NonNull DatabaseError error) {
GlobalFunction.showToastMessage(getActivity(), getString(R.string.msg_get_date_error));
@Override public void onClickItemSong(Song song) { goToSongDetail(song);
@Override public void onClickFavoriteSong(Song song, boolean favorite) {} }); mFragmentHomeBinding.viewpager2.setAdapter(bannerSongAdapter); mFragmentHomeBinding.indicator3.setViewPager(mFragmentHomeBinding.viewpager2); mFragmentHomeBinding.viewpager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override public void onPageSelected(int position) { super.onPageSelected(position); mHandlerBanner.removeCallbacks(mRunnableBanner); mHandlerBanner.postDelayed(mRunnableBanner, 3000);
} private List getListBannerSongs() { if (mListSongBanner != null) { mListSongBanner.clear();
} if (mListSong == null || mListSong.isEmpty()) { return mListSongBanner;
} for (Song song : mListSong) { if (song.isFeatured() && mListSongBanner.size() <
Constant.MAX_COUNT_BANNER) { mListSongBanner.add(song);
GridLayoutManager(getActivity(), 2); mFragmentHomeBinding.rcvPopularSongs.setLayoutManager(gridLayoutManager);
@Override public void onClickItemSong(Song song) { goToSongDetail(song);
@Override public void onClickFavoriteSong(Song song, boolean favorite) { GlobalFunction.onClickFavoriteSong(getActivity(), song, favorite);
35 mFragmentHomeBinding.rcvPopularSongs.setAdapter(songGridAdapter); } private List getListPopularSongs() {
List list = new ArrayList(); if (mListSong == null || mListSong.isEmpty()) { return list;
List allSongs = new ArrayList(mListSong);
Collections.sort(allSongs, (song1, song2) -> song2.getCount() - song1.getCount()); for (Song song : allSongs) { if (list.size() < Constant.MAX_COUNT_POPULAR) { list.add(song);
} private void displayListNewSongs() { if (getActivity() == null) { return;
LinearLayoutManager(getActivity()); mFragmentHomeBinding.rcvNewSongs.setLayoutManager(linearLayoutManager);
SongAdapter songAdapter = new SongAdapter(getListNewSongs(), new IOnClickSongItemListener() {
@Override public void onClickItemSong(Song song) { goToSongDetail(song);
@Override public void onClickFavoriteSong(Song song, boolean favorite) { GlobalFunction.onClickFavoriteSong(getActivity(), song, favorite);
List list = new ArrayList(); if (mListSong == null || mListSong.isEmpty()) { return list;
} for (Song song : mListSong) { if (song.isLatest() && list.size() < Constant.MAX_COUNT_LATEST) { list.add(song);
String strKey mFragmentHomeBinding.edtSearchName.getText().toString().trim(); if (mListSong != null) mListSong.clear(); getListSongFromFirebase(strKey);
} private void goToSongDetail(@NonNull Song song) {
GlobalFunction.startMusicService(getActivity(), Constant.PLAY, 0); GlobalFunction.startActivity(getActivity(), PlayMusicActivity.class); }
Lấy dữ từ Firebase và phát nhạc
Cũng như mục trên củng vừa trình bày Mục lấy dữ liệu từ Firebase cũng giống bên trên Để phát được nhạc thì phải gửi yêu cầu tải nhạc đến cơ sở dữ liệu trên Firebase và cũng phải thông qua file MyApplication để kết nối và lấy dữ liệu về Quá trình tải nhạc về và phát nhạc cũng tốt vài giây nếu internet của thiết bị trong trạng thái tốt, cũng có thể vài phúc nếu internet yếu.
package com.medium.music.service; import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.widget.RemoteViews; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.medium.music.MyApplication; import com.medium.music.R; import com.medium.music.activity.MainActivity; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.model.Song; import com.medium.music.utils.StringUtil; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; import java.util.List; import java.util.Random; public class MusicService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener { public static boolean isPlaying; public static List mListSongPlaying; public static int mSongPosition; public static MediaPlayer mPlayer; public static int mLengthSong; public static int mAction = -1; public static boolean isShuffle; public static boolean isRepeat;
@Override public IBinder onBind(Intent intent) { return null;
@Override public void onCreate() { super.onCreate(); if (mPlayer == null) { mPlayer = new MediaPlayer();
@Override public int onStartCommand(Intent intent, int flags, int startId) { Bundle bundle = intent.getExtras(); if (bundle != null) { if (bundle.containsKey(Constant.MUSIC_ACTION)) { mAction = bundle.getInt(Constant.MUSIC_ACTION);
} if (bundle.containsKey(Constant.SONG_POSITION)) { mSongPosition = bundle.getInt(Constant.SONG_POSITION); } handleActionMusic(mAction);
} private void handleActionMusic(int action) { switch (action) { case Constant.PLAY: playSong(); break; case Constant.PREVIOUS: prevSong(); break; case Constant.NEXT: nextSong(); break; case Constant.PAUSE: pauseSong(); break; case Constant.RESUME: resumeSong(); break;
38 case Constant.CANNEL_NOTIFICATION: cancelNotification(); break; default: break;
String songUrl = mListSongPlaying.get(mSongPosition).getUrl(); if (!StringUtil.isEmpty(songUrl)) { playMediaPlayer(songUrl);
} private void pauseSong() { if (mPlayer != null && mPlayer.isPlaying()) { mPlayer.pause(); isPlaying = false; sendMusicNotification(); sendBroadcastChangeListener();
} private void cancelNotification() { if (mPlayer != null && mPlayer.isPlaying()) { mPlayer.pause(); isPlaying = false;
NotificationManager notifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notifManager.cancelAll(); sendBroadcastChangeListener(); stopSelf();
} private void resumeSong() { if (mPlayer != null) { mPlayer.start(); isPlaying = true; sendMusicNotification(); sendBroadcastChangeListener();
} public void prevSong() { int newPosition; if (mListSongPlaying.size() > 1) { if (isShuffle) { newPosition = new
} else { if (isRepeat) newPosition = mSongPosition; else if (mSongPosition > 0) { newPosition = mSongPosition - 1;
} mSongPosition = newPosition; sendMusicNotification(); sendBroadcastChangeListener(); playSong();
} private void nextSong() { int newPosition; if (mListSongPlaying.size() > 1) { if (isShuffle) { newPosition = new
} else { if (isRepeat) newPosition = mSongPosition; else if (mSongPosition < mListSongPlaying.size() - 1) { newPosition = mSongPosition + 1;
} mSongPosition = newPosition; sendMusicNotification(); sendBroadcastChangeListener(); playSong();
} public void playMediaPlayer(String songUrl) { try { if (mPlayer.isPlaying()) { mPlayer.stop();
} mPlayer.reset(); mPlayer.setDataSource(songUrl); mPlayer.prepareAsync(); initControl();
} public void initControl() { mPlayer.setOnPreparedListener(this); mPlayer.setOnCompletionListener(this);
Song song = mListSongPlaying.get(mSongPosition); int pendingFlag; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { pendingFlag = PendingIntent.FLAG_UPDATE_CURRENT |
} else { pendingFlag = PendingIntent.FLAG_UPDATE_CURRENT;
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, pendingFlag);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_push_notification_music); remoteViews.setTextViewText(R.id.tv_song_name, song.getTitle());
// Set listener remoteViews.setOnClickPendingIntent(R.id.img_previous,
GlobalFunction.openMusicReceiver(this, Constant.PREVIOUS)); remoteViews.setOnClickPendingIntent(R.id.img_next,
GlobalFunction.openMusicReceiver(this, Constant.NEXT)); if (isPlaying) { remoteViews.setImageViewResource(R.id.img_play,
R.drawable.ic_pause_gray); remoteViews.setOnClickPendingIntent(R.id.img_play,
GlobalFunction.openMusicReceiver(this, Constant.PAUSE));
} else { remoteViews.setImageViewResource(R.id.img_play,
R.drawable.ic_play_gray); remoteViews.setOnClickPendingIntent(R.id.img_play,
GlobalFunction.openMusicReceiver(this, Constant.RESUME));
} remoteViews.setOnClickPendingIntent(R.id.img_close,
GlobalFunction.openMusicReceiver(this, Constant.CANNEL_NOTIFICATION));
NotificationCompat.Builder(this, MyApplication.CHANNEL_ID)
setSmallIcon(R.drawable.ic_small_push_notification) setContentIntent(pendingIntent)
setSound(null); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { builder.setCustomBigContentView(remoteViews);
Notification notification = builder.build(); startForeground(1, notification);
} public static void clearListSongPlaying() { if (mListSongPlaying != null) { mListSongPlaying.clear();
@Override public void onCompletion(MediaPlayer mp) { mAction = Constant.NEXT; nextSong();
@Override public void onPrepared(MediaPlayer mp) { mLengthSong = mPlayer.getDuration(); mp.start(); isPlaying = true; mAction = Constant.PLAY; sendMusicNotification(); sendBroadcastChangeListener();
Intent intent = new Intent(Constant.CHANGE_LISTENER); intent.putExtra(Constant.MUSIC_ACTION, mAction);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } private void changeCountViewSong() { long songId = mListSongPlaying.get(mSongPosition).getId();
MyApplication.get(this).getCountViewDatabaseReference(songId) addValueEventListener(new ValueEventListener() {
@Override public void onDataChange(@NonNull DataSnapshot snapshot) {
Integer currentCount snapshot.getValue(Integer.class); if (currentCount != null) { int newCount = currentCount + 1;
.get(MusicService.this).getCountViewDatabaseReference(songId).removeEven tListener(this);
.get(MusicService.this).getCountViewDatabaseReference(songId).setValue(n ewCount);
@Override public void onCancelled(@NonNull DatabaseError error) {}
@Override public void onDestroy() { super.onDestroy(); if (mPlayer != null) { mPlayer.release(); mPlayer = null;
Hiển thị danh sách bài hát nổi bật
Bài hát nổi bật là do được người quản lý đăng và gán là mục bài hát nổi bật Bài hát được gán khi người quản lý thêm bài mới Để hiển thị ra các bài hát có danh hiệu là nổi bật thì cần kết nối với cơ sở dữ liệu từ Firebase thông qua file MyApplication và lấy về các bài hát có danh hiệu là nổi bật rồi hiển thị ra màng hình
package com.medium.music.fragment;
42 import android.annotation.SuppressLint; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import com.medium.music.MyApplication; import com.medium.music.R; import com.medium.music.activity.MainActivity; import com.medium.music.activity.PlayMusicActivity; import com.medium.music.adapter.SongAdapter; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.FragmentFeaturedSongsBinding; import com.medium.music.listener.IOnClickSongItemListener; import com.medium.music.model.Song; import com.medium.music.service.MusicService; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; import java.util.List; public class FeaturedSongsFragment extends Fragment { private FragmentFeaturedSongsBinding mFragmentFeaturedSongsBinding; private List mListSong; private SongAdapter mSongAdapter;
@Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mFragmentFeaturedSongsBinding FragmentFeaturedSongsBinding.inflate(inflater, container, false); initUi(); initListener(); getListFeaturedSongs(); return mFragmentFeaturedSongsBinding.getRoot();
LinearLayoutManager(getActivity()); mFragmentFeaturedSongsBindin g.rcvData.setLayoutManager(linearLayoutManager); mListSong = new ArrayList(); mSongAdapter = new SongAdapter(mListSong, new
@Override public void onClickItemSong(Song song) { goToSongDetail(song);
@Override public void onClickFavoriteSong(Song song, boolean favorite) { GlobalFunction.onClickFavoriteSong(getActivity(), song, favorite);
@SuppressLint("NotifyDataSetChanged") private void getListFeaturedSongs() { if (getActivity() == null) return;
.get(getActivity()).getSongsDatabaseReference().addValueEventListener(ne w ValueEventListener() {
@Override public void onDataChange(@NonNull DataSnapshot snapshot) { resetListData(); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Song song = dataSnapshot.getValue(Song.class); if (song == null) { return;
} if (song.isFeatured()) { mListSong.add(0, song);
} if (mSongAdapter != null) mSongAdapter.notifyDataSetChanged();
@Override public void onCancelled(@NonNull DatabaseError error) { GlobalFunction.showToastMessage(getActivity(), getString(R.string.msg_get_date_error));
} private void resetListData() { if (mListSong == null) { mListSong = new ArrayList();
} private void goToSongDetail(@NonNull Song song) {
GlobalFunction.startMusicService(getActivity(), Constant.PLAY, 0); GlobalFunction.startActivity(getActivity(),
44 if (activity == null || activity.getActivityMainBinding() = null) { return;
.header.layoutPlayAll.setOnClickListener(v -> { if (mListSong == null || mListSong.isEmpty()) return;
Hiển thị danh sách bài hát mới nhất
Bài hát mới nhất cũng tương tự như bài hát nổi bật Cả 2 chức năng này đều do người quản lý gán cho lúc đăng nhạc Nếu muốn lấy danh sách nhạc về cũng làm các việc tương tự như kết nối đê Firebase thông qua file MyApplication để lấy và nhận dữ liệu đã lấy về mới hiển thị lên giao diện.
package com.medium.music.fragment; import android.annotation.SuppressLint; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import com.medium.music.MyApplication; import com.medium.music.R; import com.medium.music.activity.MainActivity; import com.medium.music.activity.PlayMusicActivity; import com.medium.music.adapter.SongAdapter; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.FragmentNewSongsBinding; import com.medium.music.listener.IOnClickSongItemListener; import com.medium.music.model.Song; import com.medium.music.service.MusicService; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; import java.util.List;
45 public class NewSongsFragment extends Fragment { private FragmentNewSongsBinding mFragmentNewSongsBinding; private List mListSong; private SongAdapter mSongAdapter;
@Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mFragmentNewSongsBinding FragmentNewSongsBinding.inflate(inflater, container, false); initUi(); initListener(); getListNewSongs(); return mFragmentNewSongsBinding.getRoot();
LinearLayoutManager(getActivity()); mFragmentNewSongsBinding.rcvData.setLayoutManager(linearLayoutManager); mListSong = new ArrayList(); mSongAdapter = new SongAdapter(mListSong, new
@Override public void onClickItemSong(Song song) { goToSongDetail(song);
@Override public void onClickFavoriteSong(Song song, boolean favorite) { GlobalFunction.onClickFavoriteSong(getActivity(), song, favorite);
@SuppressLint("NotifyDataSetChanged") private void getListNewSongs() { if (getActivity() == null) return;
.get(getActivity()).getSongsDatabaseReference().addValueEventListener(ne w ValueEventListener() {
@Override public void onDataChange(@NonNull DataSnapshot snapshot) { resetListData(); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Song song = dataSnapshot.getValue(Song.class); if (song == null) { return;
} if (song.isLatest()) { mListSong.add(0, song);
} if (mSongAdapter != null) mSongAdapter.notifyDataSetChanged();
@Override public void onCancelled(@NonNull DatabaseError error) {
GlobalFunction.showToastMessage(getActivity(), getString(R.string.msg_get_date_error));
} private void resetListData() { if (mListSong == null) { mListSong = new ArrayList();
} private void goToSongDetail(@NonNull Song song) {
GlobalFunction.startMusicService(getActivity(), Constant.PLAY, 0); GlobalFunction.startActivity(getActivity(),
MainActivity activity = (MainActivity) getActivity(); if (activity == null || activity.getActivityMainBinding() = null) { return;
.header.layoutPlayAll.setOnClickListener(v -> { if (mListSong == null || mListSong.isEmpty()) return;
Hiển thị danh sách bài hát đã yêu thích
Bài hát đã yêu thích là do người dùng nhấn chọn Dữ liệu sẽ được lưu vào bộ nhớ cố định SharedPreferences Mặc dù không lưu vào cơ sở dữ liệu khi người dùng nhấn thích hoặc hơn là người dùng xóa app thì và khi cài lại thì các lựa chọn
47 đó vẫn còn Chỉ cần lấy dữ liệu bài hát trên Firebase về kết hợp với dữ liệu trên SharedPreferences thì có thể khôi phục lại.
package com.medium.music.fragment; import android.annotation.SuppressLint; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.ValueEventListener; import com.medium.music.MyApplication; import com.medium.music.R; import com.medium.music.activity.MainActivity; import com.medium.music.activity.PlayMusicActivity; import com.medium.music.adapter.SongAdapter; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.FragmentFavoriteBinding; import com.medium.music.listener.IOnClickSongItemListener; import com.medium.music.model.Song; import com.medium.music.service.MusicService; import java.util.ArrayList; import java.util.List; public class FavoriteFragment extends Fragment { private FragmentFavoriteBinding mFragmentFavoriteBinding; private List mListSong; private SongAdapter mSongAdapter;
@Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { mFragmentFavoriteBinding FragmentFavoriteBinding.inflate(inflater, container, false); initUi(); initListener(); getListFavoriteSongs(); return mFragmentFavoriteBinding.getRoot();
LinearLayoutManager(getActivity()); mFragmentFavoriteBinding.rcvData.setLayoutManager(linearLayoutManager);
48 mListSong = new ArrayList(); mSongAdapter = new SongAdapter(mListSong, new
@Override public void onClickItemSong(Song song) { goToSongDetail(song);
@Override public void onClickFavoriteSong(Song song, boolean favorite) { GlobalFunction.onClickFavoriteSong(getActivity(), song, favorite);
@SuppressLint("NotifyDataSetChanged") private void getListFavoriteSongs() { if (getActivity() == null) return;
MyApplication.get(getActivity()).getSongsDatabaseReference() addValueEventListener(new ValueEventListener() {
@Override public void onDataChange(@NonNull DataSnapshot snapshot) { resetListData(); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Song song = dataSnapshot.getValue(Song.class); if (song == null) return; if (GlobalFunction.isFavoriteSong(song)) { mListSong.add(0, song);
} if (mSongAdapter != null) mSongAdapter.notifyDataSetChanged();
@Override public void onCancelled(@NonNull DatabaseError error) { GlobalFunction.showToastMessage(getActivity(), getString(R.string.msg_get_date_error));
} private void resetListData() { if (mListSong == null) { mListSong = new ArrayList();
} private void goToSongDetail(@NonNull Song song) {
GlobalFunction.startMusicService(getActivity(), Constant.PLAY, 0); GlobalFunction.startActivity(getActivity(),
MainActivity activity = (MainActivity) getActivity(); if (activity == null || activity.getActivityMainBinding() = null) { return;
.header.layoutPlayAll.setOnClickListener(v -> { if (mListSong == null || mListSong.isEmpty()) return; MusicService.clearListSongPlaying();
Hiển thị tất cả các bài hát trên hệ thống
Lấy tất cả các bài hát có trên cơ sở dữ liệu của Firebase bằng cách gọi file MyApplication thực hiện kết nối và lấy dữ liệu về rồi hiển thị danh sách bài hát
package com.medium.music.fragment; import android.annotation.SuppressLint; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import com.medium.music.MyApplication; import com.medium.music.R; import com.medium.music.activity.MainActivity; import com.medium.music.activity.PlayMusicActivity; import com.medium.music.adapter.SongAdapter; import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.FragmentAllSongsBinding; import com.medium.music.listener.IOnClickSongItemListener; import com.medium.music.model.Song; import com.medium.music.service.MusicService; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; import java.util.List;
50 public class AllSongsFragment extends Fragment { private FragmentAllSongsBinding mFragmentAllSongsBinding; private List mListSong; private SongAdapter mSongAdapter;
@Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mFragmentAllSongsBinding FragmentAllSongsBinding.inflate(inflater, container, false); initUi(); initListener(); getListAllSongs(); return mFragmentAllSongsBinding.getRoot();
LinearLayoutManager(getActivity()); mFragmentAllSongsBinding.rcvData.setLayoutManager(linearLayoutManager); mListSong = new ArrayList(); mSongAdapter = new SongAdapter(mListSong, new
@Override public void onClickItemSong(Song song) { goToSongDetail(song);
@Override public void onClickFavoriteSong(Song song, boolean favorite) { GlobalFunction.onClickFavoriteSong(getActivity(), song, favorite);
@SuppressLint("NotifyDataSetChanged") private void getListAllSongs() { if (getActivity() == null) return;
.get(getActivity()).getSongsDatabaseReference().addValueEventListener(ne w ValueEventListener() {
@Override public void onDataChange(@NonNull DataSnapshot snapshot) { resetListData(); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Song song = dataSnapshot.getValue(Song.class); if (song == null) { return;
@Override public void onCancelled(@NonNull DatabaseError error) { GlobalFunction.showToastMessage(getActivity(), getString(R.string.msg_get_date_error));
} private void resetListData() { if (mListSong == null) { mListSong = new ArrayList();
} private void goToSongDetail(@NonNull Song song) {
GlobalFunction.startMusicService(getActivity(), Constant.PLAY, 0); GlobalFunction.startActivity(getActivity(),
MainActivity activity = (MainActivity) getActivity(); if (activity == null || activity.getActivityMainBinding() = null) { return;
.header.layoutPlayAll.setOnClickListener(v -> { if (mListSong == null || mListSong.isEmpty()) return;
Kết nối đến mạng xã hội
Nếu người dùng muốn biết các cập nhật tiếp theo của ứng dụng có thể theo dõi các trang mạng xã hội của ứng dụng để biết thêm chi tiết.
package com.medium.music.fragment; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; import com.medium.music.R; import com.medium.music.adapter.ContactAdapter; import com.medium.music.constant.AboutUsConfig; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.FragmentContactBinding; import com.medium.music.model.Contact; import java.util.ArrayList; import java.util.List; public class ContactFragment extends Fragment { private FragmentContactBinding mFragmentContactBinding; private ContactAdapter mContactAdapter;
@Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mFragmentContactBinding FragmentContactBinding.inflate(inflater, container, false); initUi(); initListener(); return mFragmentContactBinding.getRoot();
} private void initUi() { mFragmentContactBindin g.tvAboutUsTitle.setText(AboutUsConfig.ABOUT_US_TITLE); mFragmentContactBindin g.tvAboutUsContent.setText(AboutUsConfig.ABOUT_US_CONTENT); mFragmentContactBindin g.tvAboutUsWebsite.setText(AboutUsConfig.ABOUT_US_WEBSITE_TITLE); mContactAdapter = new ContactAdapter(getActivity(), getListContact(),
() -> GlobalFunction.callPhoneNumber(getActivity())); GridLayoutManager layoutManager = new
GridLayoutManager(getActivity(), 3); mFragmentContactBinding.rcvData.setNestedScrollingEnabled(false); mFragmentContactBinding.rcvData.setFocusable(false); mFragmentContactBinding.rcvData.setLayoutManager(layoutManager); mFragmentContactBinding.rcvData.setAdapter(mContactAdapter);
} private void initListener() { mFragmentContactBinding.layoutWebsite.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW,
List contactArrayList = new ArrayList(); contactArrayList.add(new Contact(Contact.FACEBOOK,
R.drawable.ic_facebook)); contactArrayList.add(new Contact(Contact.HOTLINE,
R.drawable.ic_hotline)); contactArrayList.add(new Contact(Contact.GMAIL,
R.drawable.ic_gmail)); contactArrayList.add(new Contact(Contact.SKYPE,
R.drawable.ic_skype)); contactArrayList.add(new Contact(Contact.YOUTUBE,
R.drawable.ic_youtube)); contactArrayList.add(new Contact(Contact.ZALO,
R.drawable.ic_zalo)); return contactArrayList;
@Override public void onDestroy() { super.onDestroy(); mContactAdapter.release();
Đổi mật khẩu
Người dùng quên mật khẩu hoặc muốn đổi mật khẩu cho dễ nhớ hơn thì chọn chức năng này.
package com.medium.music.activity; import android.os.Bundle; import android.widget.Toast; import com.medium.music.R; import com.medium.music.databinding.ActivityForgotPasswordBinding; import com.medium.music.utils.StringUtil; import com.google.firebase.auth.FirebaseAuth; public class ForgotPasswordActivity extends BaseActivity { private ActivityForgotPasswordBinding mActivityForgotPasswordBinding;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivityForgotPasswordBinding 54
ActivityForgotPasswordBinding.inflate(getLayoutInflater()); setContentView(mActivityForgotPasswordBinding.getRoot()); mActivityForgotPasswordBinding.imgBack.setOnClickListener(v -> onBackPressed()); mActivityForgotPasswordBinding.btnResetPassword.setOnClickListener(v -> onClickValidateResetPassword());
String strEmail mActivityForgotPasswordBinding.edtEmail.getText().toString().trim(); if (StringUtil.isEmpty(strEmail)) {
Toast.makeText(ForgotPasswordActivity.this, getString(R.string.msg_email_require), Toast.LENGTH_SHORT).show();
} else if (!StringUtil.isValidEmail(strEmail)) {
Toast.makeText(ForgotPasswordActivity.this, getString(R.string.msg_email_invalid), Toast.LENGTH_SHORT).show();
} private void resetPassword(String email) { showProgressDialog(true);
FirebaseAuth auth = FirebaseAuth.getInstance(); auth.sendPasswordResetEmail(email)
addOnCompleteListener(task -> { showProgressDialog(false); if (task.isSuccessful()) {
Toast.makeText(ForgotPasswordActivity.this, getString(R.string.msg_reset_password_successfully),
Toast.LENGTH_SHORT).show(); mActivityForgotPasswordBinding.edtEmail.setText("");
Thêm bài hát mới hoặc sửa bài hát
Chức năng này là cho người quản trị, cho phép thêm hoặc sửa các tên, hình ảnh, link nhạc nếu có lỗi xảy ra trong quá trình hoạt động của ứng dụng.
package com.medium.music.activity; import android.os.Bundle; import android.view.View; import android.widget.Toast; import com.medium.music.MyApplication; import com.medium.music.R;
55 import com.medium.music.constant.Constant; import com.medium.music.constant.GlobalFunction; import com.medium.music.databinding.ActivityAddSongBinding; import com.medium.music.model.Song; import com.medium.music.utils.StringUtil; import java.util.HashMap; import java.util.Map; public class AddSongActivity extends BaseActivity { private ActivityAddSongBinding mActivityAddSongBinding; private boolean isUpdate; private Song mSong;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivityAddSongBinding ActivityAddSongBinding.inflate(getLayoutInflater()); setContentView(mActivityAddSongBinding.getRoot()); getDataIntent(); initToolbar(); initView(); mActivityAddSongBinding.btnAddOrEdit.setOnClickListener(v -> addOrEditFood());
Bundle bundleReceived = getIntent().getExtras(); if (bundleReceived != null) { isUpdate = true; mSong = (Song) bundleReceived.get(Constant.KEY_INTENT_SONG_OBJECT);
} private void initToolbar() { mActivityAddSongBindin g.toolbar.imgLeft.setImageResource(R.drawable.ic_back_white); mActivityAddSongBindin g.toolbar.tvTitle.setText(R.string.label_add_song); mActivityAddSongBinding.toolbar.layoutPlayAll.setVisibility(View.GONE); mActivityAddSongBinding.toolbar.imgLeft.setOnClickListener(v -> onBackPressed());
} private void initView() { if (isUpdate) { mActivityAddSongBindin g.toolbar.tvTitle.setText(getString(R.string.label_update_song)); mActivityAddSongBindin g.btnAddOrEdit.setText(getString(R.string.action_edit)); mActivityAddSongBinding.edtName.setText(mSong.getTitle());
56 mActivityAddSongBinding.edtArtist.setText(mSong.getArtist()); mActivityAddSongBinding.edtImage.setText(mSong.getImage()); mActivityAddSongBinding.edtLink.setText(mSong.getUrl()); mActivityAddSongBinding.chbFeatured.setChecked(mSong.isFeatured()); mActivityAddSongBinding.chbLatest.setChecked(mSong.isLatest());
} else { mActivityAddSongBindin g.toolbar.tvTitle.setText(getString(R.string.label_add_song)); mActivityAddSongBindin g.btnAddOrEdit.setText(getString(R.string.action_add));
String strName mActivityAddSongBinding.edtName.getText().toString().trim();
String strArtist mActivityAddSongBinding.edtArtist.getText().toString().trim();
String strImage mActivityAddSongBinding.edtImage.getText().toString().trim();
String strLink mActivityAddSongBinding.edtLink.getText().toString().trim(); boolean isFeatured mActivityAddSongBinding.chbFeatured.isChecked(); boolean isLatest mActivityAddSongBinding.chbLatest.isChecked(); if (StringUtil.isEmpty(strName)) {
Toast.makeText(this, getString(R.string.msg_name_song_require), Toast.LENGTH_SHORT).show(); return;
Toast.makeText(this, getString(R.string.msg_artist_song_require), Toast.LENGTH_SHORT).show(); return;
Toast.makeText(this, getString(R.string.msg_image_song_require), Toast.LENGTH_SHORT).show(); return;
Toast.makeText(this, getString(R.string.msg_link_song_require), Toast.LENGTH_SHORT).show(); return;
// Update song if (isUpdate) { showProgressDialog(true);
Map map = new HashMap(); map.put("title", strName); map.put("artist", strArtist); map.put("image", strImage); map.put("url", strLink);
57 map.put("featured", isFeatured); map.put("latest", isLatest);
.valueOf(mSong.getId())).updateChildren(map, (error, ref) -> { showProgressDialog(false);
Toast.makeText(AddSongActivity.this, getString(R.string.msg_edit_song_success), Toast.LENGTH_SHORT).show();
// Add song showProgressDialog(true); long songId = System.currentTimeMillis();
Song song = new Song(songId, strName, strArtist, strImage, strLink, isFeatured, isLatest);
//Thêm đối tượng Song vào cơ sở dữ liệu Firebase
child(String.valueOf(songId)).setValue(song, (error, ref) -> {
// Xử lý sau khi thêm dữ liệu thành công showProgressDialog(false); mActivityAddSongBinding.edtName.setText(""); mActivityAddSongBinding.edtArtist.setText(""); mActivityAddSongBinding.edtImage.setText(""); mActivityAddSongBinding.edtLink.setText(""); mActivityAddSongBinding.chbFeatured.setChecked(false); mActivityAddSongBinding.chbLatest.setChecked(false);
Toast.makeText(this, getString(R.string.msg_add_song_success), Toast.LENGTH_SHORT).show(); });