অডিও ফোকাস পরিচালনা করুন

দুই বা ততোধিক অ্যান্ড্রয়েড অ্যাপ একই আউটপুট স্ট্রিমে একই সাথে অডিও চালাতে পারে এবং সিস্টেমটি সবকিছু একসাথে মিশ্রিত করে। যদিও এটি প্রযুক্তিগতভাবে চিত্তাকর্ষক, এটি একজন ব্যবহারকারীর জন্য খুব বিরক্তিকর হতে পারে। একই সময়ে প্রতিটি মিউজিক অ্যাপ বাজানো এড়াতে, অ্যান্ড্রয়েড অডিও ফোকাসের ধারণাটি চালু করে। শুধুমাত্র একটি অ্যাপ একসাথে অডিও ফোকাস ধরে রাখতে পারে।

যখন আপনার অ্যাপের অডিও আউটপুট করার প্রয়োজন হয়, তখন এটি অডিও ফোকাসের অনুরোধ করবে। যখন এটিতে ফোকাস থাকবে, তখন এটি শব্দ বাজাতে পারবে। তবে, অডিও ফোকাস অর্জন করার পরে আপনি প্লে করা শেষ না হওয়া পর্যন্ত এটি ধরে রাখতে পারবেন না। অন্য একটি অ্যাপ ফোকাসের অনুরোধ করতে পারে, যা অডিও ফোকাসের উপর আপনার ধরে রাখার সম্ভাবনা কমিয়ে দেয়। যদি তা হয়, তাহলে আপনার অ্যাপের প্লে বন্ধ করা উচিত অথবা ভলিউম কমিয়ে দেওয়া উচিত যাতে ব্যবহারকারীরা নতুন অডিও উৎসটি আরও সহজে শুনতে পারেন।

অ্যান্ড্রয়েড ১২ (এপিআই লেভেল ৩১) এর আগে, সিস্টেম অডিও ফোকাস পরিচালনা করত না। তাই, অ্যাপ ডেভেলপারদের অডিও ফোকাস নির্দেশিকা মেনে চলতে উৎসাহিত করা হলেও, অ্যান্ড্রয়েড ১১ (এপিআই লেভেল ৩০) বা তার কম ভার্সনে চলমান ডিভাইসে অডিও ফোকাস হারিয়ে যাওয়ার পরেও যদি কোনও অ্যাপ জোরে বাজতে থাকে, তাহলে সিস্টেম তা রোধ করতে পারে না। তবে, এই অ্যাপের আচরণ খারাপ ব্যবহারকারীর অভিজ্ঞতার দিকে পরিচালিত করে এবং প্রায়শই ব্যবহারকারীদের ভুল আচরণকারী অ্যাপটি আনইনস্টল করতে বাধ্য করতে পারে।

একটি সু-নকশিত অডিও অ্যাপের এই সাধারণ নির্দেশিকা অনুসারে অডিও ফোকাস পরিচালনা করা উচিত:

  • প্লে শুরু করার ঠিক আগে requestAudioFocus() কল করুন এবং যাচাই করুন যে কলটি AUDIOFOCUS_REQUEST_GRANTED রিটার্ন করছে কিনা। আপনার মিডিয়া সেশনের onPlay() কলব্যাকে requestAudioFocus() কলটি করুন।

  • যখন অন্য কোনও অ্যাপ অডিও ফোকাস অর্জন করে, তখন বাজানো বন্ধ করুন বা বিরতি দিন, অথবা ভলিউম বন্ধ করুন (অর্থাৎ, কমিয়ে দিন)।

  • যখন প্লেব্যাক বন্ধ হয়ে যায় (উদাহরণস্বরূপ, যখন অ্যাপটিতে চালানোর জন্য কিছুই অবশিষ্ট থাকে না), তখন অডিও ফোকাস ত্যাগ করুন। ব্যবহারকারী যদি প্লেব্যাক থামায় তবে আপনার অ্যাপটিকে অডিও ফোকাস ত্যাগ করতে হবে না তবে পরে প্লেব্যাক পুনরায় শুরু করতে পারে।

  • আপনার অ্যাপটি যে ধরণের অডিও চালাচ্ছে তা বর্ণনা করতে AudioAttributes ব্যবহার করুন। উদাহরণস্বরূপ, যেসব অ্যাপ স্পিচ চালায়, তাদের জন্য CONTENT_TYPE_SPEECH উল্লেখ করুন।

অ্যান্ড্রয়েডের কোন সংস্করণটি চলছে তার উপর নির্ভর করে অডিও ফোকাস ভিন্নভাবে পরিচালিত হয়:

