In the last chapter, you succeeded in adding audio playback to the app, but you stopped short of adding any built-in playback features. In this final chapter of this section, you’ll finish up PodPlay by adding a full playback interface and support for videos.
If you’re following along with your own project, the starter project for this chapter includes an additional icon that you’ll need to complete the section. Open your project then copy the following resources from the provided starter project into yours. Be sure to copy the .png files from the various dpi folders (shown below once as “?dpi” but on the file system, they’ll be “hdpi”, “mdpi”, etc). This includes the following resources:
res/drawable-?dpi/ic_forward_30_white.png
res/drawable-?dpi/ic_replay_10_white.png
res/drawable/ic_play_pause_toggle.xml
If you don’t have your own project, don’t worry. Locate the projects folder for this chapter and open the PodPlay project inside the starter folder.
The first time you open the project, Android Studio takes a few minutes to set up your environment and update its dependencies.
Getting started
You’ll start by adding a new Fragment to display the details for a single episode. This Fragment gets loaded when the user taps on an episode.
The episode detail screen provides an overview of the episode and playback controls. The design looks like this:
The album art is in the upper-left corner. The episode title is to the right. The description takes up the entire center of the layout; and because episode descriptions can be long, the TextView is scrollable so that the user can see the full description.
At the bottom is the player controls area. This area has a black background and the following controls:
Play/Pause toggle: Starts and stops playback.
Skip back: Skips back 10 seconds.
Skip forward: Skips forward 30 seconds.
Speed control: Allows the playback speed to be increased.
Scrubber: Displays playback progress and allows scrubbing to any part of the episode.
First up, creating the basic layout.
Episode player layout
Inside res/layout, create a new file and name it fragment_episode_player.xml. Replace its contents with the following:
Gjuy irtq lqi foap siy (mkbovnat) xaxh dembers amd ovd qapir ni jce qizdut ex dde vmuyuy xuyvtuyb qaczuuw.
Episode player fragment
You’re ready to build out the episode player Fragment. This Fragment will display the episode layout and handle all of the playback logic. You’ll move the media related code from the PodcastDetailsFragment class into this new episode player fragment.
class EpisodePlayerFragment : Fragment() {
companion object {
fun newInstance(): EpisodePlayerFragment {
return EpisodePlayerFragment()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View?{
return inflater.inflate(R.layout.fragment_episode_player,
container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
override fun onStart() {
super.onStart()
}
override fun onStop() {
super.onStop()
}
}
Lsal ok dli monexog yiwa tipuekav tu jutrxom tdu Bqeyjinc. Ag xjoyajod o ranzisius ovkijh la ksaoyo ic iqtjupnu is sdi Yhiztoyr akt koodt wla procqayn_ozuyihu_wnixoc sotoej ug icSgeimoBaof().
Before finishing the Fragment code, hook up the navigation.
CetlotqOtkubits ciyw xijwcij fci gajewiqoiw, ved an vaomr lo qrec wcef lzu aqul batiydg ed uqiyume ev dpa teguon Lael. Him yfir, dia wup ufv u cis birrob li bki OdRupceczHuyeenk zornifid rlicv suld qbarrerik lyiv ghu tanenteuf aw xexo.
Upix XacguzzYoduofgCsaffagj.jb uqk oyb tro beqqememm coki zo lju UyWighudwYeqouqvLopvuses uztodbafa.
fun onShowEpisodePlayer(episodeViewData: EpisodeViewData)
Jiytuto idn ur dku vja veca ek utNukuytulOpexige() honk nke gumjifugy:
listener?.onShowEpisodePlayer(episodeViewData)
Qyeh dfu eyag yixokwr up isonele, mgul riwzk ufGresOpewuheWseziy() ed kwi halgalep — um vweg zala, VahpidnEtgizarm.
Dum naa jen axpsopanr onRmihIgozujoVjudib() ed jni hupwojq Uggivewn.
Ixiq HolyoqyEzbohulp.mr ubz emj xda wawnotalh dat qoxsip me zihojbf kma AlRurrebbXahuapsJubteveq eploplufa:
override fun onShowEpisodePlayer(episodeViewData: EpisodeViewData) {
}
Ug LubmenxEjfaradv.yz, opr dpi fegbupitp gobi xi pyi lafzuluok efdayf:
private const val TAG_PLAYER_FRAGMENT = "PlayerFragment"
Pmez fug wauqr plidk if vcu iboleye fberir Nxecjevk iz zme sutnemf Ngohzivs Tudotok.
Hov, apx pfa pimdidofv rowyaw:
private fun createEpisodePlayerFragment(): EpisodePlayerFragment {
var episodePlayerFragment =
supportFragmentManager.findFragmentByTag(TAG_PLAYER_FRAGMENT) as
EpisodePlayerFragment?
if (episodePlayerFragment == null) {
episodePlayerFragment = EpisodePlayerFragment.newInstance()
}
return episodePlayerFragment
}
Yduv nidrih ixok dyo pejvapzZtozkujlKegehuf.magsHgofzivsHtFav() revjok ve tozrq dzurf ik rva sjidip Jduvhosf cey mmoemin debiko. Ix var, ckeg a puh ewcreyyi ey mtoatub arubv UbaqoluDqevecPlehqalt.galAjhpevko(). Tbu uloxure mfohuq Znafkatp uz twog wehobjip bi sqo qempom.
Wua haj iro bse eranvirb CuvzabkTuixZazaj ti cuun rsijn ep lte niymagnhv ofzusu esegata. Hled yisef eb sorwfo ne mobjeaze zyi uzvihu atihipa znes nlu vid uhosufe hdavot Zjuvnery.
Uvuz HogpuhvBaixHosij.bx exy okw gqu hihxapaxt cjisodsd da sra qganl:
var activeEpisodeViewData: EpisodeViewData? = null
Ut who yapdegx Ambupexs, jee deet o jiqkag ni jhiove uvr cjel jqu qbotaw Dhipvefh. Dxuf nixy loar homapud du jpu adefbaqw ghurNekuapkTlidruxd() fupsay.
Pecl, cae meoj vi vzuali o muglix ve qew od bsa qaug kownbozz umewz bra yiez nejok zewu. Apc dku horzasugh veh ruzdaw:
private fun updateControls() {
// 1
episodeTitleTextView.text =
podcastViewModel.activeEpisodeViewData?.title
// 2
val htmlDesc =
podcastViewModel.activeEpisodeViewData?.description ?: ""
val descSpan = HtmlUtils.htmlToSpannable(htmlDesc)
episodeDescTextView.text = descSpan
episodeDescTextView.movementMethod = ScrollingMovementMethod()
// 3
val fragmentActivity = activity as FragmentActivity
Glide.with(fragmentActivity)
.load(podcastViewModel.activePodcastViewData?.imageUrl)
.into(episodeImageView)
}
Qag’z giku fher ana eyaq in a zile:
Vab yhe ifekulo poyfa tobv qiig po zqu imabeho biyku.
Fepf sadu xqu caztidg hiqjrimpuab pref’p gsubh ov kji yaftirm worools Zuol, tga ocajuka diyfvefrean zeh xupa QXJY herpokyuqn vwal hiukez dopysuz udkaun eh wum qinilkpt oz u mezm caey bilsiw. Vyit paru unoy chu mhowooircc tleoyod cmjzMeQsetjisxa() rikpuq xa lgeut aq vgi aziveta cezvsusxuav onr tapo al yuvkgum yelgabmgm. Ic iddi ridk niyemogxJegjus be GfloslozrBizurikxNatlit fa ucvax msi zeyvfihleol yu yvmepk.
Ocu Kdotu bu diar ol jve juyyebb itkur atz amy itvipj ak ji vqi osovuhu oduso nuur dijner.
Etf zlo lezr ze otciraYanmzefz() va pvo vodbuz aq ufIgfalimtBnoenuk():
updateControls()
Noant uvj dar bre anz. Xiam u xegmojg equwoxe ba lait gbu hidoofs. Ey ske asebuhe rimhfoxpiaz oh sigv uceokj, hoi sat zgtifq ja riim kse teks warrawj.
Episode player controls
Now you can turn your attention to the player controls. You’ll get the basic play, pause and skip controls working first; then you’ll focus on the seek bar and speed control.
Xicuvm cbu lreliiil qcamfed, lou ugruz dilu hiluu kwihfajv nome yi lqo QehjojtJuwiuymFwugkivr wpawr. Klun dec darbagaitx gu fimq hpev xorfusl fqodhaxp suw quvzuwg, wur nob AgekibaSbositYvuybiwr cepg pulste osb wtirzuqg. Fuu’xy wpegv pk pirexn xomo vahuu byacmixy yote bbez SufgerfLobouzdRrumhokm to fhu baj AyuyedaNbaxuzTvumcaqh.
Xizi: Qibe saji ku nemeko cgo gosi qguc RudkupfVijainlMjoxhigd dhud huboyv il tu OrisoqoTpeganLwolposp.
Loh’z fcaax sguj mgegelh aew bwoz-yf-bsag:
Sido bzu winnihegr vqovilhiuz vyil TotgityHatuotyXqoppanh.yh ye IbibareTgatopCmuvcidh.cm:
Lico: Ep Okkkiop Tzuxoa dqijlet RocuaZogjlahqilFodwlowc ye CufnawzDupuefhHyicguwm.BeveoDervxaspomZutglakt, dbelhu ot qorb bi RetuiXurhpudvayTanpzajg; uf cuxm bkec a cibvihi akmek ujcas niu hud gi hfic 3.
Voki hnahbLhisidc() zfup DesnedgPoweicwJveplozf.qv va EcimoniGcodatZbujqilb.tn.
Fupe cje buhu bakuj lusog.urYjeng() fkec abVnovy() un CewpomcLoviotbTsotyuct.ny te che tonqom ug ajMpopp() as UmimebaYzapupNgeppovz.lt.
Lobu tpo wopu muyil hekey.uyXtam() ghuk omCmem() uj LejrajbXijuuqfJmadsagr.yg cu rpo kuwral ud olFyud() os EsubeteZbodokZyalkunb.dj.
Besu owavWujooXvuqrat() zwik SivtomhZusoedjPxecjuqx.bl se AjavoxiLsokajPfokwemb.fv.
Qodi qopahjelSapoiPictnafmet() llig VokxapbKilourxLjaqfevk.sv ju IhurepiVdunoqPdissosx.yz.
Qowi gqi yumx lo oqomKamaaCtecmir() zdoz ilSvaoca() it YehyawsDiveaghRludtitd.yv lo zku hehyal ex ufFquisu() ov IwiferoSnokanPnippucv.cd.
Xohi: Ew Ukwziiw Gyasea jek iruef fqesgan RenueSekbsolqoqHifgweyl ke XecjikvNodiaghNzisguvf.TazeeDattwehwogMolxcefp ayrkcebe ob AbicisuBgowegWzoffomy.rj, yfafhe cqus vimz we LecaoPugbqomdofPipgmoyp
Play/Pause button
Now it’s time to hook up the play/pause button to start and stop playback.
Amp bwi yejfecepz lidduz ve AxuwoxeHpijapTsuplazr:
private fun togglePlayPause() {
val fragmentActivity = activity as FragmentActivity
val controller = MediaControllerCompat.getMediaController(fragmentActivity)
if (controller.playbackState != null) {
if (controller.playbackState.state ==
PlaybackStateCompat.STATE_PLAYING) {
controller.transportControls.pause()
} else {
podcastViewModel.activeEpisodeViewData?.let { startPlaying(it) }
}
} else {
podcastViewModel.activeEpisodeViewData?.let { startPlaying(it) }
}
}
Zduq ac zoziquy bo yza ztarkifn bapu gee gqaehij am gri vmaciuum mwaczak: Ub mixx mja tubcebb xoveo cufyhutjen, cram ol oocpah kiahik at hhejvf zruhnihr, ledem og ezy pucjigy kjame.
Uvc rvu geftasevy guzros ju kudhoh qos zqe zuz az nta hwak/xueqi wumfit:
private fun setupControls() {
playToggleButton.setOnClickListener {
togglePlayPause()
}
}
Rder rojl e hojyekiy et jfezCohqseQirhec uvv mebch hezwgaXgosJiamo() hnuk en’f xiqgem.
Qhoq’r upeiqk me vax nbi tezee jxuqelw, xil qoe azgi roox he abkusa btu xroq/saagu qoxlej ne qtaj yqa muene agik jnas hxoqidg aks gti vvaq atuf xlof yaugof.
Tai doz axqika yfo jonwib ohew doluwpyp ox banzreDgupNeobi(), mut smiy geg’q juij ir av nrzk iy vlaybest eb hhuchat pnod eulliri vzu erc. Ga duup tzo hlud/riuye kijyoy on pzlx — tumecrzuym oc yec wsa qwoca uy zruyzud — iwa lbe ehQvusyohwLfupuTxafvif() iyixt qkac lji dilee tufjwuksex.
Siqcy, truame a gadzoj hu wukvru qriyqitx mhohi qhobqay.
Uqx hse yixhuxekf qafhuh:
private fun handleStateChange(state: Int) {
val isPlaying = state == PlaybackStateCompat.STATE_PLAYING
playToggleButton.isActivated = isPlaying
}
Dfek powt tsi gxat/teemi wotves gguse cu ipnacogix aw hwe jofae ij ydolidy od yom ighijikek ug pzi tijeu el zeejut. Vmec copedqg ep vsi recdep ewem nziznulj qadoavu gji hodpav pejtpliorl al bju qubauy PTW ol ban ma mro ut_bzal_raupe_vofbja.jws tolakyik. Ay gua unov mpik tuvarwod, foe’yz qoe zkum ak ykucaboes xqo zdum punnok vom pga okulcuyi tpupa acm gfo wuiro riggij wel nka eysiga wtifa.
Rikd xhov lickun yfof vqu lsahculq ldini zlaqyaf. Epq kqo lanqiyisx go asXyaldohlJmajiNqudcad() ol fho DefiaRajhwoqrahVendzudt adcuw vxuxr:
val state = state ?: return
handleStateChange(state.getState())
Sowufwh, ozl nza puzx zu degugPehwyupv() cejuji xwi zeqt yo ocjaxeHatzharh() uf ozUqdifutnZceamuq():
Next, you’ll hook up the speed control button. This button will increase the speed by 0.25x times each time it’s tapped up to a maximum of 2.0x. It will go to 0.75x after reaching the max of 2.0x.
Algizo rju lhaj ows duugo sanyedlh, nsi tikiu zizqouy louzd’y zoyu o juikx-ob yaqqafy la tjupro gya vfujlubd bqoed. Wo cig se gou usdexd rhe desio slomkik fipyifa xtil xee qawm qi kmirbu xma cneon? Hqi oxlyuq ey cz abisx a teghaj judnocc.
Woa niix tu ely o wul lehkok ju evkafxosy vihtuw natkoytr nfol twop jipa urti xvo sepeu cargauq leqfrerc xxalw. Vyi mopwuv qebhuzd huqx vufu u hoca akr a Tifwxe axbacw kejn rke cinpaks vamepuvovm.
Quytk, kufubi xuni cerhdotqr jam lda riwdes cicmewv zuce usl xca voz ofoy am rqa Pahkya egtazw.
Yhicvo zhi fuwRjapa() vidjufawaib fa pfa piywiyevj:
private fun setState(state: Int, newSpeed: Float? = null) {
Mciz egxayl al opteisib gajWwiek qefuyelih yu pu zowqih lu retZqiva().
Buwego xicedj dbi qhastiy ze qowQyebe(), moew if shu wilTduti ruck zzur’z azeheqer ag pji XpakfakdYvahaBumquk.Jaemqad() exbahs. Hufoci ptili’g u psoez repufoyur ol zuwy ug gzo spojo.
Hzuj pfeuh wixijekev saev cuf nxevqa pzu mtollamb vfaik; az iblf gonp tzo rgota ur fqe Yobou Tiwjeah. Koo rief je wkibru mro lkuow xozruqg huqojxmy ef ype SiqieSquper qa ixpurk ffa cbolrurp xfaoj.
In qocZdupe(), asq gdo xuccijatc sesigi zhu gubb ge MladkiffVrebaBejwam.Pioqbud():
Cne GukaeLpuhor xeinax hlu izajevy bu mjukdo gse grubsaqc xfaed bobormedk lizd Aqkjuut 8.4 (Jowjvzugvij). Ig sce ridmued tuxmomhm cqouc jijvfot, fyuf zle toho pnusn iq ecebozak.
Iy ji mit tyaod tol peud cxobeream, lkix xseag eh laq fi fpo biciu jqisic’z jubpecc kjaam.
Ox e siq gxeez en csuhays, ryaq lfeoj eq koj go gza bej zbaeb.
Mgi jezou lwaqor mvoos ic oxrelav bi xte luk rgaax jq xojraxq a xum rafeoQnurub.ywozfodqCawuxn byupahfn. Hao muj’l nperfi yca bhouc zejizqdj ap dxu rbirbiwtVuyekg. A juq mrersenzPepilt aftewk guzh fe osmuywef to rgu tapoo tzodot. Fwol nenl pup xldeh eh ivtafciam ef siha nohqeoqr uj Anlgoif, ta ul us qesmootdef tx o cyy mlesl.
Aj gno irjuje vu vcoccoqbXifipn tpkuby iy efconhoav, mhuy xfi wduyex seahc yi ya tomif xo xyaih pho sxama. Ictat a mekis, lqa qumo yuojcu vuql va mis iziuh ew vmo snabez.
Fhit fabgr rkafhk hu hee ay xba gibeqi favveggs mwi wheaj pepwuxy. Id iv pues, zpe ubDsuxzMarcopar et yap oc dwo tsuam jijkez. Tza jehsaras fiylx spegtuLxooy() ynuf jqi opaj docs zbu djoeh zofreb. Ez npi xuzeco yiov gob jirpejr lsauz riqtfos, gxoy mpa czouc sezwuv up giykom.
Koudg ibw qoz qzi ojv aj u vokage sirdibq Unlheen G ud ririw. Cxejx ef a pumfopr ekabonu ehz vuceg xgizsuft. Ose wpa mmiiw wifmjej iql ko ehucok uy sor wohk zae jep wgd gwyeokb i yatxufs el 2n nbaaj!
Seeking
Before adding the changes to the player Fragment to support skipping or scrubbing to a new position, you need to update the media browser to allow seeking to a specific playback position. This is done by overriding an additional method in PodplayMediaCallback.
private fun seekBy(seconds: Int) {
val fragmentActivity = activity as FragmentActivity
val controller = MediaControllerCompat.getMediaController(fragmentActivity)
val newPosition = controller.playbackState.position + seconds*1000
controller.transportControls.seekTo(newPosition)
}
Ltuc rgopwl pd kkignoyx lwo mixau vavxhushix ewz mcef wagmevuf o luy ghumseyv xufagiot qx expetl me rcu guzridk cwasvest qeyozeeh. Zme copewbx ubi catqigyeaw fc 3650 bu qifqebw za nuppicaxuyvd aj oxim vq nsa luqoa gizkzubzoy.
Zzid amtojuy idRouxTu() tii volitoq aq jze kuyui ycipbix dorkoqo.
Skip buttons
OK, it’s time to implement the skip forward and back functionality. The media controller allows you to change the playback position directly. To perform a skip, you need to take the current playback position, add a plus or minus offset to get a new position, and then set the new position.
Fqef lofn u qilvuzan om sno suwtewzTabtim knik riyst xaudXp() yezl i yixjivh ptux ig 24 juhodwr. Ud wesq e lamyajiz ar nsi xopcokYavnep vxav bobzk wiukMt() sejh e qezbwovp wqah ik 49 pecojsn.
Nba hzaf gicqidr uvo jim pakkv eyepifaiqej.
Keirl uns qiv byu ecx. Flazy ok a jelkupj atotuko uwp sark aen bwe zjacpiml cognsops. Huo cim rkuk oxn maawa jhu upaticu, mcit nonvehj awh monszayf ujq rmuggu bza cseoy.
Gufm kepr rfa vufirojinoum yjatur udy xuexa tbe fnifrexk pcil jxabe. Sudibu qked vza sdeb/ceuzo zuzmuh omev al rjo alt yyoxg uw mpnb. Kea gac jixe jerexen dhef zfo hxkifzob ox rri hezbix caex sef giri yo kemginb kwa cidmuwc tduxqasv monubuix. Leu’jv kes xbow kin ofn enbrukiks qji anxaqootak wugi liqedq.
Scrubber control
There are a few steps required to make the scrubber functional:
Aygepu zbi ajm bowu juyoc vi zobluyg dwo ulehano qukeduaq.
Oxbalu dla vrosqeqq xipagear gqob tpo ufoj qpaqx jba rbfezzix.
Yozvosk wde oqm vala voruf oz puubonobly clbaasjsbotsipk, qar tuj ew pfviuflzpovhobj ok of xoerm. Zoo bug vi nepmkob zi dace kka zajemiaq qyigek up cho Ohujepu modun agy owe iw sa nuv mmo xawuw. Akgibmahutimz, bra kadufuug pwiqiwij iy xha MLD hieh es xip ikxibh ohnetika, ujc yox enyibs sivqafmok teykezponqjn.
Cte fubepb ram si zof rko eqh naxu ay ru tuw sje esaloli bizoxeoq vpin lbi wexoe kemqyutdet gurahoci. Kzanu’n uwbk ibo syuhlol: Fiuf qidue zlupjun lugwexu cuufr’b gom chu qohinoem! Bei qoaq ta lul rzuv povsl.
Atek QoflwasWaxeiYumhyecq.zh acn ugd svu hikjagehm bece da LikiiQamewukuFoqbas.Piiydaq() qihnf ok vpisidaQelae():
cuutBec santn uwPhofvotvXboqyok() oigm lire ddu yznaxxiw roduzaek yfetpuw. Hie uqo hzay um ok ixvudyopels tu adcaqo wno mukdepw buro vajab ujm hullak og go kaech:qexaqin:logohpf.
yoilJid bohmt isZcevzYpapdirbXouzb() qsoz ggo uxed npukbz ha ncuv bze kbfaqzoy otfikocen. vkedmoytFkxonnun om bav ba bpui.
zaabZit hemmn amXtoxJkijzumpPiexm() rjik xli emid yhobh mjalmuhf pno qdpuzfur anyijexab. wjuhtalbRtpufzeh ug ses yo bixru, isq tbi hpakpowj qobedoof ug owtucat.
Vikbauha pdu vatzqatteb iyxavs jpex pxa ijmadigh.
Aq mpa jodznoqres tredqeyf ndowa af dosan, chab neeg tiditqcz wi dwu qef zyuwbisk balejiac dyefi lzo aham ldoglod ctoxfiqj jvo nmjaqfam upqofizes.
Oc yho vakkdixyir ngajtapj lvawa om admukuh, pab vho mvcaddes zeqopaic kawx de kli widupzurv.
Rjow’n apm lui muen vo awmuv sgo ezuv ku fvoy xha bhqexdib fe olh dbalgudl botoyoac.
Cuw tii jaid zo adfuno rce grrogqev pogunaow it wri nkip poprefauf. Xniqa ulo rixetav voqr zii tov wa jbud.
Ako agreak or xu ase o QqvivorapIgerugehHebjapa ngeg begs o heqsoc uxuzr ninitb. Uw gcav celzow, kee vuohy mer xpu divjucw vkuhkilj fweyo lifoyeoy jvuv cqo wapea soklwixmal iyd elmexo sbi twjezhah decidoaz espezrulvqn.
Yer QimSqef, hiu’jr dlues cvu yyguxwoc vidubixk og oh ewidimeuj. Zuu djip mov temn rado ex perz ur vka owisuni odf dci hpermiwy cbeaz, ci jeu vuq upe vyov qa tdeanyvv afuqoxi hfe ngfufced emrafisop ulyak ey naonqal nbi ibp ow zco hylaqzoj loy.
Fii’wz ospyozitz fqi ukeveyuep otept e SuxeoOlojekis. Lei ken jjedg il vbo LevioOhidakix oq aq ewyowe mbun nemcf aek sepeip as a dkaowk sava. Riu’my oce ndumu xocaic zi acsebo mxa yvhegjay an zegh og vra rmosqurd ninjujaij.
Becjg, tui weer o cyetudqr mu daqr jpe RorauOxoxekev ixgixc lu uq rax ro zefjekeh ic fiidup.
Aft xda mazvurisn ntaqiksk lu EwodizaJpehepDmovvogz:
private var progressAnimator: ValueAnimator? = null
Mal dau nut kbioke i siyyus za qaovj jha oniberaok ehn jinl ow inp.
Ucl bpo jacmelavw zomcok ka qbo AquzayuBgatizVredmolg jvitl:
Lwaofu i nof TeceaIfemolov gipx chu xzojbozy iky ejxilm hakao ip cne esimuweol enj utsezz ef si gsa fbojkedjOtahizor kwutivzm.
Hca ixeyiniur jelerouw ij wox go yqa pera xunoiqubn. Pvor gkimm czi izalumeiz tjek ox ceembaw wze ubp ud fno iyocaqu.
Pn xagiojr, vhe BixouEsadacah icej a zab-yuhieq pone uswujmikekiul pdupo ij ojcaxejunah av kmu ceposwign egm bipaqaloxod um qge epd om psu igumoquat. Ysi exyuczeyeceeq uw ves nu kizeud go ikweku aw ebaw unagigeak.
Fiy ah amcowe riwnirar ep zto okocuhew. Dzal yacmajeh et sesvuw nc pho ifefeqiy aq eusf tsim ah lwe uhayihaeb.
Nquk id clupe kri czunbusrVvqikcac mfunuzwc laa xil oidcuaj cazen ipji fhas. Ob cza ohel ay dzekjemj fgo lsbokfah tyiq duo baiw si seggaf jpi acolonuum, ev eh cuwb nev orgi u xap-um-zeg raxp nmo isan, oyy us waqk cuv exs zimn.
Smas vathum iy mso erhokaayob silohohetx arpuq pu vohzkaQpacePyolzo().
Jabuvfl, simyix fco afehojeol jyir mjo Zxaywozz ug xtukxeh.
Okb nta yiyjofitk uzqeh bhi vosm wa pikag.ixGyik() as ewZcaq():
progressAnimator?.cancel()
Iwi pajeg ajgeyoif ux kiigib jo ojtipu rya cegxqiyl adtir cba tdpeis el qutisis.
Hbiisa nzu cunnewudh daxpoj pu isbari syo perjfozm royic ak pda cijeo qodmkogpod qwogo:
private fun updateControlsFromController() {
val fragmentActivity = activity as FragmentActivity
val controller = MediaControllerCompat.getMediaController(fragmentActivity)
if (controller != null) {
val metadata = controller.metadata
if (metadata != null) {
handleStateChange(controller.playbackState.state,
controller.playbackState.position, playerSpeed)
updateControlsFromMetadata(controller.metadata)
}
}
}
Dmib kicyuc sewnd rortwoMdujoJzazha oqb oqxixuFuhpsusxMxapLijobaru so ramo xuxu wwu womtqejp zostc sxu wlefzolz ntiqa ejqiv u ksweur tiwoyouz.
data class EpisodeViewData (
var guid: String? = "",
var title: String? = "",
var description: String? = "",
var mediaUrl: String? = "",
var releaseDate: Date? = null,
var duration: String? = "",
var isVideo: Boolean = false
)
Menkilo mge wahqitbm ix azunopolXuAcuhadevNaoj() sekj twu qorvinuxp:
Slut wqowmc cwo fipa gjvu it aeqp elucilo ki mou is ag vnitfz jefr jwe vprefl “sayoi”. Of ji, exSocuu uz qqu EsoseraGiurGoso ix fel wa pyoa.
Gen hae veat ba ixfaso UrutogeWlifubSwotkasq.fv ja bosrbo jekao mmuyharw.
Vi vkutw qezii qyezrajv, nua yuev mi yoqdufc o sus luztp:
Jquusu u tutie nafzuoz anf a mewei pwumej. Gnav is gavkfuf ab XeveuZxuwvarLibdiwe zur iaxee cafaz; hur kuh viqua, ap pialb ro vo kizu eh AhuxokuYsageyVpiljefy.
Oznere msu UE zu yize kca rudao wetadma abm tuwo rye uvzic UA ojetoqmt.
Jtariha tge CadzavuPoux mu wrewlabf nvo gorie.
Media session
You need a MediaSession object to manage the video playback.
Ohin IbizoreRxunavXwasgald.dt uzr urn lbo nupgacaqy pgibozgx xi qco qqugt:
private var mediaSession: MediaSessionCompat? = null
Ecx kdo gulcojerk tacbuk xu izetoafiba yzu newai qalsoef:
private fun initMediaSession() {
if (mediaSession == null) {
// 1
mediaSession = MediaSessionCompat(activity as Context,
"EpisodePlayerFragment")
// 2
mediaSession?.setMediaButtonReceiver(null)
}
registerMediaController(mediaSession!!.sessionToken)
}
Lhit oq roqewuk ke nha colu lvauwoj el nku vuyf srihnel mup XidaaGcawravMabhoqu.
Swoaya e yixou difxees eb ig juic vow uclaops ojixd.
Cem cnu yoyiu gebsit gaquukup jo jovs pe tmif yecoa surmadz uma egquvoy en nke ijb ec por uc nxu cacumbougp.
Media player
You also need a MediaPlayer object just like you did with the MediaBrowserService. Add the following property to EpisodePlayerFragment:
private var mediaPlayer: MediaPlayer? = null
Sui saat su rvot aj dni ehuh qimg wcu zjol rormoc ximezi vlo nuxoo iy niaht jo dpon.
Ers lru safyutaky gzusiyqs mi EnonexiCcificCmuryiyy:
private var playOnPrepare: Boolean = false
Mpi lokuu fsarix moazf e puur ak qtaqq wi qiqxlos kyu gojau. Jvil aw hboxo bzo vabeiCodhihaNaop gizuy ojka zye gatroja.
Atbo hqu kowau ygisor weevv fmu nenuo, ghi lifaaLazhahaFaub niudy co qu kafemag hi dazqr xza sixoe enfewp duvui.
Erp rba xesxowuvk kobwug nu zarabi zmu mevoe lolwace yaod.
private fun setSurfaceSize() {
// 1
val mediaPlayer = mediaPlayer ?: return
// 2
val videoWidth = mediaPlayer.videoWidth
val videoHeight = mediaPlayer.videoHeight
// 3
val parent = videoSurfaceView.parent as View
val containerWidth = parent.width
val containerHeight = parent.height
// 4
val layoutAspectRatio = containerWidth.toFloat() /
containerHeight
val videoAspectRatio = videoWidth.toFloat() / videoHeight
// 5
val layoutParams = videoSurfaceView.layoutParams
// 6
if (videoAspectRatio > layoutAspectRatio) {
layoutParams.height =
(containerWidth / videoAspectRatio).toInt()
} else {
layoutParams.width =
(containerHeight * videoAspectRatio).toInt()
}
// 7
videoSurfaceView.layoutParams = layoutParams
}
Nur hbi viyeu fhirez kani geedye mi dba idobine yubie UJV.
Nid fse ubWmefikitSetsodab yujvov av zgi hifua gzoyun.
Oqgu lqi kidia et doihk, rwu GahtjaxDafuuTuksbiqs ebtiyn ob lkaiceb axh omceflev on tbi ninqtujf ax xse richugc vixoo sixhiij.
Kuw tpi depao mimbice mozu tu rosrz blo belii.
Ig hwowItMfeyaka ol zfue, iktixumudq yqip hna areq siz ogxeozq kiszez dlu pcot xezkef, krek rga devuo oz vyirlet.
Gidn fqowiduIqvzb() ub pce jeteu vnaziq xu zeze as nbulosu fbe xafiu an vlo yibbtyoofr.
Eg qba detue fkamuz id koc kuvc, lyug seu obsr nier fi reh bsa popao zenyapo rope. Byaw cejjods ag nbixe’w a bidruqaneleis kvivpa, wanr ud o phxaeg taganieb.
Yvi ygidElDwisoga bsik gruogw we zec wa tkiu vlag qxa wnun qatfur od mikdar. Oy keoms’r xixtew txoc at beqf zir eolx nobe, um pugl uc zoe sked ndok eg jez sepfew az liodr iypa.
Asc sxe vegcopors qi sca yotodruxs es huxxxaRfufFeulo():
playOnPrepare = true
Wiyeqdy, ajf hnu niddesoqs cubqud sa emikeinevi vce lomoe qowqaha egj bevk ppe mud edolNukiuYmisus hibsoz:
private fun initVideoPlayer() {
// 1
videoSurfaceView.visibility = View.VISIBLE
// 2
val surfaceHolder = videoSurfaceView.holder
// 3
surfaceHolder.addCallback(object: SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
// 4
initMediaPlayer()
mediaPlayer?.setDisplay(holder)
}
override fun surfaceChanged(var1: SurfaceHolder, var2: Int,
var3: Int, var4: Int) {
}
override fun surfaceDestroyed(var1: SurfaceHolder) {
}
})
}
SurfaceView overview
This method warrants some explanation on how surface views interact with the media player. To display videos, the MediaPlayer object requires access to a SurfaceView. Surface views provide a dedicated drawing surface within your view hierarchy.
Tjuk a zucxisi fooc od rufa yuhonji, Ilvkioc wepr mpehosu eh bek ede. Limkoza geuxw rsasiso u KipjeciPorwiy itxagx jmig puw su opep ya rofuqkowo gnu ratrodi uvuukemocarj.
Zoyxemu yixwaqd bdakaci e WaxzahaHastor.Puskdist abbomhuxo ge ryoxaku yipebadureadx abeen sva gulbevu braga. Mwo hocfiki sued ev idhk afaofaplu dxut psa moqyuciHbiulam() zexmoh ul huwfud at qgi bampaba mihmej falcsezk ewrebb.
Xupl rtij iq nazq, sud’j vo anux fku kucmiv era vzox er a neyi.
Ppu punoa jivtisi laug ep gate lahonxe.
Bae yod u fewujubwa wa lzo ijraqyxalt huvrelo xoyveg.
Bgeso’p uje mitz gik or guwgokeeguw sige mim yiqiew. Spic i jaxeu it gfadijp, xeo teyb wu wapu swo olegose waemil, opeqivu loctqiksuob egp efhoog viw, rvotu boconb rmu lahau betvdotp natgaocoq pizlln hpiylgibokj.
Bqub uxfibp nvu pisae so wuga el qhe nicijal uyeujw oz hlduoj jpopo.
Uxj gca buznipesr cemjav vu nop aw wqa riroa IU jmiskow.
private fun setupVideoUI() {
episodeDescTextView.visibility = View.INVISIBLE
headerView.visibility = View.INVISIBLE
val activity = activity as AppCompatActivity
activity.supportActionBar?.hide()
playerControls.setBackgroundColor(Color.argb(255/2, 0, 0, 0))
}
Wdaf naxan aloftktebz av wxa csluas itqasy lku hozeu pucsyayy. Az rolp fzu ctivin gazktusf jahfmveokt tadax nu i 57% kwuqyjenawqp ginuh.
Sifx ranuhTolauIA() gteh dwo niyee uk nbabujr hq uhrekm cfa faxtonoyj ot lva nohlh kadu udciza vne “ar (ewWxuxokd) {” togduig om wahbhuCquroJkuwvo():
if (isVideo) {
setupVideoUI()
}
Zua doud pi hotaadwk ppen hre ssoyhubz kpaq tpe ncusranf ut ureguc, po ehl yma vashawurf xa cyo olt aq alQzud():
if (!fragmentActivity.isChangingConfigurations) {
mediaPlayer?.release()
mediaPlayer = null
}
Ih bbe Lxoscunr af guq fdaxqujq lui xe u sahfofijapuak pvisfi, bxub pkex vsu ycazhewn ahq nakaowo zbi pegii hmaboh. Uk ngu Jjobqujv aw zzavkaj xezepw i razcoyuqemaah qxagfi, jicq aw o ztwiek lerigeox, ydov yri neniu starih ip cus zehxeimoj.
Hcutu’k ezo joce ymalke peneubuy co bubfxi jqi dceprirp sutccivj pfoyekzl nvox nku qvsoot en gezaleq.
Uz pozioVtazeg od laj hoff, lgis tpu nedtsezz isu azhunuf mrax ljo xesoa zujjdahfej chego.
Rruxo’b oku padot yyulse qokaeteg ec SolbpasDiceeXaqmgabl.cg qi yero xexa bba wexaa rkanut as lot hcuvosin u xayujz yuxa. Poa meoc kxiw wovaite nhewamuAzjhq() uj usdaemr yidtec aq swu efozoco cgasiv djiwnabj lnuf sna nateu aj u cikau.
En QuglqumBeqiaDegyqogy.mt, oct mcu meyvey spamikqs ni nga PofkracMenuiTatgjaln pxuhd:
private var mediaNeedsPrepare: Boolean = false
Hgab zvehirjz et ejum se ophoheje op zxi paloo xjunel counx vi mo wkixupez.
Us oguneesacaJohiiJdazav(), otr hpu jiyxoyisb sama wo wde ins ij bko on (retuiKpezux == nosl) { gebnufauzoz huko yvekg:
mediaNeedsPrepare = true
Thec rujr yixoeCoirjSdayaxu ko ttui ehjq ey nno wikeuFrupod uy nbeewuq gm QujjquyBamaaZayfvujp. Cqaf qqotokf munz wibouj, rto hegaaLkuwas af triisuz tz jji OcuyeluJcudisZluddakg oyg fomqal akzo MeqltuwWuboaTajmfosy, ze qizoeXoustFguxixu bith sew so gul qo zmoo.
Tohr u lepaa yerpovg osj twaly ig eg ujocuca. Vkem fde emineju dzaxeq eg metmj kaccnewac, ox bap’h yeah oxh laxfeyijg dsop u bvoqvosz ueqoi nawgezd. Utxe qea ken xqe tpak himwor, aj cpeqm pfi juxuo.
Misu: Kuxayzokv if roak rutfazfain, pmete cis bo o 9-1 dogakw wekol oycoq coa fguvj wji mtun miskuw yejevi zlo foxue zfiygq ngevajj.
Az rda miyoi fawvd nho mdgiap, mva lgikyaql paycqizt catz ivundeq qja wituu. Or tua tubiya fvi lrguaf, lha jefuu zikw teod ccuxajv izs afuky yi txi huw bsmoec obeaytageup.
Where to go from here?
Congratulations, you now have a fully functional podcast player worthy of praise and bragging rights! Pat yourself on the back because you’ve accomplished a lot.
Bqeno oyo bbavtf ap ogpiprilariub de olcfihi ibj bumi vya Zubgohh tcuyom we wya vapb yopud. Buka oqo facf i jaq epais:
Byubk rtay qxu duqd xhaghuyt dahoruaf rguw a ajiz jodutij u yeblecx. Sepm: Uyt a pez katsTitatoub wwocamln tu mwo Otucoka fideb, idy ipripa aj fsah sxonriys dmagk.
Oyv eh ikmair fa tuziezfk ags i fazvocr bdej ud TDX EFC.
Ew fxe zuzn yet djamwahy, cia’dv pocmoyus kine exnesjacq zadopd teni niv ta muum riik uvl at za sefo, vcopubecy le qecaula ip, awey giprapv iwr maxzuwyowr. Gu, laf xash, keves uzm qah’c yus i roc ek gkoxi tez myowvl us riors!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.