Sunteți pe pagina 1din 15

Miniproiect PPA

Music Player

Sfetcu Ctlina-Gabriela 424D


erbnescu Andrei 424D

2016

1. Descrierea temei
Platforma Andoid ofera multe posibilitati de a controla redarea de
fisiere sau streaming-uri audio sau video. Una din aceste metode se face
folosind o clasa numita MediaPlayer. Android ofera clasa MediaPlayer
pentru a accesa functiile built-in ale mediaplayer-ului, cum ar fi redarea
audio, video, etc.
Vom realiza o aplicatie de music player simpla. Aplicatia va prezenta
o lista de melodii stocate pe device-ul utilizatorului pentru ca acesta sa
poata selecta ce melodii doreste sa redea.
De asemenea, aplicatia prezinta butoane de control pentru redare si
va continua sa lucreze in fundal, afisand o notificare.

2. Descrierea punctului de start


In realizarea proiectului am pornit de la urmatoarele tutoriale:
https://www.youtube.com/watch?v=q1sCQMCyZtU
http://code.tutsplus.com/tutorials/create-a-music-player-on-androidproject-setup--mobile-22764
Aplicatia este destul de simpla si are elemente de redare, de trecere
de la o melodie la alta si derulare inainte si inapoi. Aplicatia va prelua
melodiile stocate pe spatiul de stocare al telefonului.

3. Descrierea proiectarii elementelor de noutate


Ca si elemente de noutate fata de versiunea din tutorial, am
schimbat paleta de culori a aplicatiei din fisierele de layout si am inlocuit
functia de trecere de la o melodie la alta cu derularea inainte sau inapoi a
melodiei curente.

4. Descrierea implementarii
In fisierul AndroidManifests.xml avem:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.playaudioexample" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".PlayAudioExample"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
</manifest>

In fisierul de layout avem:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".PlayAudioExample">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/screen_background_light"
android:orientation="vertical"
android:padding="10dip">
<TextView
android:id="@+id/selectedfile"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:gravity="center_horizontal"
android:singleLine="true"
android:text="Not file selected"
android:textColor="@android:color/black" />
<SeekBar
android:id="@+id/seekbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="100"
android:paddingBottom="10dip" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/screen_background_light"
android:gravity="center"
android:orientation="horizontal">
<ImageButton
android:id="@+id/prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_media_previous" />
<ImageButton
android:id="@+id/play"
android:layout_width="wrap_content"

android:layout_height="wrap_content"
android:src="@android:drawable/ic_media_play" />
<ImageButton
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_media_next" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

In fisierul de java, com.example.playaudioexample, avem:


package com.example.playaudioexample;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.ListActivity;
android.content.Context;
android.database.Cursor;
android.media.MediaPlayer;
android.os.Handler;
android.provider.MediaStore;
android.support.v7.app.AppCompatActivity;
android.os.Bundle;
android.util.Log;
android.view.LayoutInflater;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.view.ViewGroup;
android.widget.ImageButton;
android.widget.ListView;
android.widget.SeekBar;
android.widget.SimpleCursorAdapter;
android.widget.TextView;

import java.io.IOException;
import java.math.BigDecimal;
public class PlayAudioExample extends ListActivity {
private static final int UPDATE_FREQUENCY = 500;
private static final int STEP_VALUE = 4000;
private
private
private
private
private
private
private

MediaCursorAdapter mediaAdapter = null;


TextView selelctedFile = null;
SeekBar seekbar = null;
MediaPlayer player = null;
ImageButton playButton = null;
ImageButton prevButton = null;
ImageButton nextButton = null;

private boolean isStarted = true;


private String currentFile = "";

private boolean isMoveingSeekBar = false;


private final Handler handler = new Handler();
private final Runnable updatePositionRunnable = new Runnable() {
public void run() {
updatePosition();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_audio_example);
selelctedFile = (TextView) findViewById(R.id.selectedfile);
seekbar = (SeekBar) findViewById(R.id.seekbar);
playButton = (ImageButton) findViewById(R.id.play);
prevButton = (ImageButton) findViewById(R.id.prev);
nextButton = (ImageButton) findViewById(R.id.next);
player = new MediaPlayer();
player.setOnCompletionListener(onCompletion);
player.setOnErrorListener(onError);
seekbar.setOnSeekBarChangeListener(seekBarChanged);
Cursor cursor =
getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null,
null, null);
if (null != cursor) {
cursor.moveToFirst();
mediaAdapter = new MediaCursorAdapter(this, R.layout.listitem, cursor);
setListAdapter(mediaAdapter);
playButton.setOnClickListener(onButtonClick);
nextButton.setOnClickListener(onButtonClick);
prevButton.setOnClickListener(onButtonClick);
}
}
@Override
protected void onListItemClick(ListView list, View view, int position, long id) {
super.onListItemClick(list, view, position, id);
currentFile = (String) view.getTag();
}