Android 12 (API লেভেল 31) বা তার পরবর্তী ভার্সন
অডিও ফোকাস সিস্টেম দ্বারা পরিচালিত হয়। অন্য কোনও অ্যাপ অডিও ফোকাসের অনুরোধ করলে সিস্টেমটি একটি অ্যাপ থেকে অডিও প্লেব্যাককে জোর করে ম্লান করে দেয়। ইনকামিং কল রিসিভ করলে সিস্টেমটি অডিও প্লেব্যাককেও নিঃশব্দ করে দেয়।
অ্যান্ড্রয়েড ৮.০ (এপিআই লেভেল ২৬) থেকে অ্যান্ড্রয়েড ১১ (এপিআই লেভেল ৩০) পর্যন্ত
অডিও ফোকাস সিস্টেম দ্বারা পরিচালিত হয় না, তবে এতে কিছু পরিবর্তন অন্তর্ভুক্ত রয়েছে যা অ্যান্ড্রয়েড 8.0 (API লেভেল 26) থেকে শুরু হয়েছিল।
অ্যান্ড্রয়েড ৭.১ (এপিআই লেভেল ২৫) এবং তার নিচের ভার্সন
অডিও ফোকাস সিস্টেম দ্বারা পরিচালিত হয় না এবং অ্যাপগুলি requestAudioFocus() এবং abandonAudioFocus() ব্যবহার করে অডিও ফোকাস পরিচালনা করে।

অ্যান্ড্রয়েড ১২ এবং তার পরবর্তী ভার্সনে অডিও ফোকাস

অডিও ফোকাস ব্যবহার করে এমন কোনও মিডিয়া বা গেম অ্যাপ ফোকাস হারিয়ে ফেলার পরে অডিও চালানো উচিত নয়। অ্যান্ড্রয়েড ১২ (এপিআই লেভেল ৩১) এবং তার উচ্চতর সংস্করণে, সিস্টেমটি এই আচরণটি প্রয়োগ করে। যখন কোনও অ্যাপ অন্য অ্যাপের ফোকাস থাকা অবস্থায় অডিও ফোকাসের অনুরোধ করে এবং চলছে, তখন সিস্টেমটি প্লেয়িং অ্যাপটিকে ফেইড আউট করতে বাধ্য করে। ফেইড-আউট যোগ করার ফলে এক অ্যাপ থেকে অন্য অ্যাপে যাওয়ার সময় একটি মসৃণ রূপান্তর ঘটে।

এই ফেইড আউট আচরণটি তখন ঘটে যখন নিম্নলিখিত শর্তগুলি পূরণ হয়:

  1. প্রথম, বর্তমানে চলমান অ্যাপটি এই সমস্ত মানদণ্ড পূরণ করে:

  2. দ্বিতীয় একটি অ্যাপ AudioManager.AUDIOFOCUS_GAIN দিয়ে অডিও ফোকাসের অনুরোধ করে।

এই শর্তগুলি পূরণ হলে, অডিও সিস্টেম প্রথম অ্যাপটিকে ফেইড আউট করে। ফেইড আউটের শেষে, সিস্টেমটি প্রথম অ্যাপটিকে ফোকাস হারিয়ে যাওয়ার বিষয়ে অবহিত করে। অ্যাপটি আবার অডিও ফোকাসের অনুরোধ না করা পর্যন্ত অ্যাপের প্লেয়ারগুলি মিউট থাকে।

বিদ্যমান অডিও ফোকাস আচরণ

অডিও ফোকাসে পরিবর্তনের সাথে জড়িত অন্যান্য ক্ষেত্রেও আপনার সচেতন থাকা উচিত।

স্বয়ংক্রিয় হাঁস

অ্যান্ড্রয়েড ৮.০ (এপিআই লেভেল ২৬) তে অটোমেটিক ডাকিং (একটি অ্যাপের অডিও লেভেল সাময়িকভাবে কমিয়ে আনা যাতে অন্য অ্যাপটি স্পষ্টভাবে শোনা যায়) চালু করা হয়েছিল।

সিস্টেমটি ডাকিং বাস্তবায়ন করার মাধ্যমে, আপনার অ্যাপে ডাকিং বাস্তবায়ন করতে হবে না।

যখন কোনও অডিও বিজ্ঞপ্তি কোনও প্লেয়িং অ্যাপ থেকে ফোকাস দখল করে তখনও স্বয়ংক্রিয় ডাকিং ঘটে। বিজ্ঞপ্তি প্লেব্যাকের শুরুটি ডাকিং র‍্যাম্পের শেষের সাথে সিঙ্ক্রোনাইজ করা হয়।

