এই ডেভেলপার গাইডে বর্ণনা করা হয়েছে, কীভাবে অ্যান্ড্রয়েড সেন্ডার এসডিকে ব্যবহার করে আপনার অ্যান্ড্রয়েড সেন্ডার অ্যাপে গুগল কাস্ট সাপোর্ট যোগ করতে হয়।
মোবাইল ডিভাইস বা ল্যাপটপটি হলো প্রেরক , যা প্লেব্যাক নিয়ন্ত্রণ করে, এবং গুগল কাস্ট ডিভাইসটি হলো গ্রাহক , যা টিভিতে বিষয়বস্তু প্রদর্শন করে।
প্রেরক ফ্রেমওয়ার্ক বলতে প্রেরকের উপর রানটাইমে উপস্থিত Cast ক্লাস লাইব্রেরি বাইনারি এবং সংশ্লিষ্ট রিসোর্সসমূহকে বোঝায়। প্রেরক অ্যাপ বা Cast অ্যাপ বলতে প্রেরকের উপর চলমান একটি অ্যাপকে বোঝায়। ওয়েব রিসিভার অ্যাপ বলতে Cast-সক্ষম ডিভাইসে চলমান HTML অ্যাপ্লিকেশনটিকে বোঝায়।
প্রেরক ফ্রেমওয়ার্কটি প্রেরক অ্যাপকে ইভেন্ট সম্পর্কে অবহিত করতে এবং কাস্ট অ্যাপের জীবনচক্রের বিভিন্ন অবস্থার মধ্যে রূপান্তর ঘটাতে একটি অ্যাসিঙ্ক্রোনাস কলব্যাক ডিজাইন ব্যবহার করে।
অ্যাপ প্রবাহ
নিম্নলিখিত ধাপগুলো একটি প্রেরক অ্যান্ড্রয়েড অ্যাপের সাধারণ উচ্চ-স্তরের কার্যপ্রবাহ বর্ণনা করে:
- Cast ফ্রেমওয়ার্কটি
Activityলাইফসাইকেলের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবেMediaRouterডিভাইস ডিসকভারি শুরু করে। - যখন ব্যবহারকারী কাস্ট বোতামে ক্লিক করেন, তখন ফ্রেমওয়ার্কটি আবিষ্কৃত কাস্ট ডিভাইসগুলির তালিকা সহ কাস্ট ডায়ালগটি প্রদর্শন করে।
- যখন ব্যবহারকারী একটি কাস্ট ডিভাইস নির্বাচন করেন, তখন ফ্রেমওয়ার্কটি সেই কাস্ট ডিভাইসে ওয়েব রিসিভার অ্যাপটি চালু করার চেষ্টা করে।
- ওয়েব রিসিভার অ্যাপটি চালু হয়েছে কিনা, তা নিশ্চিত করার জন্য ফ্রেমওয়ার্কটি প্রেরক অ্যাপে কলব্যাক আহ্বান করে।
- এই ফ্রেমওয়ার্কটি প্রেরক এবং ওয়েব রিসিভার অ্যাপগুলোর মধ্যে একটি যোগাযোগ চ্যানেল তৈরি করে।
- ফ্রেমওয়ার্কটি ওয়েব রিসিভারে মিডিয়া প্লেব্যাক লোড ও নিয়ন্ত্রণ করতে কমিউনিকেশন চ্যানেলটি ব্যবহার করে।
- ফ্রেমওয়ার্কটি প্রেরক এবং ওয়েব রিসিভারের মধ্যে মিডিয়া প্লেব্যাকের অবস্থা সিঙ্ক্রোনাইজ করে: যখন ব্যবহারকারী প্রেরক UI-তে কোনো কাজ করেন, ফ্রেমওয়ার্কটি সেই মিডিয়া নিয়ন্ত্রণের অনুরোধগুলো ওয়েব রিসিভারের কাছে পাঠিয়ে দেয়, এবং যখন ওয়েব রিসিভার মিডিয়ার স্ট্যাটাস আপডেট পাঠায়, তখন ফ্রেমওয়ার্কটি প্রেরক UI-এর অবস্থা আপডেট করে।
- যখন ব্যবহারকারী কাস্ট ডিভাইস থেকে সংযোগ বিচ্ছিন্ন করার জন্য কাস্ট বোতামে ক্লিক করেন, তখন ফ্রেমওয়ার্কটি প্রেরক অ্যাপটিকে ওয়েব রিসিভার থেকে সংযোগ বিচ্ছিন্ন করে দেবে।
Google Cast Android SDK-এর সমস্ত ক্লাস, মেথড এবং ইভেন্টের একটি বিশদ তালিকার জন্য, Android-এর জন্য Google Cast Sender API Reference দেখুন। নিম্নলিখিত বিভাগগুলিতে আপনার Android অ্যাপে Cast যোগ করার ধাপগুলি বর্ণনা করা হয়েছে।
অ্যান্ড্রয়েড ম্যানিফেস্ট কনফিগার করুন
আপনার অ্যাপের AndroidManifest.xml ফাইলে Cast SDK-এর জন্য নিম্নলিখিত উপাদানগুলি কনফিগার করতে হবে:
ব্যবহার-এসডিকে
Cast SDK যে সর্বনিম্ন এবং সর্বোচ্চ অ্যান্ড্রয়েড এপিআই লেভেলগুলো সমর্থন করে, তা নির্ধারণ করুন। বর্তমানে সর্বনিম্ন হলো এপিআই লেভেল ২৩ এবং সর্বোচ্চ হলো এপিআই লেভেল ৩৪।
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
অ্যান্ড্রয়েড:থিম
সর্বনিম্ন অ্যান্ড্রয়েড এসডিকে সংস্করণের উপর ভিত্তি করে আপনার অ্যাপের থিম সেট করুন। উদাহরণস্বরূপ, আপনি যদি নিজের থিম প্রয়োগ না করেন, তবে ললিপপের পূর্ববর্তী কোনো সর্বনিম্ন অ্যান্ড্রয়েড এসডিকে সংস্করণকে লক্ষ্য করার সময় আপনার Theme.AppCompat এর কোনো একটি সংস্করণ ব্যবহার করা উচিত।
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
কাস্ট কনটেক্সট প্রারম্ভিক করুন
ফ্রেমওয়ার্কটির একটি গ্লোবাল সিঙ্গেলটন অবজেক্ট রয়েছে, যার নাম CastContext , যা ফ্রেমওয়ার্কটির সমস্ত মিথস্ক্রিয়া সমন্বয় করে।
CastContext সিঙ্গেলটনটি ইনিশিয়ালাইজ করার জন্য প্রয়োজনীয় অপশন সরবরাহ করতে আপনার অ্যাপকে অবশ্যই OptionsProvider ইন্টারফেসটি ইমপ্লিমেন্ট করতে হবে। OptionsProvider CastOptions এর একটি ইনস্ট্যান্স প্রদান করে, যাতে এমন সব অপশন থাকে যা ফ্রেমওয়ার্কের আচরণকে প্রভাবিত করে। এগুলোর মধ্যে সবচেয়ে গুরুত্বপূর্ণ হলো ওয়েব রিসিভার অ্যাপ্লিকেশন আইডি, যা ডিসকভারি রেজাল্ট ফিল্টার করতে এবং একটি কাস্ট সেশন শুরু হলে ওয়েব রিসিভার অ্যাপটি চালু করতে ব্যবহৃত হয়।
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context): CastOptions { return Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
প্রেরক অ্যাপের AndroidManifest.xml ফাইলে আপনাকে অবশ্যই প্রয়োগকৃত OptionsProvider এর সম্পূর্ণ নামটি একটি মেটাডেটা ফিল্ড হিসেবে ঘোষণা করতে হবে:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
যখন CastContext.getSharedInstance() কল করা হয়, তখন CastContext লেজিলি ইনিশিয়ালাইজ করা হয়।
class MyActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { val castContext = CastContext.getSharedInstance(this) } }
public class MyActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { CastContext castContext = CastContext.getSharedInstance(this); } }
কাস্ট ইউএক্স উইজেট
কাস্ট ফ্রেমওয়ার্কটি কাস্ট ডিজাইন চেকলিস্ট মেনে চলা উইজেটগুলো প্রদান করে:
পরিচিতিমূলক ওভারলে : ফ্রেমওয়ার্কটি একটি কাস্টম ভিউ,
IntroductoryOverlayপ্রদান করে, যা প্রথমবার কোনো রিসিভার উপলব্ধ হলে কাস্ট (Cast) বাটনটির প্রতি ব্যবহারকারীর দৃষ্টি আকর্ষণ করার জন্য দেখানো হয়। প্রেরক অ্যাপটি এই শিরোনামের লেখা এবং এর অবস্থান নিজের মতো করে সাজিয়ে নিতে পারে।কাস্ট বাটন : কাস্ট ডিভাইস উপলব্ধ থাকুক বা না থাকুক, কাস্ট বাটনটি দৃশ্যমান থাকে। ব্যবহারকারী যখন প্রথমবার কাস্ট বাটনে ক্লিক করেন, তখন একটি কাস্ট ডায়ালগ প্রদর্শিত হয়, যেখানে আবিষ্কৃত ডিভাইসগুলোর তালিকা থাকে। ডিভাইসটি সংযুক্ত থাকা অবস্থায় ব্যবহারকারী যখন কাস্ট বাটনে ক্লিক করেন, তখন এটি বর্তমান মিডিয়ার মেটাডেটা (যেমন শিরোনাম, রেকর্ডিং স্টুডিওর নাম এবং একটি থাম্বনেইল ছবি) প্রদর্শন করে অথবা ব্যবহারকারীকে কাস্ট ডিভাইস থেকে সংযোগ বিচ্ছিন্ন করার সুযোগ দেয়। "কাস্ট বাটন"-কে কখনও কখনও "কাস্ট আইকন" নামেও উল্লেখ করা হয়।
মিনি কন্ট্রোলার : যখন ব্যবহারকারী কন্টেন্ট কাস্ট করার সময় বর্তমান কন্টেন্ট পেজ থেকে অন্য কোনো স্ক্রিনে চলে যান বা কন্ট্রোলারটি এক্সপ্যান্ড করে সেন্ডার অ্যাপের অন্য কোনো স্ক্রিনে যান, তখন স্ক্রিনের নিচে মিনি কন্ট্রোলারটি প্রদর্শিত হয়। এর মাধ্যমে ব্যবহারকারী বর্তমানে কাস্ট হওয়া মিডিয়ার মেটাডেটা দেখতে এবং প্লেব্যাক নিয়ন্ত্রণ করতে পারেন।
বর্ধিত কন্ট্রোলার : যখন ব্যবহারকারী কন্টেন্ট কাস্ট করেন, তখন তিনি যদি মিডিয়া নোটিফিকেশন বা মিনি কন্ট্রোলারে ক্লিক করেন, তাহলে বর্ধিত কন্ট্রোলারটি চালু হয়, যা বর্তমানে প্লে হওয়া মিডিয়ার মেটাডেটা প্রদর্শন করে এবং মিডিয়া প্লেব্যাক নিয়ন্ত্রণ করার জন্য কয়েকটি বাটন প্রদান করে।
নোটিফিকেশন : শুধুমাত্র অ্যান্ড্রয়েডের জন্য। যখন ব্যবহারকারী কন্টেন্ট কাস্ট করার সময় প্রেরক অ্যাপ থেকে অন্য অ্যাপে চলে যান, তখন একটি মিডিয়া নোটিফিকেশন প্রদর্শিত হয়, যেখানে বর্তমানে কাস্ট হতে থাকা মিডিয়ার মেটাডেটা এবং প্লেব্যাক কন্ট্রোলগুলো দেখানো হয়।
লক স্ক্রিন : শুধুমাত্র অ্যান্ড্রয়েডের জন্য। যখন ব্যবহারকারী কন্টেন্ট কাস্ট করার সময় লক স্ক্রিনে চলে যান (অথবা ডিভাইসটির টাইম আউট হয়ে যায়), তখন একটি মিডিয়া লক স্ক্রিন কন্ট্রোল প্রদর্শিত হয়, যা বর্তমানে কাস্ট হতে থাকা মিডিয়ার মেটাডেটা এবং প্লেব্যাক কন্ট্রোলগুলো দেখায়।
নিম্নলিখিত নির্দেশিকায় আপনার অ্যাপে এই উইজেটগুলি কীভাবে যুক্ত করবেন তার বর্ণনা রয়েছে।
একটি কাস্ট বাটন যোগ করুন
অ্যান্ড্রয়েড MediaRouter এপিআইগুলো সেকেন্ডারি ডিভাইসে মিডিয়া প্রদর্শন ও প্লেব্যাক সক্ষম করার জন্য ডিজাইন করা হয়েছে। যে অ্যান্ড্রয়েড অ্যাপগুলো MediaRouter এপিআই ব্যবহার করে, সেগুলোর ইউজার ইন্টারফেসের অংশ হিসেবে একটি কাস্ট বাটন থাকা উচিত, যাতে ব্যবহারকারীরা কাস্ট ডিভাইসের মতো কোনো সেকেন্ডারি ডিভাইসে মিডিয়া প্লে করার জন্য একটি মিডিয়া রুট বেছে নিতে পারেন।
এই ফ্রেমওয়ার্কটি একটি MediaRouteButton Cast button হিসেবে যুক্ত করা অত্যন্ত সহজ করে তোলে। প্রথমে আপনাকে আপনার মেনু নির্ধারণকারী xml ফাইলে একটি মেনু আইটেম বা একটি MediaRouteButton যোগ করতে হবে এবং ফ্রেমওয়ার্কের সাথে এটিকে সংযুক্ত করতে CastButtonFactory ব্যবহার করতে হবে।
// To add a Cast button, add the following snippet.
// menu.xml
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always" />
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.kt override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.main, menu) CastButtonFactory.setUpMediaRouteButton( applicationContext, menu, R.id.media_route_menu_item ) return true }
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.java @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.main, menu); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); return true; }
এরপর, যদি আপনার Activity FragmentActivity থেকে ইনহেরিট করে, তাহলে আপনি আপনার লেআউটে একটি MediaRouteButton যোগ করতে পারেন।
// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<androidx.mediarouter.app.MediaRouteButton
android:id="@+id/media_route_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:mediaRouteTypes="user"
android:visibility="gone" />
</LinearLayout>
// MyActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_layout) mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton) mCastContext = CastContext.getSharedInstance(this) }
// MyActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_layout); mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton); mCastContext = CastContext.getSharedInstance(this); }
থিম ব্যবহার করে কাস্ট বাটনের চেহারা নির্ধারণ করতে, ‘কাস্ট বাটন কাস্টমাইজ করুন’ দেখুন।
ডিভাইস আবিষ্কার কনফিগার করুন
ডিভাইস ডিসকভারি সম্পূর্ণরূপে CastContext দ্বারা পরিচালিত হয়। CastContext ইনিশিয়ালাইজ করার সময়, প্রেরক অ্যাপটি ওয়েব রিসিভার অ্যাপ্লিকেশন আইডি নির্দিষ্ট করে, এবং ঐচ্ছিকভাবে CastOptions এ supportedNamespaces সেট করে নেমস্পেস ফিল্টারিংয়ের জন্য অনুরোধ করতে পারে। CastContext অভ্যন্তরীণভাবে MediaRouter এর একটি রেফারেন্স ধারণ করে, এবং নিম্নলিখিত শর্তগুলির অধীনে ডিসকভারি প্রক্রিয়া শুরু করবে:
- ডিভাইস শনাক্তকরণের বিলম্ব এবং ব্যাটারি ব্যবহারের মধ্যে ভারসাম্য রক্ষার জন্য ডিজাইন করা একটি অ্যালগরিদমের উপর ভিত্তি করে, প্রেরক অ্যাপটি ফোরগ্রাউন্ডে এলে মাঝে মাঝে শনাক্তকরণ প্রক্রিয়াটি স্বয়ংক্রিয়ভাবে শুরু হবে।
- কাস্ট ডায়ালগটি খোলা আছে।
- Cast SDK একটি Cast সেশন পুনরুদ্ধার করার চেষ্টা করছে।
কাস্ট ডায়ালগটি বন্ধ করা হলে বা প্রেরক অ্যাপটি ব্যাকগ্রাউন্ডে চলে গেলে ডিসকভারি প্রক্রিয়াটি থেমে যাবে।
class CastOptionsProvider : OptionsProvider { companion object { const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace" } override fun getCastOptions(appContext: Context): CastOptions { val supportedNamespaces: MutableList<String> = ArrayList() supportedNamespaces.add(CUSTOM_NAMESPACE) return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
class CastOptionsProvider implements OptionsProvider { public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"; @Override public CastOptions getCastOptions(Context appContext) { List<String> supportedNamespaces = new ArrayList<>(); supportedNamespaces.add(CUSTOM_NAMESPACE); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
সেশন ম্যানেজমেন্ট কীভাবে কাজ করে
কাস্ট এসডিকে (Cast SDK) কাস্ট সেশনের ধারণাটি প্রবর্তন করেছে, যা স্থাপন করার জন্য একটি ডিভাইসের সাথে সংযোগ স্থাপন, একটি ওয়েব রিসিভার অ্যাপ চালু করা (বা তাতে যোগদান করা), সেই অ্যাপের সাথে সংযোগ স্থাপন এবং একটি মিডিয়া কন্ট্রোল চ্যানেল ইনিশিয়ালাইজ করার ধাপগুলো একত্রিত করা হয়। কাস্ট সেশন এবং ওয়েব রিসিভারের জীবনচক্র সম্পর্কে আরও তথ্যের জন্য ওয়েব রিসিভার অ্যাপ্লিকেশন জীবনচক্র নির্দেশিকাটি দেখুন।
সেশনগুলি SessionManager ক্লাস দ্বারা পরিচালিত হয়, যা আপনার অ্যাপ CastContext.getSessionManager() এর মাধ্যমে অ্যাক্সেস করতে পারে। স্বতন্ত্র সেশনগুলি Session ক্লাসের সাবক্লাস দ্বারা প্রতিনিধিত্ব করা হয়। উদাহরণস্বরূপ, CastSession কাস্ট ডিভাইসগুলির সাথে সেশনগুলিকে প্রতিনিধিত্ব করে। আপনার অ্যাপ SessionManager.getCurrentCastSession() এর মাধ্যমে বর্তমানে সক্রিয় কাস্ট সেশনটি অ্যাক্সেস করতে পারে।
আপনার অ্যাপ সেশন ইভেন্ট, যেমন—সেশন তৈরি, স্থগিতকরণ, পুনরায় চালু এবং সমাপ্তি নিরীক্ষণ করতে SessionManagerListener ক্লাসটি ব্যবহার করতে পারে। সেশন সক্রিয় থাকা অবস্থায় কোনো অস্বাভাবিক বা আকস্মিক সমাপ্তি ঘটলে ফ্রেমওয়ার্কটি স্বয়ংক্রিয়ভাবে তা পুনরায় চালু করার চেষ্টা করে।
MediaRouter ডায়ালগ থেকে ব্যবহারকারীর অঙ্গভঙ্গির প্রতিক্রিয়ায় সেশনগুলো স্বয়ংক্রিয়ভাবে তৈরি ও বন্ধ হয়ে যায়।
কাস্ট শুরু হওয়ার ত্রুটিগুলো আরও ভালোভাবে বোঝার জন্য, অ্যাপগুলো সেশন শুরুর ত্রুটিকে CastReasonCodes এ রূপান্তর করতে CastContext#getCastReasonCodeForCastStatusCode(int) ব্যবহার করতে পারে। অনুগ্রহ করে মনে রাখবেন যে কিছু সেশন শুরুর ত্রুটি (যেমন CastReasonCodes#CAST_CANCELLED ) স্বাভাবিক আচরণ এবং এগুলোকে ত্রুটি হিসেবে লগ করা উচিত নয়।
সেশনের অবস্থার পরিবর্তন সম্পর্কে অবগত থাকার প্রয়োজন হলে, আপনি একটি SessionManagerListener প্রয়োগ করতে পারেন। এই উদাহরণটি একটি Activity তে CastSession এর প্রাপ্যতা পর্যবেক্ষণ করে।
class MyActivity : Activity() { private var mCastSession: CastSession? = null private lateinit var mCastContext: CastContext private lateinit var mSessionManager: SessionManager private val mSessionManagerListener: SessionManagerListener<CastSession> = SessionManagerListenerImpl() private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> { override fun onSessionStarting(session: CastSession?) {} override fun onSessionStarted(session: CastSession?, sessionId: String) { invalidateOptionsMenu() } override fun onSessionStartFailed(session: CastSession?, error: Int) { val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error) // Handle error } override fun onSessionSuspended(session: CastSession?, reason Int) {} override fun onSessionResuming(session: CastSession?, sessionId: String) {} override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) { invalidateOptionsMenu() } override fun onSessionResumeFailed(session: CastSession?, error: Int) {} override fun onSessionEnding(session: CastSession?) {} override fun onSessionEnded(session: CastSession?, error: Int) { finish() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mCastContext = CastContext.getSharedInstance(this) mSessionManager = mCastContext.sessionManager mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onResume() { super.onResume() mCastSession = mSessionManager.currentCastSession } override fun onDestroy() { super.onDestroy() mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java) } }
public class MyActivity extends Activity { private CastContext mCastContext; private CastSession mCastSession; private SessionManager mSessionManager; private SessionManagerListener<CastSession> mSessionManagerListener = new SessionManagerListenerImpl(); private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> { @Override public void onSessionStarting(CastSession session) {} @Override public void onSessionStarted(CastSession session, String sessionId) { invalidateOptionsMenu(); } @Override public void onSessionStartFailed(CastSession session, int error) { int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error); // Handle error } @Override public void onSessionSuspended(CastSession session, int reason) {} @Override public void onSessionResuming(CastSession session, String sessionId) {} @Override public void onSessionResumed(CastSession session, boolean wasSuspended) { invalidateOptionsMenu(); } @Override public void onSessionResumeFailed(CastSession session, int error) {} @Override public void onSessionEnding(CastSession session) {} @Override public void onSessionEnded(CastSession session, int error) { finish(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCastContext = CastContext.getSharedInstance(this); mSessionManager = mCastContext.getSessionManager(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onResume() { super.onResume(); mCastSession = mSessionManager.getCurrentCastSession(); } @Override protected void onDestroy() { super.onDestroy(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); } }
প্রবাহ স্থানান্তর
সেশন স্টেট সংরক্ষণ করাই হলো স্ট্রিম ট্রান্সফারের ভিত্তি, যার মাধ্যমে ব্যবহারকারীরা ভয়েস কমান্ড, গুগল হোম অ্যাপ বা স্মার্ট ডিসপ্লে ব্যবহার করে ডিভাইসগুলোর মধ্যে বিদ্যমান অডিও এবং ভিডিও স্ট্রিম স্থানান্তর করতে পারেন। একটি ডিভাইসে (সোর্স) মিডিয়া প্লে হওয়া বন্ধ হয়ে যায় এবং অন্যটিতে (ডেস্টিনেশন) তা আবার চলতে থাকে। সর্বশেষ ফার্মওয়্যারযুক্ত যেকোনো কাস্ট ডিভাইস স্ট্রিম ট্রান্সফারের ক্ষেত্রে সোর্স বা ডেস্টিনেশন হিসেবে কাজ করতে পারে।
স্ট্রিম ট্রান্সফার বা এক্সপ্যানশনের সময় নতুন গন্তব্য ডিভাইস পেতে, CastSession#addCastListener ব্যবহার করে একটি Cast.Listener রেজিস্টার করুন। তারপর onDeviceNameChanged কলব্যাকের সময় CastSession#getCastDevice() কল করুন।
আরও তথ্যের জন্য ওয়েব রিসিভারে স্ট্রিম ট্রান্সফার দেখুন।
স্বয়ংক্রিয় পুনঃসংযোগ
এই ফ্রেমওয়ার্কটি একটি ReconnectionService প্রদান করে, যা প্রেরক অ্যাপ দ্বারা সক্রিয় করা যেতে পারে অনেক সূক্ষ্ম ব্যতিক্রমী ক্ষেত্রে পুনঃসংযোগ পরিচালনা করার জন্য, যেমন:
- সাময়িক ওয়াইফাই সংযোগ বিচ্ছিন্ন হলে তা থেকে পুনরুদ্ধার করুন।
- ডিভাইস স্লিপ থেকে পুনরুদ্ধার করুন
- অ্যাপটিকে ব্যাকগ্রাউন্ড থেকে পুনরুদ্ধার করুন
- অ্যাপটি ক্র্যাশ করলে পুনরুদ্ধার করুন
এই পরিষেবাটি ডিফল্টরূপে চালু থাকে এবং CastOptions.Builder এ এটি বন্ধ করা যেতে পারে।
আপনার গ্রেডল ফাইলে অটো-মার্জ চালু করা থাকলে, এই সার্ভিসটি আপনার অ্যাপের ম্যানিফেস্টে স্বয়ংক্রিয়ভাবে যুক্ত হয়ে যেতে পারে।
ফ্রেমওয়ার্কটি মিডিয়া সেশন চলাকালীন সার্ভিসটি চালু করবে এবং সেশন শেষ হলে তা বন্ধ করে দেবে।
মিডিয়া নিয়ন্ত্রণ কীভাবে কাজ করে
Cast ফ্রেমওয়ার্কটি Cast 2.x-এর RemoteMediaPlayer ক্লাসটিকে বাতিল করে RemoteMediaClient নামে একটি নতুন ক্লাস চালু করেছে, যা আরও সুবিধাজনক কিছু API-এর মাধ্যমে একই কার্যকারিতা প্রদান করে এবং GoogleApiClient পাস করার প্রয়োজনীয়তা দূর করে।
যখন আপনার অ্যাপ মিডিয়া নেমস্পেস সমর্থন করে এমন কোনো ওয়েব রিসিভার অ্যাপের সাথে একটি CastSession স্থাপন করে, তখন ফ্রেমওয়ার্ক দ্বারা স্বয়ংক্রিয়ভাবে RemoteMediaClient এর একটি ইনস্ট্যান্স তৈরি হয়ে যায়; আপনার অ্যাপ CastSession ইনস্ট্যান্সটির getRemoteMediaClient() মেথড কল করার মাধ্যমে এটি অ্যাক্সেস করতে পারে।
RemoteMediaClient এর যে সকল মেথড ওয়েব রিসিভারে অনুরোধ পাঠায়, সেগুলো একটি PendingResult অবজেক্ট রিটার্ন করবে যা সেই অনুরোধটি ট্র্যাক করতে ব্যবহার করা যেতে পারে।
আশা করা যায় যে, RemoteMediaClient এর ইনস্ট্যান্সটি আপনার অ্যাপের একাধিক অংশ এবং ফ্রেমওয়ার্কের কিছু অভ্যন্তরীণ কম্পোনেন্ট, যেমন—পার্সিস্টেন্ট মিনি কন্ট্রোলার ও নোটিফিকেশন সার্ভিস দ্বারা ব্যবহৃত হতে পারে। সেই উদ্দেশ্যে, এই ইনস্ট্যান্সটি RemoteMediaClient.Listener এর একাধিক ইনস্ট্যান্স নিবন্ধন সমর্থন করে।
মিডিয়া মেটাডেটা সেট করুন
MediaMetadata ক্লাসটি আপনি কাস্ট করতে চান এমন একটি মিডিয়া আইটেম সম্পর্কিত তথ্য উপস্থাপন করে। নিম্নলিখিত উদাহরণটি একটি মুভির নতুন MediaMetadata ইনস্ট্যান্স তৈরি করে এবং এর টাইটেল, সাবটাইটেল ও দুটি ছবি সেট করে।
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()) movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0)))) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0)))); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));
মিডিয়া মেটাডেটার সাথে ছবির ব্যবহার সম্পর্কে জানতে ইমেজ সিলেকশন দেখুন।
মিডিয়া লোড করুন
আপনার অ্যাপ একটি মিডিয়া আইটেম লোড করতে পারে, যেমনটি নিম্নলিখিত কোডে দেখানো হয়েছে। প্রথমে মিডিয়ার মেটাডেটা সহ MediaInfo.Builder ব্যবহার করে একটি MediaInfo ইনস্ট্যান্স তৈরি করুন। বর্তমান CastSession থেকে RemoteMediaClient টি নিন, তারপর সেই RemoteMediaClient এ MediaInfo লোড করুন। Web Receiver-এ চলমান একটি মিডিয়া প্লেয়ার অ্যাপ প্লে, পজ এবং অন্যভাবে নিয়ন্ত্রণ করতে RemoteMediaClient ব্যবহার করুন।
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build() val remoteMediaClient = mCastSession.getRemoteMediaClient() remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build(); RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());
মিডিয়া ট্র্যাক ব্যবহার সংক্রান্ত অংশটিও দেখুন।
4K ভিডিও ফরম্যাট
আপনার মিডিয়া কোন ভিডিও ফরম্যাটের, তা জানতে MediaStatus-এ getVideoInfo() ব্যবহার করে VideoInfo এর বর্তমান ইনস্ট্যান্সটি নিন। এই ইনস্ট্যান্সটিতে HDR টিভি ফরম্যাটের ধরন এবং পিক্সেলে প্রদর্শিত উচ্চতা ও প্রস্থ থাকে। 4K ফরম্যাটের বিভিন্ন প্রকারভেদ HDR_TYPE_* কনস্ট্যান্ট দ্বারা নির্দেশিত হয়।
একাধিক ডিভাইসে রিমোট কন্ট্রোল নোটিফিকেশন
যখন কোনো ব্যবহারকারী কাস্টিং করেন, তখন একই নেটওয়ার্কে থাকা অন্যান্য অ্যান্ড্রয়েড ডিভাইসগুলোও একটি নোটিফিকেশন পাবে, যা তাদেরও প্লেব্যাক নিয়ন্ত্রণ করার সুযোগ দেবে। যাদের ডিভাইসে এই ধরনের নোটিফিকেশন আসে, তারা সেটিংস অ্যাপে Google > Google Cast > Show remote control notifications- এ গিয়ে সেই ডিভাইসের জন্য এটি বন্ধ করতে পারেন। (নোটিফিকেশনগুলোতে সেটিংস অ্যাপের একটি শর্টকাট অন্তর্ভুক্ত থাকে।) আরও বিস্তারিত জানতে, Cast remote control notifications দেখুন।
মিনি কন্ট্রোলার যোগ করুন
কাস্ট ডিজাইন চেকলিস্ট অনুসারে, একটি প্রেরক অ্যাপে মিনি কন্ট্রোলার নামে একটি স্থায়ী কন্ট্রোল থাকা উচিত, যা ব্যবহারকারী বর্তমান কন্টেন্ট পেজ থেকে প্রেরক অ্যাপের অন্য কোনো অংশে গেলে প্রদর্শিত হবে। মিনি কন্ট্রোলারটি ব্যবহারকারীকে বর্তমান কাস্ট সেশনের একটি দৃশ্যমান অনুস্মারক প্রদান করে। মিনি কন্ট্রোলারে ট্যাপ করে, ব্যবহারকারী কাস্টের পূর্ণ-স্ক্রিনের প্রসারিত কন্ট্রোলার ভিউতে ফিরে যেতে পারেন।
এই ফ্রেমওয়ার্কটি MiniControllerFragment নামে একটি কাস্টম ভিউ প্রদান করে, যা আপনি প্রতিটি অ্যাক্টিভিটির লেআউট ফাইলের শেষে যোগ করতে পারেন, যেখানে আপনি মিনি কন্ট্রোলারটি দেখাতে চান।
<fragment
android:id="@+id/castMiniController"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
যখন আপনার প্রেরক অ্যাপ কোনো ভিডিও বা অডিও লাইভ স্ট্রিম চালায়, তখন এসডিকে স্বয়ংক্রিয়ভাবে মিনি কন্ট্রোলারের প্লে/পজ বাটনের পরিবর্তে একটি প্লে/স্টপ বাটন প্রদর্শন করে।
এই কাস্টম ভিউয়ের শিরোনাম এবং উপ-শিরোনামের লেখার ধরন নির্ধারণ করতে এবং বাটন নির্বাচন করতে, ‘কাস্টমাইজ মিনি কন্ট্রোলার’ দেখুন।
বর্ধিত কন্ট্রোলার যোগ করুন
গুগল কাস্ট ডিজাইন চেকলিস্ট অনুযায়ী, প্রেরক অ্যাপকে কাস্ট করা মিডিয়ার জন্য একটি বর্ধিত কন্ট্রোলার প্রদান করতে হয়। এই বর্ধিত কন্ট্রোলারটি হলো মিনি কন্ট্রোলারের একটি ফুল স্ক্রিন সংস্করণ।
Cast SDK এক্সপান্ডেড কন্ট্রোলারের জন্য ExpandedControllerActivity নামে একটি উইজেট প্রদান করে। এটি একটি অ্যাবস্ট্রাক্ট ক্লাস, যাতে একটি Cast বাটন যোগ করার জন্য আপনাকে সাবক্লাস করতে হবে।
প্রথমে, কাস্ট বাটনটি যোগ করার জন্য এক্সপান্ডেড কন্ট্রোলারের জন্য একটি নতুন মেনু রিসোর্স ফাইল তৈরি করুন:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
</menu>
একটি নতুন ক্লাস তৈরি করুন যা ExpandedControllerActivity এক্সটেন্ড করে।
class ExpandedControlsActivity : ExpandedControllerActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.expanded_controller, menu) CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item) return true } }
public class ExpandedControlsActivity extends ExpandedControllerActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.expanded_controller, menu); CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item); return true; } }
এখন application ট্যাগের মধ্যে অ্যাপ ম্যানিফেস্টে আপনার নতুন অ্যাক্টিভিটিটি ঘোষণা করুন:
<application>
...
<activity
android:name=".expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
...
</application>
CastOptionsProvider সম্পাদনা করুন এবং আপনার নতুন অ্যাক্টিভিটিতে টার্গেট অ্যাক্টিভিটি সেট করতে NotificationOptions এবং CastMediaOptions পরিবর্তন করুন:
override fun getCastOptions(context: Context): CastOptions? { val notificationOptions = NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity::class.java.name) .build() val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name) .build() return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build() }
public CastOptions getCastOptions(Context context) { NotificationOptions notificationOptions = new NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity.class.getName()) .build(); CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName()) .build(); return new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build(); }
রিমোট মিডিয়া লোড হওয়ার সময় আপনার নতুন অ্যাক্টিভিটি প্রদর্শন করার জন্য LocalPlayerActivity loadRemoteMedia মেথডটি আপডেট করুন:
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) { val remoteMediaClient = mCastSession?.remoteMediaClient ?: return remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() { override fun onStatusUpdated() { val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java) startActivity(intent) remoteMediaClient.unregisterCallback(this) } }) remoteMediaClient.load( MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position.toLong()).build() ) }
private void loadRemoteMedia(int position, boolean autoPlay) { if (mCastSession == null) { return; } final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); if (remoteMediaClient == null) { return; } remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() { @Override public void onStatusUpdated() { Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class); startActivity(intent); remoteMediaClient.unregisterCallback(this); } }); remoteMediaClient.load(new MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position).build()); }
যখন আপনার প্রেরক অ্যাপ কোনো ভিডিও বা অডিও লাইভ স্ট্রিম চালায়, তখন SDK স্বয়ংক্রিয়ভাবে এক্সপান্ডেড কন্ট্রোলারের প্লে/পজ বাটনের পরিবর্তে একটি প্লে/স্টপ বাটন প্রদর্শন করে।
থিম ব্যবহার করে চেহারা নির্ধারণ করতে, কোন বাটনগুলো প্রদর্শন করা হবে তা নির্বাচন করতে এবং কাস্টম বাটন যোগ করতে, ‘কাস্টমাইজ এক্সপান্ডেড কন্ট্রোলার’ দেখুন।
ভলিউম নিয়ন্ত্রণ
ফ্রেমওয়ার্কটি প্রেরক অ্যাপের জন্য ভলিউম স্বয়ংক্রিয়ভাবে পরিচালনা করে। ফ্রেমওয়ার্কটি প্রেরক এবং ওয়েব রিসিভার অ্যাপ দুটিকে স্বয়ংক্রিয়ভাবে সিঙ্ক্রোনাইজ করে, যাতে প্রেরক UI সর্বদা ওয়েব রিসিভার দ্বারা নির্দিষ্ট করা ভলিউমটিই প্রদর্শন করে।
ফিজিক্যাল বাটন ভলিউম কন্ট্রোল
অ্যান্ড্রয়েডে, জেলি বিন বা তার পরবর্তী সংস্করণ ব্যবহারকারী যেকোনো ডিভাইসের ক্ষেত্রে, প্রেরক ডিভাইসের ফিজিক্যাল বাটনগুলো ব্যবহার করে ডিফল্টভাবে ওয়েব রিসিভারে কাস্ট সেশনের ভলিউম পরিবর্তন করা যায়।
জেলি বিনের আগে ফিজিক্যাল বাটন ভলিউম কন্ট্রোল
জেলি বিনের চেয়ে পুরোনো অ্যান্ড্রয়েড ডিভাইসগুলিতে ফিজিক্যাল ভলিউম কী ব্যবহার করে ওয়েব রিসিভার ডিভাইসের ভলিউম নিয়ন্ত্রণ করতে হলে, প্রেরক অ্যাপটিকে তাদের অ্যাক্টিভিটিগুলিতে dispatchKeyEvent ওভাররাইড করতে হবে এবং CastContext.onDispatchVolumeKeyEventBeforeJellyBean() কল করতে হবে।
class MyActivity : FragmentActivity() { override fun dispatchKeyEvent(event: KeyEvent): Boolean { return (CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event)) } }
class MyActivity extends FragmentActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { return CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event); } }
নোটিফিকেশন এবং লক স্ক্রিনে মিডিয়া নিয়ন্ত্রণ যোগ করুন
শুধুমাত্র অ্যান্ড্রয়েডের জন্য, গুগল কাস্ট ডিজাইন চেকলিস্ট অনুযায়ী প্রেরক অ্যাপটিকে নোটিফিকেশনে এবং লক স্ক্রিনে মিডিয়া কন্ট্রোল প্রয়োগ করতে হয়, যেখানে প্রেরক কাস্ট করছে কিন্তু অ্যাপটির উপর ফোকাস নেই। ফ্রেমওয়ার্কটি প্রেরক অ্যাপকে নোটিফিকেশনে এবং লক স্ক্রিনে মিডিয়া কন্ট্রোল তৈরি করতে সাহায্য করার জন্য MediaNotificationService এবং MediaIntentReceiver প্রদান করে।
প্রেরক যখন কাস্টিং করেন, তখন MediaNotificationService চালু হয় এবং এটি একটি নোটিফিকেশন দেখায়, যাতে ছবির থাম্বনেইল, বর্তমান কাস্টিং আইটেম সম্পর্কিত তথ্য, একটি প্লে/পজ বাটন এবং একটি স্টপ বাটন থাকে।
MediaIntentReceiver হলো একটি BroadcastReceiver যা নোটিফিকেশন থেকে ব্যবহারকারীর কার্যকলাপ পরিচালনা করে।
আপনার অ্যাপ লক স্ক্রিন থেকে NotificationOptions মাধ্যমে নোটিফিকেশন এবং মিডিয়া নিয়ন্ত্রণ কনফিগার করতে পারে। আপনার অ্যাপ নির্ধারণ করতে পারে যে নোটিফিকেশনে কোন কন্ট্রোল বাটনগুলো দেখানো হবে এবং ব্যবহারকারী নোটিফিকেশনটিতে ট্যাপ করলে কোন Activity খুলবে। যদি অ্যাকশনগুলো স্পষ্টভাবে উল্লেখ না করা হয়, তাহলে ডিফল্ট মান MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK এবং MediaIntentReceiver.ACTION_STOP_CASTING ব্যবহৃত হবে।
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". val buttonActions: MutableList<String> = ArrayList() buttonActions.add(MediaIntentReceiver.ACTION_REWIND) buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK) buttonActions.add(MediaIntentReceiver.ACTION_FORWARD) buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING) // Showing "play/pause" and "stop casting" in the compat view of the notification. val compatButtonActionsIndices = intArrayOf(1, 3) // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. val notificationOptions = NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity::class.java.name) .build()
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". List<String> buttonActions = new ArrayList<>(); buttonActions.add(MediaIntentReceiver.ACTION_REWIND); buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK); buttonActions.add(MediaIntentReceiver.ACTION_FORWARD); buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING); // Showing "play/pause" and "stop casting" in the compat view of the notification. int[] compatButtonActionsIndices = new int[]{1, 3}; // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. NotificationOptions notificationOptions = new NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity.class.getName()) .build();
নোটিফিকেশন এবং লক স্ক্রিন থেকে মিডিয়া কন্ট্রোল দেখানো ডিফল্টরূপে চালু থাকে, এবং CastMediaOptions.Builder এ setNotificationOptions null দিয়ে কল করে এটি নিষ্ক্রিয় করা যায়। বর্তমানে, যতক্ষণ নোটিফিকেশন চালু থাকে, ততক্ষণ লক স্ক্রিন ফিচারটিও চালু থাকে।
// ... continue with the NotificationOptions built above val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build() val castOptions: CastOptions = Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build()
// ... continue with the NotificationOptions built above CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build(); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build();
যখন আপনার প্রেরক অ্যাপ কোনো ভিডিও বা অডিও লাইভ স্ট্রিম চালায়, তখন SDK স্বয়ংক্রিয়ভাবে নোটিফিকেশন কন্ট্রোলের প্লে/পজ বাটনের পরিবর্তে একটি প্লে/স্টপ বাটন প্রদর্শন করে, কিন্তু লক স্ক্রিন কন্ট্রোলে তা করে না।
দ্রষ্টব্য : ললিপপ-পূর্ববর্তী ডিভাইসগুলিতে লক স্ক্রিন কন্ট্রোল প্রদর্শন করার জন্য, RemoteMediaClient স্বয়ংক্রিয়ভাবে আপনার পক্ষ থেকে অডিও ফোকাসের জন্য অনুরোধ করবে।
ত্রুটিগুলি পরিচালনা করুন
প্রেরক অ্যাপগুলোর জন্য সমস্ত ত্রুটি কলব্যাক পরিচালনা করা এবং কাস্ট লাইফ সাইকেলের প্রতিটি পর্যায়ে সর্বোত্তম প্রতিক্রিয়া নির্ধারণ করা অত্যন্ত গুরুত্বপূর্ণ। অ্যাপটি ব্যবহারকারীকে ত্রুটির ডায়ালগ প্রদর্শন করতে পারে অথবা ওয়েব রিসিভারের সাথে সংযোগ বিচ্ছিন্ন করার সিদ্ধান্ত নিতে পারে।