startPlay(currentFile);

@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(updatePositionRunnable);

player.stop();
player.reset();
player.release();
}

player = null;

private void startPlay(String file) {


Log.i("Selected: ", file);
selelctedFile.setText(file);
seekbar.setProgress(0);
player.stop();
player.reset();
try {
player.setDataSource(file);
player.prepare();
player.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
seekbar.setMax(player.getDuration());
playButton.setImageResource(android.R.drawable.ic_media_pause);
updatePosition();
isStarted = true;
}
private void stopPlay() {
player.stop();
player.reset();
playButton.setImageResource(android.R.drawable.ic_media_play);
handler.removeCallbacks(updatePositionRunnable);
seekbar.setProgress(0);
isStarted = false;
}
private void updatePosition() {
handler.removeCallbacks(updatePositionRunnable);
seekbar.setProgress(player.getCurrentPosition());
handler.postDelayed(updatePositionRunnable, UPDATE_FREQUENCY);
}
private class MediaCursorAdapter extends SimpleCursorAdapter {
public MediaCursorAdapter(Context context, int layout, Cursor c) {
super(context, layout, c,
new String[]{MediaStore.MediaColumns.DISPLAY_NAME,
MediaStore.MediaColumns.TITLE, MediaStore.Audio.AudioColumns.DURATION},

new int[]{R.id.displayname, R.id.title, R.id.duration});

@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView title = (TextView) view.findViewById(R.id.title);
TextView name = (TextView) view.findViewById(R.id.displayname);
TextView duration = (TextView) view.findViewById(R.id.duration);
name.setText(cursor.getString(
cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)));
title.setText(cursor.getString(
cursor.getColumnIndex(MediaStore.MediaColumns.TITLE)));
long durationInMs = Long.parseLong(cursor.getString(
cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION)));
double durationInMin = ((double) durationInMs / 1000.0) / 60.0;
durationInMin = new BigDecimal(Double.toString(durationInMin)).setScale(2,
BigDecimal.ROUND_UP).doubleValue();
duration.setText("" + durationInMin);
view.setTag(cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA)));
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.listitem, parent, false);
bindView(v, context, cursor);
}

return v;

}
private View.OnClickListener onButtonClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.play: {
if (player.isPlaying()) {
handler.removeCallbacks(updatePositionRunnable);
player.pause();
playButton.setImageResource(android.R.drawable.ic_media_play);
} else {
if (isStarted) {
player.start();
playButton.setImageResource(android.R.drawable.ic_media_pause);

updatePosition();
} else {
startPlay(currentFile);
}

break;
}
case R.id.next: {
int seekto = player.getCurrentPosition() + STEP_VALUE;
if (seekto > player.getDuration())
seekto = player.getDuration();
player.pause();
player.seekTo(seekto);
player.start();
break;
}
case R.id.prev: {
int seekto = player.getCurrentPosition() - STEP_VALUE;
if (seekto < 0)
seekto = 0;
player.pause();
player.seekTo(seekto);
player.start();
break;
}
};

private MediaPlayer.OnCompletionListener onCompletion = new


MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
stopPlay();
}
};
private MediaPlayer.OnErrorListener onError = new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
};

private SeekBar.OnSeekBarChangeListener seekBarChanged = new


SeekBar.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
isMoveingSeekBar = false;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
isMoveingSeekBar = true;
}

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (isMoveingSeekBar) {
player.seekTo(progress);
}
};
}

Log.i("OnSeekBarChangeListener", "onProgressChanged");

5. Prezentarea executiei aplicatiei


Am inceput prin deschiderea unui nou proiect. In elementul
manifest am adaugat o linie de cod pentru a permite aplicatiei sa lucreze
in fundal.
Mai departe, am stabilit orientarea ecranului la portret pentru
simplitate.
In fisierul de layout am inlocuit codul pentru a creea o lista unde
vom afisa melodiile.

Mai departe am adaugat butoanele de Play, Previous si Next si am


schimbat culorile.

Aplicatia finala, functionala, ce afiseaza lista de melodii disponibile


pe device si reda una din ele. Rularea aplicatiei s-a facut direct pe un
telefon ce ruleaza Android 6.0.1.