নিম্নলিখিত শর্তগুলি পূরণ হলে স্বয়ংক্রিয় ডাকিং ঘটে:

  1. প্রথম, বর্তমানে চলমান অ্যাপটি এই সমস্ত মানদণ্ড পূরণ করে:

    • অ্যাপটি যেকোনো ধরণের ফোকাস লাভের সাথে অডিও ফোকাসের জন্য সফলভাবে অনুরোধ করেছে।
    • অ্যাপটি AudioAttributes.CONTENT_TYPE_SPEECH ধরণের কন্টেন্ট সহ অডিও চালাচ্ছে না।
    • অ্যাপটি AudioFocusRequest.Builder.setWillPauseWhenDucked(true) সেট করেনি।
  2. দ্বিতীয় একটি অ্যাপ AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK দিয়ে অডিও ফোকাসের অনুরোধ করে।

যখন এই শর্তগুলি পূরণ করা হয়, তখন অডিও সিস্টেম প্রথম অ্যাপের সমস্ত সক্রিয় প্লেয়ারগুলিকে বন্ধ করে দেয় যখন দ্বিতীয় অ্যাপটিতে ফোকাস থাকে। যখন দ্বিতীয় অ্যাপটি ফোকাস ত্যাগ করে, তখন এটি তাদের বন্ধ করে দেয়। যখন প্রথম অ্যাপটি ফোকাস হারিয়ে ফেলে তখন তাকে জানানো হয় না, তাই এটিকে কিছু করতে হয় না।

মনে রাখবেন যে ব্যবহারকারী যখন বক্তৃতা শুনছেন তখন স্বয়ংক্রিয় ডাকিং করা হয় না, কারণ ব্যবহারকারী প্রোগ্রামের কিছু অংশ মিস করতে পারেন। উদাহরণস্বরূপ, ড্রাইভিং দিকনির্দেশের জন্য ভয়েস নির্দেশিকা ডাক করা হয় না।

ইনকামিং ফোন কলের জন্য বর্তমান অডিও প্লেব্যাক মিউট করুন

কিছু অ্যাপ সঠিকভাবে আচরণ করে না এবং ফোন কলের সময় অডিও বাজতে থাকে। এই পরিস্থিতির কারণে ব্যবহারকারীকে তাদের কল শুনতে আপত্তিকর অ্যাপটি খুঁজে বের করে মিউট করতে হয় অথবা বন্ধ করতে হয়। এটি প্রতিরোধ করার জন্য, ইনকামিং কল থাকাকালীন সিস্টেমটি অন্যান্য অ্যাপ থেকে অডিও মিউট করতে পারে। যখন কোনও ইনকামিং ফোন কল রিসিভ করা হয় এবং কোনও অ্যাপ এই শর্তগুলি পূরণ করে তখন সিস্টেমটি এই বৈশিষ্ট্যটি ব্যবহার করে:

  • অ্যাপটিতে AudioAttributes.USAGE_MEDIA অথবা AudioAttributes.USAGE_GAME ব্যবহারের বৈশিষ্ট্য রয়েছে।
  • অ্যাপটি সফলভাবে অডিও ফোকাস (যেকোন ফোকাস লাভ) অনুরোধ করেছে এবং অডিও বাজছে।

যদি কোনও অ্যাপ কল চলাকালীন চলতে থাকে, তাহলে কল শেষ না হওয়া পর্যন্ত তার প্লেব্যাক মিউট করা থাকে। তবে, যদি কোনও অ্যাপ কল চলাকালীন বাজতে শুরু করে, তাহলে ব্যবহারকারী ইচ্ছাকৃতভাবে প্লেব্যাক শুরু করেছেন এই ধারণায় প্লেয়ারটি মিউট করা হয় না।

অ্যান্ড্রয়েড ৮.০ থেকে অ্যান্ড্রয়েড ১১ পর্যন্ত অডিও ফোকাস

