2020 Jul. 11.
2020 Jul. 05.
(Service, IntentService等を使わず)Activity上でMediaPlayerで音声再生すると、
Activityがフォアグラウンドにある時は再生される。
Activityがバックグラウンドに移ると再生が止まる。
再生中にActivityに配置したボタン(再生停止・ポーズ等)を操作できる
参照元 https://www.atmarkit.co.jp/ait/articles/1203/28/news128.html
外部記憶装置読み取りを許可する
AndroidManifest.xmlに次行を記述する。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="MY.mylearngetsoundfilewithcontentresolver"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
// MainActivity.kt package YOUR.PACKAGE.mylearngetsoundfilewithcontentresolver import android.Manifest import android.annotation.TargetApi import android.content.DialogInterface import android.content.pm.PackageManager import android.media.AudioAttributes import android.media.MediaPlayer import android.os.Build import android.os.Bundle import android.os.Handler import android.util.Log import android.view.View import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import kotlinx.android.synthetic.main.activity_main.* import java.io.IOException class MainActivity : AppCompatActivity(), View.OnClickListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnInfoListener, MediaPlayer.OnCompletionListener { private val mHandler: Handler = Handler() private var mIndex = 0 private var mItems: List<MusicItem>? = null private var mMediaPlayer: MediaPlayer? = null private val REQUEST_PERMISSION_EX_STORAGE = 1010 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(fairway.rokkosan.mylearngetsoundfilewithcontentresolver.R.layout.activity_main) // check & get ex-card-permissions getPermissionSdCard() if ( checkSelfPermission( Manifest.permission.READ_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED) { // Unable ex-card", in case of no-permission return } mButtonPlayPause?.setOnClickListener(this@MainActivity) mButtonSkip.setOnClickListener(this@MainActivity) mButtonRewind.setOnClickListener(this@MainActivity) mButtonStop.setOnClickListener(this@MainActivity) setEnabledButton(false) } override fun onResume() { super.onResume() Log.d("MyLog", "onResume") val mItemsTmp = MusicItem.getItems(applicationContext) mItemsTmp?.let{mItems = it} if (mItems?.let{it.size} !== 0) { // mMediaPlayer = MediaPlayer() val mMediaPlayerTmp = MediaPlayer() mMediaPlayerTmp?.let{mMediaPlayer = it} mMediaPlayer?.setOnPreparedListener(this) mMediaPlayer?.setOnInfoListener(this) mMediaPlayer?.setOnCompletionListener(this) prepare() } } override fun onPause() { super.onPause() Log.d("MyLog", "onPause") if (mMediaPlayer != null) { mMediaPlayer?.let{it.reset()} mMediaPlayer?.let{it.release()} mMediaPlayer = null // mChronometer.stop() } } private fun setEnabledButton(enabled: Boolean) { Log.d("MyLog", "setEnabledButton:$enabled") mHandler.post(Runnable { mButtonPlayPause!!.isEnabled = enabled mButtonSkip.isEnabled = enabled mButtonRewind.isEnabled = enabled mButtonStop.isEnabled = enabled }) } override fun onClick(v: View) { val isPlaying = mMediaPlayer!!.isPlaying if (v === mButtonPlayPause) { Log.d("MyLog", "PlayPauseClicked") if (isPlaying) { mMediaPlayer!!.pause() // mChronometer.stop() // mButtonPlayPause.setImageResource(R.drawable.media_play) mButtonPlayPause.setText(fairway.rokkosan.mylearngetsoundfilewithcontentresolver.R.string.play) } else { mMediaPlayer!!.start() // mChronometer.setBase(SystemClock.elapsedRealtime() - mMediaPlayer!!.currentPosition) // mChronometer.start() // mButtonPlayPause.setImageResource(R.drawable.media_pause) // mButtonPlayPause.contentDescription = resources.getText(R.string.pause) mButtonPlayPause.setText(fairway.rokkosan.mylearngetsoundfilewithcontentresolver.R.string.pause) } } else if (v === mButtonSkip) { Log.d("MyLog", "SkipClicked") // mIndex = (mIndex + 1) % mItems?.size mIndex = (mIndex + 1).rem(mItems!!.size) onClick(mButtonStop) if (isPlaying) { onClick(mButtonPlayPause) } } else if (v === mButtonRewind) { Log.d("MyLog", "RewindClicked") mMediaPlayer!!.seekTo(0) // mChronometer.setBase(SystemClock.elapsedRealtime()) } else if (v === mButtonStop) { Log.d("MyLog", "StopClicked") mMediaPlayer!!.stop() mMediaPlayer!!.reset() // mChronometer.stop() // mChronometer.setBase(SystemClock.elapsedRealtime()) prepare() } } private fun prepare() { setEnabledButton(false) // mMediaPlayer!!.setAudioStreamType(AudioManager.STREAM_MUSIC) mMediaPlayer!!.setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build() ) val playingItem: MusicItem = mItems!![mIndex] try { mMediaPlayer!!.setDataSource(applicationContext, playingItem.getURI()) Log.d("MyLog", "URI: " + playingItem.getURI().path) mMediaPlayer!!.prepare() } catch (e: IllegalArgumentException) { Toast.makeText(applicationContext, e.message, Toast.LENGTH_LONG).show() e.printStackTrace() } catch (e: SecurityException) { Toast.makeText(applicationContext, e.message, Toast.LENGTH_LONG).show() e.printStackTrace() } catch (e: IllegalStateException) { Toast.makeText(applicationContext, e.message, Toast.LENGTH_LONG).show() e.printStackTrace() } catch (e: IOException) { Toast.makeText(applicationContext, e.message, Toast.LENGTH_LONG).show() e.printStackTrace() } // mTextViewArtist.setText(playingItem.artist) // mTextViewAlbum.setText(playingItem.album) // mTextViewTitle.setText(playingItem.title) // mButtonPlayPause!!.setImageResource(R.drawable.media_play) // mButtonPlayPause.contentDescription = resources.getText(R.string.play) mButtonPlayPause.setText(fairway.rokkosan.mylearngetsoundfilewithcontentresolver.R.string.play) // mChronometer.setBase(SystemClock.elapsedRealtime()) } override fun onPrepared(mp: MediaPlayer?) { Log.d("MyLog", "onPrepared") setEnabledButton(true) } override fun onCompletion(mp: MediaPlayer?) { Log.d("MyLog", "onCompletion") mHandler.post(Runnable { onClick(mButtonSkip) while (!mButtonPlayPause!!.isEnabled) { try { Thread.sleep(100) } catch (e: InterruptedException) { } } onClick(mButtonPlayPause) }) } override fun onInfo(mp: MediaPlayer?, what: Int, extra: Int): Boolean { Log.d("MyLog", "onInfo:") return true } /* * SD-card permissionの確認・取得 */ @TargetApi(Build.VERSION_CODES.M) private fun getPermissionSdCard() { if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // 許可されていない if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) { /* shouldShowRequestPermissionRationale()は * 許可が必要な理由をユーザーに示すべき場合にtrueとなる * 一度も拒否されていない(まだパーミッションダイアログを出していない)ならfalse、 * 1度拒否されて「今後表示しない」が選択されてないならtrue、 * 1度拒否されて「今後表示しない」が選択された場合はfalse * https://peitechblog.com/?p=949 より */ // 許可されたかどうかはonRequestPermissionsResult()で確認できる AlertDialog.Builder(this) .setTitle("パーミッションの追加説明") // 1) Messageを確認するだけのダイアログを表示する .setMessage("写真を保存するには許可が必要です") .setPositiveButton( android.R.string.ok, DialogInterface.OnClickListener { _, _ -> // 2) OKがタップされると許可諾否ダイアログが表示される ActivityCompat.requestPermissions( this,arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_PERMISSION_EX_STORAGE) }) .create() .show() } else { /* 初めてパーミッション要求ダイアログを出す場合 * あるいは * 1度拒否されて「今後表示しない」が選択された場合 */ /* 前に、許可要求ダイアログで「今後は確認しない」にチェックして許可しなかった場合、 * checkSelfPermission() はダイアログ表示せず、権限を許可しようとしない。 * https://qiita.com/caad1229/items/35bab757217b204711df より */ /* ActivityCompat.requestPermissions(this@MainActivity, String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_EX_STORAGE) */ ActivityCompat.requestPermissions(this@MainActivity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_PERMISSION_EX_STORAGE) // 許可されたかどうかはonRequestPermissionsResult()で確認できる } } } }
// MusicItem.java /* referrer * https://www.atmarkit.co.jp/ait/articles/1203/28/news128.html */ import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.MediaStore; import android.util.Log; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * 外部ストレージ上の音楽をあらわすクラス。 */ public class MusicItem implements Comparable<Object> { private static final String TAG = "Item"; final long id; final String artist; final String title; final String album; final int truck; final long duration; public MusicItem(long id, String artist, String title, String album, int truck, long duration) { this.id = id; this.artist = artist; this.title = title; this.album = album; this.truck = truck; this.duration = duration; } public Uri getURI() { return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); } /** * 外部ストレージ上から音楽を探してリストを返す。 * @param context コンテキスト * @return 見つかった音楽のリスト */ public static List<MusicItem> getItems(Context context) { List<MusicItem> musicItems = new LinkedList<MusicItem>(); // ContentResolver を取得 ContentResolver cr = context.getContentResolver(); // 外部ストレージから音楽を検索 Cursor cur = cr.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Audio.Media.IS_MUSIC + " = 1", null, null); if (cur != null) { if (cur.moveToFirst()) { Log.i(TAG, "Listing..."); // 曲情報のカラムを取得 int artistColumn = cur.getColumnIndex(MediaStore.Audio.Media.ARTIST); int titleColumn = cur.getColumnIndex(MediaStore.Audio.Media.TITLE); int albumColumn = cur.getColumnIndex(MediaStore.Audio.Media.ALBUM); int durationColumn = cur.getColumnIndex(MediaStore.Audio.Media.DURATION); int idColumn = cur.getColumnIndex(MediaStore.Audio.Media._ID); int idTruck = cur.getColumnIndex(MediaStore.Audio.Media.TRACK); Log.i(TAG, "Title column index: " + String.valueOf(titleColumn)); Log.i(TAG, "ID column index: " + String.valueOf(titleColumn)); // リストに追加 do { Log.i(TAG, "ID: " + cur.getString(idColumn) + " Title: " + cur.getString(titleColumn)); musicItems.add(new MusicItem(cur.getLong(idColumn), cur.getString(artistColumn), cur.getString(titleColumn), cur.getString(albumColumn), cur.getInt(idTruck), cur.getLong(durationColumn))); } while (cur.moveToNext()); Log.i(TAG, "Done querying media. MusicRetriever is ready."); } // カーソルを閉じる cur.close(); } // 見つかる順番はソートされていないため、アルバム単位でソートする Collections.sort(musicItems); return musicItems; } @Override public int compareTo(Object another) { if (another == null) { return 1; } MusicItem item = (MusicItem) another; int result = album.compareTo(item.album); if (result != 0) { return result; } return truck - item.truck; } }