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

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

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

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

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

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

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

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

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

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

Android 12 (API লেভেল 31) বা তার পরে
অডিও ফোকাস সিস্টেম দ্বারা পরিচালিত হয়. যখন অন্য অ্যাপ অডিও ফোকাসের অনুরোধ করে তখন সিস্টেম একটি অ্যাপ থেকে অডিও প্লেব্যাককে বিবর্ণ হতে বাধ্য করে। একটি ইনকামিং কল প্রাপ্ত হলে সিস্টেমটি অডিও প্লেব্যাককে নিঃশব্দ করে।
Android 8.0 (API স্তর 26) Android 11 (API স্তর 30) এর মাধ্যমে
অডিও ফোকাস সিস্টেম দ্বারা পরিচালিত হয় না, তবে কিছু পরিবর্তন অন্তর্ভুক্ত করে যা Android 8.0 (API স্তর 26) থেকে শুরু করা হয়েছিল।
Android 7.1 (API স্তর 25) এবং নিম্ন
অডিও ফোকাস সিস্টেম দ্বারা পরিচালিত হয় না, এবং অ্যাপ্লিকেশনগুলি requestAudioFocus() এবং abandonAudioFocus() ব্যবহার করে অডিও ফোকাস পরিচালনা করে।

Android 12 এবং উচ্চতর সংস্করণে অডিও ফোকাস

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

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

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

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

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

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

অডিও ফোকাসে একটি সুইচ জড়িত এই অন্যান্য ক্ষেত্রেও আপনার সচেতন হওয়া উচিত।

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

স্বয়ংক্রিয় ডাকিং (একটি অ্যাপের অডিও লেভেল সাময়িকভাবে কমিয়ে দেওয়া যাতে অন্যটি স্পষ্টভাবে শোনা যায়) Android 8.0 (API লেভেল 26) এ চালু করা হয়েছিল।

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

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

নিম্নলিখিত শর্ত পূরণ হলে স্বয়ংক্রিয় হাঁস হয়:

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

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

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

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

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

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

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

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

Android 11 এর মাধ্যমে Android 8.0 এ অডিও ফোকাস

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

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

FocusGain ক্ষেত্র প্রয়োজন; অন্য সব ক্ষেত্র ঐচ্ছিক।

পদ্ধতি মন্তব্য
setFocusGain() প্রতিটি অনুরোধে এই ক্ষেত্রটি প্রয়োজন। এটি অনুরোধের জন্য প্রি-অ্যান্ড্রয়েড 8.0 কলে ব্যবহৃত durationHint মতো একই মান নেয় requestAudioFocus() : AUDIOFOCUS_GAIN , AUDIOFOCUS_GAIN_TRANSIENT , AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK , বা AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
setAudioAttributes() AudioAttributes আপনার অ্যাপের ব্যবহারের ক্ষেত্রে বর্ণনা করে। যখন একটি অ্যাপ লাভ করে এবং অডিও ফোকাস হারায় তখন সিস্টেম তাদের দিকে তাকায়। বৈশিষ্ট্যগুলি স্ট্রীম প্রকারের ধারণাকে অগ্রাহ্য করে৷ Android 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;
        }
    }
}

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

অ্যান্ড্রয়েড 8.0 (API লেভেল 26) এ, যখন অন্য একটি অ্যাপ AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK এর সাথে ফোকাস করার অনুরোধ করে, তখন সিস্টেমটি অ্যাপের onAudioFocusChange() কলব্যাক না করেই ভলিউম পুনরুদ্ধার করতে পারে।

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

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

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

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

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

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

অ্যান্ড্রয়েড 7.1 এবং তার নিচের সংস্করণে অডিও ফোকাস

যখন আপনি 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() কলব্যাকেও এই পদ্ধতিটি কল করা উচিত৷