Android 8.0 (API লেভেল 26) থেকে শুরু করে, যখন আপনি requestAudioFocus() কল করবেন তখন আপনাকে একটি AudioFocusRequest প্যারামিটার সরবরাহ করতে হবে। AudioFocusRequest আপনার অ্যাপের অডিও প্রসঙ্গ এবং ক্ষমতা সম্পর্কে তথ্য থাকে। সিস্টেমটি স্বয়ংক্রিয়ভাবে অডিও ফোকাসের লাভ এবং ক্ষতি পরিচালনা করতে এই তথ্য ব্যবহার করে। অডিও ফোকাস প্রকাশ করতে, abandonAudioFocusRequest() পদ্ধতিটি কল করুন যা একটি AudioFocusRequest কেও তার আর্গুমেন্ট হিসাবে গ্রহণ করে। ফোকাস অনুরোধ এবং পরিত্যাগ করার সময় একই AudioFocusRequest উদাহরণ ব্যবহার করুন।

একটি AudioFocusRequest তৈরি করতে, একটি AudioFocusRequest.Builder ব্যবহার করুন। যেহেতু একটি ফোকাস অনুরোধে সর্বদা অনুরোধের ধরণ নির্দিষ্ট করতে হবে, তাই বিল্ডারের জন্য কনস্ট্রাক্টরে টাইপটি অন্তর্ভুক্ত করা হয়। অনুরোধের অন্যান্য ক্ষেত্রগুলি সেট করতে বিল্ডারের পদ্ধতিগুলি ব্যবহার করুন।

FocusGain ক্ষেত্রটি আবশ্যক; অন্যান্য সমস্ত ক্ষেত্র ঐচ্ছিক।

পদ্ধতি মন্তব্য
setFocusGain() প্রতিটি অনুরোধের ক্ষেত্রে এই ক্ষেত্রটি আবশ্যক। এটি পূর্ব-Android 8.0 কলে ব্যবহৃত durationHint মানগুলির সমান মান গ্রহণ করে requestAudioFocus() : AUDIOFOCUS_GAIN , AUDIOFOCUS_GAIN_TRANSIENT , AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK , অথবা AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
setAudioAttributes() AudioAttributes আপনার অ্যাপের ব্যবহারের ধরণ বর্ণনা করে। যখন কোনও অ্যাপ অডিও ফোকাস বাড়ায় বা হারায় তখন সিস্টেমটি সেগুলি দেখে। অ্যাট্রিবিউটগুলি স্ট্রিম টাইপের ধারণাকে ছাড়িয়ে যায়। অ্যান্ড্রয়েড 8.0 (API লেভেল 26) এবং পরবর্তী সংস্করণগুলিতে, ভলিউম নিয়ন্ত্রণ ব্যতীত অন্য যেকোনো অপারেশনের জন্য স্ট্রিম টাইপগুলি অবচিত করা হয়েছে। আপনার অডিও প্লেয়ারে যে ফোকাস অনুরোধ ব্যবহার করেন তাতে একই অ্যাট্রিবিউট ব্যবহার করুন (যেমনটি এই টেবিলের নীচের উদাহরণে দেখানো হয়েছে)।

প্রথমে অ্যাট্রিবিউটগুলি নির্দিষ্ট করতে একটি AudioAttributes.Builder ব্যবহার করুন, তারপর অনুরোধে অ্যাট্রিবিউটগুলি বরাদ্দ করতে এই পদ্ধতিটি ব্যবহার করুন।

যদি নির্দিষ্ট না করা থাকে, তাহলে AudioAttributes ডিফল্টভাবে AudioAttributes.USAGE_MEDIA তে সেট করা হয়।

setWillPauseWhenDucked() যখন অন্য কোনও অ্যাপ AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK দিয়ে ফোকাসের অনুরোধ করে, তখন ফোকাসযুক্ত অ্যাপটি সাধারণত onAudioFocusChange() কলব্যাক পায় না কারণ সিস্টেমটি নিজেই ডাকিং করতে পারে। যখন আপনার ভলিউম ডাকিং করার পরিবর্তে প্লেব্যাক থামাতে হয়, setWillPauseWhenDucked(true) কল করুন এবং একটি OnAudioFocusChangeListener তৈরি এবং সেট করুন, যেমনটি অটোমেটিক ডাকিং- এ বর্ণিত হয়েছে।
setAcceptsDelayedFocusGain() অন্য কোনও অ্যাপ দ্বারা ফোকাস লক করা থাকলে অডিও ফোকাসের অনুরোধ ব্যর্থ হতে পারে। এই পদ্ধতিটি বিলম্বিত ফোকাস লাভ সক্ষম করে: যখন এটি উপলব্ধ হয় তখন অ্যাসিঙ্ক্রোনাসভাবে ফোকাস অর্জন করার ক্ষমতা।

