diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 46d25f0..18cd5f2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,10 @@ + + + audioItems = new ArrayList<>(); - // 构造函数 public A_InstrumentAdapter(Context context) { this.context = context; } @@ -42,18 +43,25 @@ public class A_InstrumentAdapter extends RecyclerView.Adapter newAudioItems) { diff --git a/app/src/main/java/com/hi/music/player/adapter/A_NatureAdapter.java b/app/src/main/java/com/hi/music/player/adapter/A_NatureAdapter.java index 96385fa..1330492 100644 --- a/app/src/main/java/com/hi/music/player/adapter/A_NatureAdapter.java +++ b/app/src/main/java/com/hi/music/player/adapter/A_NatureAdapter.java @@ -1,6 +1,7 @@ package com.hi.music.player.adapter; import android.content.Context; +import android.content.Intent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -14,6 +15,7 @@ import com.bumptech.glide.Glide; import com.hi.music.player.R; import com.hi.music.player.helper.AudioItemDiffCallback; import com.hi.music.player.javabean.A_data.AudioItem; +import com.hi.music.player.ui.activity.A_PlayActivity; import java.util.ArrayList; import java.util.List; @@ -38,10 +40,19 @@ public class A_NatureAdapter extends RecyclerView.Adapter { // 图标数组定义为类成员,避免重复 @@ -96,7 +98,7 @@ public class A_HomeActivity extends BaseActivity { } private void updateTabIcon(TabLayout.Tab tab, boolean isSelected) { - HomeTabCustomBinding tabBinding = HomeTabCustomBinding.bind(tab.getCustomView()); + HomeTabCustomBinding tabBinding = HomeTabCustomBinding.bind(Objects.requireNonNull(tab.getCustomView())); int position = tab.getPosition(); tabBinding.homeIcon.setImageResource(isSelected ? selectedIcons[position] : defaultIcons[position]); } diff --git a/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java index c7ae3b4..77094ce 100644 --- a/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java +++ b/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java @@ -1,11 +1,23 @@ package com.hi.music.player.ui.activity; +import android.content.Intent; +import android.media.MediaPlayer; +import android.util.Log; import android.view.View; +import android.widget.SeekBar; +import androidx.lifecycle.ViewModelProvider; + +import com.hi.music.player.R; import com.hi.music.player.databinding.ActivityAplayBinding; +import com.hi.music.player.javabean.A_data.AudioItem; +import com.hi.music.player.ui.activity.viewmodel.A_VMPlay; public class A_PlayActivity extends BaseActivity { + private AudioItem audioItem; + private MediaPlayer mediaPlayer; + private A_VMPlay playViewModel; @Override protected ActivityAplayBinding getViewBinding() { @@ -15,13 +27,68 @@ public class A_PlayActivity extends BaseActivity { @Override protected void onCreateInit() { + Intent intent = getIntent(); + audioItem = (AudioItem) intent.getSerializableExtra("Path"); + if (audioItem == null) { + return; + } + + String path = audioItem.getFile(); + Log.d("------", "--------" + path); + + playViewModel = new ViewModelProvider(this).get(A_VMPlay.class); + mediaPlayer = new MediaPlayer(); + + playViewModel.initializePlayer(path); + + // 观察进度更新 + playViewModel.getProgress().observe(this, progress -> { + vb.songSeekbar.setProgress(progress); + }); + + // 观察播放状态 + playViewModel.isPlaying().observe(this, isPlaying -> { + vb.playButton.setBackgroundResource(isPlaying ? R.drawable.play : R.drawable.pause); + }); + + // 观察当前时间 + playViewModel.getCurrentTime().observe(this, currentTime -> { + vb.current.setText(currentTime); + }); + + // 观察总时间 + playViewModel.getTotalTime().observe(this, totalTime -> { + vb.time.setText(totalTime); + }); + } @Override protected void onInitClick() { + vb.playButton.setOnClickListener(v -> playViewModel.togglePlay()); + + vb.songSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + playViewModel.seekTo(progress); // 拖动进度条时控制音频 + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // 暂停更新进度 + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // 恢复更新进度 + } + }); } + @Override public boolean isFullScreen() { return true; @@ -34,6 +101,6 @@ public class A_PlayActivity extends BaseActivity { @Override public void onClick(View v) { - + // 处理其他点击事件 } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/hi/music/player/ui/activity/BaseActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/BaseActivity.java index 649fc7a..0b78b96 100644 --- a/app/src/main/java/com/hi/music/player/ui/activity/BaseActivity.java +++ b/app/src/main/java/com/hi/music/player/ui/activity/BaseActivity.java @@ -14,6 +14,8 @@ import androidx.viewbinding.ViewBinding; import com.hi.music.player.helper.ViewModelScope; +import java.io.IOException; + public abstract class BaseActivity extends AppCompatActivity implements View.OnClickListener{ private final ViewModelScope mViewModelScope = new ViewModelScope(); diff --git a/app/src/main/java/com/hi/music/player/ui/activity/viewmodel/A_VMPlay.java b/app/src/main/java/com/hi/music/player/ui/activity/viewmodel/A_VMPlay.java new file mode 100644 index 0000000..bd64ae4 --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/activity/viewmodel/A_VMPlay.java @@ -0,0 +1,105 @@ +package com.hi.music.player.ui.activity.viewmodel; + +import android.app.Application; +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.util.Log; + +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import java.io.IOException; + +public class A_VMPlay extends AndroidViewModel { + private MediaPlayer mediaPlayer; + private final MutableLiveData currentTime = new MutableLiveData<>(); + private final MutableLiveData totalTime = new MutableLiveData<>(); + private final MutableLiveData isPlaying = new MutableLiveData<>(false); + private final MutableLiveData progress = new MutableLiveData<>(0); // 添加进度 LiveData + + public A_VMPlay(Application application) { + super(application); + mediaPlayer = new MediaPlayer(); + } + + // 获取进度的 LiveData + public LiveData getProgress() { + return progress; + } + + public LiveData getCurrentTime() { + return currentTime; + } + + public LiveData getTotalTime() { + return totalTime; + } + + public LiveData isPlaying() { + return isPlaying; + } + + public void initializePlayer(String path) { + try { + AssetFileDescriptor afd = getApplication().getAssets().openFd(path); + mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + mediaPlayer.prepare(); + mediaPlayer.setOnPreparedListener(mp -> { + totalTime.setValue(formatTime(mediaPlayer.getDuration())); + startUpdatingProgress(); + }); + } catch (IOException e) { + Log.e("MediaPlayerError", "Could not open asset file: " + path, e); + } + } + + public void togglePlay() { + if (mediaPlayer.isPlaying()) { + mediaPlayer.pause(); + isPlaying.setValue(false); + } else { + mediaPlayer.start(); + isPlaying.setValue(true); + } + } + + private void startUpdatingProgress() { + new Thread(() -> { + while (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) { + int currentPosition = mediaPlayer.getCurrentPosition(); + currentTime.postValue(formatTime(currentPosition)); + progress.postValue((int) ((currentPosition / (float) mediaPlayer.getDuration()) * 100)); // 更新进度 + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + }).start(); + } + + public void seekTo(int progress) { + if (mediaPlayer != null) { + int duration = mediaPlayer.getDuration(); + mediaPlayer.seekTo((int) (duration * progress / 100.0)); + } + } + + private String formatTime(int milliseconds) { + int minutes = (milliseconds / 1000) / 60; + int seconds = (milliseconds / 1000) % 60; + return String.format("%02d:%02d", minutes, seconds); + } + + @Override + protected void onCleared() { + super.onCleared(); + if (mediaPlayer != null) { + mediaPlayer.release(); + mediaPlayer = null; + } + } +} diff --git a/app/src/main/java/com/hi/music/player/ui/fragmnt/A_ImportFragment.java b/app/src/main/java/com/hi/music/player/ui/fragmnt/A_ImportFragment.java index 02f6ff3..4e39bb1 100644 --- a/app/src/main/java/com/hi/music/player/ui/fragmnt/A_ImportFragment.java +++ b/app/src/main/java/com/hi/music/player/ui/fragmnt/A_ImportFragment.java @@ -1,13 +1,25 @@ package com.hi.music.player.ui.fragmnt; +import static android.app.Activity.RESULT_OK; + +import android.Manifest; import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import com.hi.music.player.databinding.FragmentAImportBinding; import com.hi.music.player.ui.activity.A_SettingActivity; - public class A_ImportFragment extends BaseFragment { + private static final int REQUEST_CODE_READ_MEDIA_AUDIO = 1001; + private static final int REQUEST_CODE_PICK_AUDIO = 1002; @Override protected FragmentAImportBinding getFragmentVb() { @@ -28,10 +40,72 @@ public class A_ImportFragment extends BaseFragment { } public void initEvent() { + Vb.setting.setOnClickListener(v -> { Intent intent = new Intent(getContext(), A_SettingActivity.class); startActivity(intent); }); + + Vb.add.setOnClickListener(v -> { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_MEDIA_AUDIO) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(requireActivity(), + new String[]{Manifest.permission.READ_MEDIA_AUDIO}, + REQUEST_CODE_READ_MEDIA_AUDIO); + } else { + openAudioPicker(); + } + } else { + if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(requireActivity(), + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + REQUEST_CODE_READ_MEDIA_AUDIO); + } else { + openAudioPicker(); + } + } + + + }); + } -} \ No newline at end of file + // 处理权限请求结果 + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == REQUEST_CODE_READ_MEDIA_AUDIO) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + openAudioPicker(); + } else { + Toast.makeText(requireContext(), "Permission denied", Toast.LENGTH_SHORT).show(); + } + } + } + + // 打开音频选择器 + private void openAudioPicker() { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.setType("audio/*"); // 设置 MIME 类型为音频 + intent.addCategory(Intent.CATEGORY_OPENABLE); // 限制为可打开的文件 + startActivityForResult(intent, REQUEST_CODE_PICK_AUDIO); + } + + + // 处理 Activity 结果 + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_CODE_PICK_AUDIO && resultCode == RESULT_OK) { + if (data != null) { + Uri audioUri = data.getData(); + // 处理音频文件,例如播放或显示在列表中 + Toast.makeText(requireContext(), "Audio Selected: " + audioUri.toString(), Toast.LENGTH_SHORT).show(); + } + } + } + + +} + diff --git a/app/src/main/java/com/hi/music/player/ui/fragmnt/viewmodel/A_VMImport.java b/app/src/main/java/com/hi/music/player/ui/fragmnt/viewmodel/A_VMImport.java new file mode 100644 index 0000000..cd90a5d --- /dev/null +++ b/app/src/main/java/com/hi/music/player/ui/fragmnt/viewmodel/A_VMImport.java @@ -0,0 +1,9 @@ +package com.hi.music.player.ui.fragmnt.viewmodel; + +import androidx.lifecycle.ViewModel; + +public class A_VMImport extends ViewModel { + + + +} diff --git a/app/src/main/res/drawable/round_rectangle.xml b/app/src/main/res/drawable/round_rectangle.xml index 29c732b..eed6052 100644 --- a/app/src/main/res/drawable/round_rectangle.xml +++ b/app/src/main/res/drawable/round_rectangle.xml @@ -1,8 +1,5 @@ - - - - - + + + diff --git a/app/src/main/res/layout/activity_ahome.xml b/app/src/main/res/layout/activity_ahome.xml index 96b46e5..b097461 100644 --- a/app/src/main/res/layout/activity_ahome.xml +++ b/app/src/main/res/layout/activity_ahome.xml @@ -5,6 +5,7 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@drawable/home_background" tools:context=".ui.activity.A_HomeActivity"> diff --git a/app/src/main/res/layout/fragment_a_home.xml b/app/src/main/res/layout/fragment_a_home.xml index bfaefc8..6fc32db 100644 --- a/app/src/main/res/layout/fragment_a_home.xml +++ b/app/src/main/res/layout/fragment_a_home.xml @@ -21,6 +21,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ready_to_sleep" + android:textColor="@color/white" android:textSize="44sp" android:textStyle="italic|bold" /> @@ -29,6 +30,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/we_ve_carefully_prepared_sounds_for_you" + android:textColor="@color/white" android:textSize="14sp" /> + tools:listitem="@layout/item_a_home_1" /> + android:orientation="horizontal" + tools:itemCount="1" + tools:listitem="@layout/item_a_home_2" /> + android:orientation="horizontal" + tools:itemCount="1" + tools:listitem="@layout/item_a_home_3" /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_a_home_1.xml b/app/src/main/res/layout/item_a_home_1.xml index 3093763..83f2070 100644 --- a/app/src/main/res/layout/item_a_home_1.xml +++ b/app/src/main/res/layout/item_a_home_1.xml @@ -1,55 +1,64 @@ + android:layout_height="wrap_content"> - - - - - - - - - - + app:cardCornerRadius="18dp" + app:cardElevation="0dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent"> - + android:id="@+id/background_image" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="centerCrop" + android:src="@mipmap/cover" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_a_home_2.xml b/app/src/main/res/layout/item_a_home_2.xml index ccdea02..85abb15 100644 --- a/app/src/main/res/layout/item_a_home_2.xml +++ b/app/src/main/res/layout/item_a_home_2.xml @@ -4,13 +4,23 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - + app:cardCornerRadius="18dp" + app:cardElevation="0dp" + app:layout_constraintBottom_toTopOf="@id/title"> + + + + + + app:layout_constraintTop_toBottomOf="@id/card_view" /> diff --git a/app/src/main/res/layout/item_a_home_3.xml b/app/src/main/res/layout/item_a_home_3.xml index c74a477..395777d 100644 --- a/app/src/main/res/layout/item_a_home_3.xml +++ b/app/src/main/res/layout/item_a_home_3.xml @@ -5,17 +5,26 @@ android:layout_height="wrap_content" android:padding="8dp"> - + app:layout_constraintTop_toTopOf="parent"> + + + + +