মনে রাখবেন যে ডিলেডেড ফোকাস গেইন শুধুমাত্র তখনই কাজ করবে যদি আপনি অডিও অনুরোধে একটি AudioManager.OnAudioFocusChangeListener উল্লেখ করেন, কারণ ফোকাস মঞ্জুর করা হয়েছে তা জানার জন্য আপনার অ্যাপকে কলব্যাক গ্রহণ করতে হবে।

setOnAudioFocusChangeListener() একটি OnAudioFocusChangeListener শুধুমাত্র তখনই প্রয়োজন যখন আপনি অনুরোধে willPauseWhenDucked(true) অথবা setAcceptsDelayedFocusGain(true) উল্লেখ করেন।

লিসেনার সেট করার দুটি পদ্ধতি আছে: একটি হ্যান্ডলার আর্গুমেন্ট সহ এবং অন্যটি হ্যান্ডলার ছাড়াই। হ্যান্ডলার হল সেই থ্রেড যার উপর লিসেনার চলে। যদি আপনি কোনও হ্যান্ডলার নির্দিষ্ট না করেন, তাহলে মূল Looper সাথে যুক্ত হ্যান্ডলারটি ব্যবহার করা হবে।

নিচের উদাহরণে দেখানো হয়েছে কিভাবে AudioFocusRequest.Builder ব্যবহার করে AudioFocusRequest তৈরি করতে হয় এবং অডিও ফোকাস অনুরোধ করতে হয় এবং পরিত্যাগ করতে হয়:

কোটলিন

// initializing variables for audio focus and playback management
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
    setAudioAttributes(AudioAttributes.Builder().run {
        setUsage(AudioAttributes.USAGE_GAME)
        setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        build()
    })
    setAcceptsDelayedFocusGain(true)
    setOnAudioFocusChangeListener(afChangeListener, handler)
    build()
}
val focusLock = Any()

var playbackDelayed = false
var playbackNowAuthorized = false

// requesting audio focus and processing the response
val res = audioManager.requestAudioFocus(focusRequest)
synchronized(focusLock) {
    playbackNowAuthorized = when (res) {
        AudioManager.AUDIOFOCUS_REQUEST_FAILED -> false
        AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
            playbackNow()
            true
        }
        AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> {
            playbackDelayed = true
            false
        }
        else -> false
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
override fun onAudioFocusChange(focusChange: Int) {
    when (focusChange) {
        AudioManager.AUDIOFOCUS_GAIN ->
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false
                    resumeOnFocusGain = false
                }
                playbackNow()
            }
        AudioManager.AUDIOFOCUS_LOSS -> {
            synchronized(focusLock) {
                resumeOnFocusGain = false
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying()
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // ... pausing or ducking depends on your app
        }
    }
}

জাভা

// initializing variables for audio focus and playback management
audioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
playbackAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
        .setAudioAttributes(playbackAttributes)
        .setAcceptsDelayedFocusGain(true)
        .setOnAudioFocusChangeListener(afChangeListener, handler)
        .build();
final Object focusLock = new Object();

boolean playbackDelayed = false;
boolean playbackNowAuthorized = false;

// requesting audio focus and processing the response
int res = audioManager.requestAudioFocus(focusRequest);
synchronized(focusLock) {
    if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
        playbackNowAuthorized = false;
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        playbackNowAuthorized = true;
        playbackNow();
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
        playbackDelayed = true;
        playbackNowAuthorized = false;
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
@Override
public void onAudioFocusChange(int focusChange) {
    switch (focusChange) {
        case AudioManager.AUDIOFOCUS_GAIN:
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false;
                    resumeOnFocusGain = false;
                }
                playbackNow();
            }
            break;
        case AudioManager.AUDIOFOCUS_LOSS:
            synchronized(focusLock) {
                resumeOnFocusGain = false;
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying();
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            // ... pausing or ducking depends on your app
            break;
        }
    }
}

স্বয়ংক্রিয় হাঁস

অ্যান্ড্রয়েড ৮.০ (এপিআই লেভেল ২৬) তে, যখন অন্য কোনও অ্যাপ AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK দিয়ে ফোকাসের অনুরোধ করে, তখন সিস্টেমটি অ্যাপের onAudioFocusChange() কলব্যাক না করেই ভলিউমটি কমিয়ে পুনরুদ্ধার করতে পারে।

যদিও সঙ্গীত এবং ভিডিও প্লেব্যাক অ্যাপের জন্য স্বয়ংক্রিয় ডাকিং গ্রহণযোগ্য আচরণ, তবে অডিও বুক অ্যাপের মতো কথ্য সামগ্রী চালানোর সময় এটি কার্যকর নয়। এই ক্ষেত্রে, অ্যাপটিকে বরং বিরতি দেওয়া উচিত।

যদি আপনি চান যে আপনার অ্যাপটি ভলিউম কমানোর পরিবর্তে ডাক করার জন্য অনুরোধ করা হলে বিরতি দিন, তাহলে OnAudioFocusChangeListener তৈরি করুন একটি onAudioFocusChange() কলব্যাক পদ্ধতি দিয়ে যা পছন্দসই বিরতি/পুনরায় শুরু আচরণ বাস্তবায়ন করে। শ্রোতা নিবন্ধন করতে setOnAudioFocusChangeListener() কল করুন এবং সিস্টেমকে স্বয়ংক্রিয় ডাকিং করার পরিবর্তে আপনার কলব্যাক ব্যবহার করতে বলার জন্য setWillPauseWhenDucked(true) কল করুন।

বিলম্বিত ফোকাস লাভ

কখনও কখনও সিস্টেম অডিও ফোকাসের জন্য অনুরোধ মঞ্জুর করতে পারে না কারণ ফোকাসটি অন্য অ্যাপ দ্বারা "লক" করা থাকে, যেমন একটি ফোন কলের সময়। এই ক্ষেত্রে, requestAudioFocus() AUDIOFOCUS_REQUEST_FAILED প্রদান করে। যখন এটি ঘটে, তখন আপনার অ্যাপটি অডিও প্লেব্যাকের সাথে এগিয়ে যাওয়া উচিত নয় কারণ এটি ফোকাস অর্জন করেনি।

setAcceptsDelayedFocusGain(true) পদ্ধতিটি, যা আপনার অ্যাপকে ফোকাসের জন্য একটি অনুরোধ অ্যাসিঙ্ক্রোনাসভাবে পরিচালনা করতে দেয়। এই ফ্ল্যাগ সেটের সাহায্যে, ফোকাস লক থাকা অবস্থায় করা একটি অনুরোধ AUDIOFOCUS_REQUEST_DELAYED ফেরত পাঠায়। যখন অডিও ফোকাস লক করার শর্তটি আর বিদ্যমান থাকে না, যেমন যখন একটি ফোন কল শেষ হয়, তখন সিস্টেমটি মুলতুবি থাকা ফোকাস অনুরোধটি মঞ্জুর করে এবং আপনার অ্যাপটিকে অবহিত করার জন্য onAudioFocusChange() কল করে।

ফোকাসের বিলম্বিত লাভ পরিচালনা করার জন্য, আপনাকে একটি OnAudioFocusChangeListener তৈরি করতে হবে যার একটি onAudioFocusChange() কলব্যাক পদ্ধতি রয়েছে যা পছন্দসই আচরণ বাস্তবায়ন করে এবং setOnAudioFocusChangeListener() কল করে শ্রোতা নিবন্ধন করে।

অ্যান্ড্রয়েড ৭.১ এবং তার নিচের ভার্সনে অডিও ফোকাস

যখন আপনি requestAudioFocus() কল করবেন তখন আপনাকে একটি সময়কাল ইঙ্গিত নির্দিষ্ট করতে হবে, যা বর্তমানে ফোকাস ধরে রাখা এবং বাজানো অন্য অ্যাপ দ্বারা সম্মানিত হতে পারে:

  • যখন আপনি অদূর ভবিষ্যতের জন্য অডিও চালানোর পরিকল্পনা করেন (উদাহরণস্বরূপ, সঙ্গীত চালানোর সময়) এবং আপনি আশা করেন যে অডিও ফোকাসের পূর্ববর্তী ধারকটি বাজানো বন্ধ করবে, তখন স্থায়ী অডিও ফোকাসের ( AUDIOFOCUS_GAIN ) অনুরোধ করুন।
  • যখন আপনি অল্প সময়ের জন্য অডিও চালানোর আশা করেন এবং পূর্ববর্তী হোল্ডারটি প্লে করা বন্ধ করে দেবে বলে আশা করেন, তখন ক্ষণস্থায়ী ফোকাস ( AUDIOFOCUS_GAIN_TRANSIENT ) অনুরোধ করুন।
  • ( AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ) ব্যবহার করে ট্রানজিয়েন্ট ফোকাস অনুরোধ করুন যাতে বোঝা যায় যে আপনি অল্প সময়ের জন্য অডিও চালাতে চান এবং পূর্ববর্তী ফোকাস মালিক যদি তার অডিও আউটপুট "ডাক" (কম) করে তাহলে তা বাজানো চালিয়ে যেতে পারবেন। উভয় অডিও আউটপুটই অডিও স্ট্রিমে মিশ্রিত হয়। ডাকিং বিশেষ করে এমন অ্যাপগুলির জন্য উপযুক্ত যারা মাঝে মাঝে অডিও স্ট্রিম ব্যবহার করে, যেমন শ্রবণযোগ্য ড্রাইভিং দিকনির্দেশনার জন্য।

requestAudioFocus() পদ্ধতির জন্য একটি AudioManager.OnAudioFocusChangeListener প্রয়োজন। এই শ্রোতাটি আপনার মিডিয়া সেশনের মালিকানাধীন একই কার্যকলাপ বা পরিষেবাতে তৈরি করা উচিত। এটি কলব্যাক onAudioFocusChange() বাস্তবায়ন করে যা আপনার অ্যাপটি অন্য কোনও অ্যাপ অডিও ফোকাস অর্জন করলে বা পরিত্যাগ করলে পায়।

নিম্নলিখিত স্নিপেটটি STREAM_MUSIC স্ট্রীমে স্থায়ী অডিও ফোকাসের অনুরোধ করে এবং অডিও ফোকাসে পরবর্তী পরিবর্তনগুলি পরিচালনা করার জন্য একটি OnAudioFocusChangeListener নিবন্ধন করে। (পরিবর্তন শ্রোতা সম্পর্কে অডিও ফোকাস পরিবর্তনের প্রতিক্রিয়া বিভাগে আলোচনা করা হয়েছে।)

কোটলিন

audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
lateinit var afChangeListener AudioManager.OnAudioFocusChangeListener

...
// Request audio focus for playback
val result: Int = audioManager.requestAudioFocus(
        afChangeListener,
        // Use the music stream.
        AudioManager.STREAM_MUSIC,
        // Request permanent focus.
        AudioManager.AUDIOFOCUS_GAIN
)

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

জাভা

AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener afChangeListener;

...
// Request audio focus for playback
int result = audioManager.requestAudioFocus(afChangeListener,
                             // Use the music stream.
                             AudioManager.STREAM_MUSIC,
                             // Request permanent focus.
                             AudioManager.AUDIOFOCUS_GAIN);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

প্লেব্যাক শেষ হলে, abandonAudioFocus() কল করুন।

কোটলিন

audioManager.abandonAudioFocus(afChangeListener)

জাভা

// Abandon audio focus when playback complete
audioManager.abandonAudioFocus(afChangeListener);

এটি সিস্টেমকে অবহিত করে যে আপনার আর ফোকাসের প্রয়োজন নেই এবং সংশ্লিষ্ট OnAudioFocusChangeListener আনরেজিস্টার করে। যদি আপনি ক্ষণস্থায়ী ফোকাসের অনুরোধ করেন, তাহলে এটি এমন একটি অ্যাপকে অবহিত করবে যা বিরতি দিয়েছে বা বন্ধ করেছে যে এটি প্লে করা চালিয়ে যেতে পারে বা এর ভলিউম পুনরুদ্ধার করতে পারে।

অডিও ফোকাস পরিবর্তনের প্রতিক্রিয়া জানানো

যখন কোন অ্যাপ অডিও ফোকাস অর্জন করে, তখন অন্য অ্যাপ যখন নিজের জন্য অডিও ফোকাসের অনুরোধ করে তখন এটি অবশ্যই এটি প্রকাশ করতে সক্ষম হবে। যখন এটি ঘটে, তখন আপনার অ্যাপটি AudioFocusChangeListeneronAudioFocusChange() পদ্ধতিতে একটি কল পায় যা আপনি অ্যাপটি যখন requestAudioFocus() কল করেছিলেন তখন নির্দিষ্ট করেছিলেন।

onAudioFocusChange() এ পাঠানো focusChange প্যারামিটারটি কী ধরণের পরিবর্তন ঘটছে তা নির্দেশ করে। এটি ফোকাস অর্জনকারী অ্যাপ দ্বারা ব্যবহৃত সময়কালের ইঙ্গিতের সাথে মিলে যায়। আপনার অ্যাপটি যথাযথভাবে প্রতিক্রিয়া জানাবে।

ক্ষণস্থায়ী মনোযোগ হ্রাস
যদি ফোকাস পরিবর্তন ক্ষণস্থায়ী হয় ( AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK অথবা AUDIOFOCUS_LOSS_TRANSIENT ), তাহলে আপনার অ্যাপটি (যদি আপনি স্বয়ংক্রিয় ডাকিংয়ের উপর নির্ভর না করেন) বন্ধ করে দেবে অথবা প্লে করা বন্ধ করবে কিন্তু অন্যথায় একই অবস্থা বজায় রাখবে।

অডিও ফোকাসের ক্ষণস্থায়ী ক্ষতির সময়, আপনার অডিও ফোকাসের পরিবর্তনগুলি পর্যবেক্ষণ করা চালিয়ে যাওয়া উচিত এবং ফোকাস ফিরে পেলে স্বাভাবিক প্লেব্যাক পুনরায় শুরু করার জন্য প্রস্তুত থাকা উচিত। যখন ব্লকিং অ্যাপটি ফোকাস ছেড়ে দেয়, তখন আপনি একটি কলব্যাক পাবেন ( AUDIOFOCUS_GAIN )। এই মুহুর্তে, আপনি ভলিউমকে স্বাভাবিক স্তরে ফিরিয়ে আনতে পারেন অথবা প্লেব্যাক পুনরায় চালু করতে পারেন।

স্থায়ীভাবে মনোযোগ হারানো
যদি অডিও ফোকাস লস স্থায়ী হয় ( AUDIOFOCUS_LOSS ), তাহলে অন্য একটি অ্যাপ অডিও চালাচ্ছে। আপনার অ্যাপের প্লেব্যাক অবিলম্বে থামানো উচিত, কারণ এটি কখনও AUDIOFOCUS_GAIN কলব্যাক পাবে না। প্লেব্যাক পুনরায় চালু করতে, ব্যবহারকারীকে একটি স্পষ্ট পদক্ষেপ নিতে হবে, যেমন একটি বিজ্ঞপ্তি বা অ্যাপ UI-তে প্লে ট্রান্সপোর্ট কন্ট্রোল টিপুন।

নিচের কোড স্নিপেটটি OnAudioFocusChangeListener এবং এর onAudioFocusChange() কলব্যাক কীভাবে বাস্তবায়ন করতে হয় তা দেখায়। অডিও ফোকাস স্থায়ীভাবে হারিয়ে গেলে স্টপ কলব্যাক বিলম্বিত করার জন্য একটি Handler ব্যবহার লক্ষ্য করুন।

কোটলিন

private val handler = Handler()
private val afChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
    when (focusChange) {
        AudioManager.AUDIOFOCUS_LOSS -> {
            // Permanent loss of audio focus
            // Pause playback immediately
            mediaController.transportControls.pause()
            // Wait 30 seconds before stopping playback
            handler.postDelayed(delayedStopRunnable, TimeUnit.SECONDS.toMillis(30))
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            // Pause playback
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // Lower the volume, keep playing
        }
        AudioManager.AUDIOFOCUS_GAIN -> {
            // Your app has been granted audio focus again
            // Raise volume to normal, restart playback if necessary
        }
    }
}

জাভা

private Handler handler = new Handler();
AudioManager.OnAudioFocusChangeListener afChangeListener =
  new AudioManager.OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
      if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
        // Permanent loss of audio focus
        // Pause playback immediately
        mediaController.getTransportControls().pause();
        // Wait 30 seconds before stopping playback
        handler.postDelayed(delayedStopRunnable,
          TimeUnit.SECONDS.toMillis(30));
      }
      else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
        // Pause playback
      } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
        // Lower the volume, keep playing
      } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
        // Your app has been granted audio focus again
        // Raise volume to normal, restart playback if necessary
      }
    }
  };

হ্যান্ডলারটি একটি Runnable ব্যবহার করে যা দেখতে এরকম:

কোটলিন

private var delayedStopRunnable = Runnable {
    mediaController.transportControls.stop()
}

জাভা

private Runnable delayedStopRunnable = new Runnable() {
    @Override
    public void run() {
        getMediaController().getTransportControls().stop();
    }
};

ব্যবহারকারী প্লেব্যাক পুনরায় চালু করলে বিলম্বিত স্টপ শুরু না হয় তা নিশ্চিত করার জন্য, যেকোনো অবস্থা পরিবর্তনের প্রতিক্রিয়া হিসেবে mHandler.removeCallbacks(mDelayedStopRunnable) এ কল করুন। উদাহরণস্বরূপ, আপনার কলব্যাকের onPlay() , onSkipToNext() ইত্যাদিতে removeCallbacks() এ কল করুন। আপনার পরিষেবা দ্বারা ব্যবহৃত রিসোর্সগুলি পরিষ্কার করার সময় আপনার পরিষেবার onDestroy() কলব্যাকেও এই পদ্ধতিটি কল করা উচিত।