চ্যানেল ডেটা নিয়ে কাজ করা

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

TV ইনপুট পরিষেবা স্যাম্পেল অ্যাপ ব্যবহার করে দেখুন।

অনুমতি নিন

EPG ডেটার সাথে আপনার টিভি ইনপুট কাজ করার জন্য, এটিকে অবশ্যই নিজের Android ম্যানিফেস্ট ফাইলে নিম্নলিখিত রাইট অনুমতি ঘোষণা করতে হবে:

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DA>TA" /

ডেটাবেসে চ্যানেল রেজিস্টার করা

Android TV সিস্টেম ডেটাবেস, টিভি ইনপুটের জন্য চ্যানেল ডেটার রেকর্ড বজায় রাখে। আপনার সেট-আপ অ্যাক্টিভিটিতে, আপনার প্রতিটি চ্যানেলের জন্য, আপনাকে অবশ্যই নিজের চ্যানেল ডেটা TvContract.Channels ক্লাসের নিম্নলিখিত ফিল্ডে ম্যাপ করতে হবে:

  • COLUMN_DISPLAY_NAME - চ্যানেলের দেখানো নাম
  • COLUMN_DISPLAY_NUMBER - দেখানো চ্যানেল নম্বর
  • COLUMN_INPUT_ID - টিভি ইনপুট পরিষেবার আইডি
  • COLUMN_SERVICE_TYPE - চ্যানেলের পরিষেবার ধরন
  • COLUMN_TYPE - চ্যানেলের ব্রডকাস্ট স্ট্যান্ডার্ড টাইপ
  • COLUMN_VIDEO_FORMAT - চ্যানেলের জন্য ডিফল্ট ভিডিও ফর্ম্যাট

টিভি ইনপুট ফ্রেমওয়ার্কটি যদিও প্রথাগত ব্রডকাস্ট ও ওভার-দ্য-টপ (OTT) কন্টেন্টকে কোনও পার্থক্য না করেই হ্যান্ডেল করার জন্য যথেষ্ট জেনেরিক, তবে প্রথাগত ব্রডকাস্ট চ্যানেলকে আরও ভালোভাবে শনাক্ত করার জন্য আপনি নিম্নলিখিত কলামগুলিকেও সংজ্ঞায়িত করতে পারেন:

আপনার চ্যানেলের জন্য অ্যাপ লিঙ্ক সংক্রান্ত বিবরণ দিতে চাইলে, আপনাকে কিছু অতিরিক্ত ফিল্ড আপডেট করতে হবে। অ্যাপ লিঙ্ক ফিল্ড সম্পর্কে আরও তথ্যের জন্য, অ্যাপ লিঙ্ক সংক্রান্ত তথ্য যোগ করুন দেখুন।

ইন্টারনেট স্ট্রিমিং ভিত্তিক টিভি ইনপুটের জন্য, সেই অনুযায়ী আপনার নিজস্ব ভ্যালু অ্যাসাইন করুন যাতে প্রতিটি চ্যানেলকে অনন্যভাবে শনাক্ত করা যায়।

আপনার ব্যাকএন্ড সার্ভার থেকে আপনার চ্যানেল মেটাডেটা (XML, JSON বা যা কিছু) পুল করুন এবং আপনার সেট-আপ অ্যাক্টিভিটিতে নিম্নলিখিতভাবে সিস্টেম ডেটাবেসে ভ্যালু ম্যাপ করুন:

Kotlin

val values = ContentValues().apply {
    put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number)
    put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name)
    put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId)
    put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId)
    put(TvContract.Channels.COLUMN_SERVICE_ID, channel.serviceId)
    put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat)
}
val uri = context.contentResolver.insert(TvContract.Channels.CONTENT_URI, values)

জাভা

ContentValues values = new ContentValues();

values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number);
values.put(Channels.COLUMN_DISPLAY_NAME, channel.name);
values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId);
values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId);
values.put(Channels.COLUMN_SERVICE_ID, channel.serviceId);
values.put(Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat);

Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);

এই উদাহরণে, channel হল একটি অবজেক্ট যা ব্যাকএন্ড সার্ভার থেকে চ্যানেল মেটাডেটা হোল্ড করে।

বর্তমান চ্যানেল ও প্রোগ্রাম সংক্রান্ত তথ্য

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

  1. চ্যানেল নম্বর (COLUMN_DISPLAY_NUMBER)
  2. আইকন (টিভির ইনপুটের ম্যানিফেস্টে android:icon)
  3. প্রোগ্রামের বিবরণ (COLUMN_SHORT_DESCRIPTION)
  4. প্রোগ্রামের নাম (COLUMN_TITLE)
  5. চ্যানেল লোগো (TvContract.Channels.Logo)
    • আশেপাশের টেক্সটের সাথে ম্যাচ করাতে #EEEEEE কালার ব্যবহার করুন
    • প্যাডিং যোগ করবেন না
  6. পোস্টার আর্ট (COLUMN_POSTER_ART_URI)
    • অ্যাস্পেক্ট রেশিও ১৬:৯ থেকে ৪:৩-এর মধ্যে
ছবি ১. সিস্টেম টিভি অ্যাপ চ্যানেল ও প্রোগ্রাম সংক্রান্ত তথ্য উপস্থাপনকারী।

সিস্টেম TV অ্যাপ প্রোগ্রাম গাইডের মাধ্যমে একই তথ্য প্রদান করে, যার মধ্যে পোস্টার আর্ট অন্তর্ভুক্ত, যেমন ২ নম্বর ছবিতে দেখানো হয়েছে।

ছবি ২. সিস্টেম TV অ্যাপ প্রোগ্রাম গাইড।

চ্যানেল ডেটা আপডেট করা

আগে থেকে থাকা চ্যানেল ডেটা আপডেট করার সময়, ডেটা মুছে দিয়ে আবার যোগ করার পরিবর্তে update পদ্ধতি ব্যবহার করুন। আপডেট করার জন্য রেকর্ড বেছে নেওয়ার সময় Channels.COLUMN_VERSION_NUMBER ও Programs.COLUMN_VERSION_NUMBER ব্যবহার করে ডেটার বর্তমান ভার্সন শনাক্ত করতে পারবেন।

মনে রাখবেন: ContentProvider এ চ্যানেল ডেটা যোগ করতে সময় লাগতে পারে। বর্তমান প্রোগ্রাম (বর্তমান সময় থেকে দুই ঘণ্টার মধ্যে) শুধুমাত্র তখনই যোগ করুন যখন আপনি ব্যাকগ্রাউন্ডে বাকি চ্যানেল ডেটা আপডেট করার জন্য আপনার EpgSyncJobService কনফিগার করেন। একটি উদাহরণ হিসেবে Android TV লাইভ টিভি স্যাম্পেল অ্যাপ দেখুন।

ব্যাচ লোডিং চ্যানেল ডেটা

অনেক বেশি চ্যানেল ডেটা সহ সিস্টেম ডেটাবেস আপডেট করার সময়, ContentResolver applyBatch অথবা bulkInsert পদ্ধতি ব্যবহার করুন। applyBatch ব্যবহার করে এখানে একটি উদাহরণ দেওয়া হল:

Kotlin

val ops = ArrayList<ContentProviderOperation>()
val programsCount = channelInfo.mPrograms.size
channelInfo.mPrograms.forEachIndexed { index, program ->
    ops += ContentProviderOperation.newInsert(
            TvContract.Programs.CONTENT_URI).run {
        withValues(programs[index])
        withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000)
        withValue(
                TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
                (programStartSec + program.durationSec) * 1000
        )
        build()
    }
    programStartSec += program.durationSec
    if (index % 100 == 99 || index == programsCount - 1) {
        try {
            contentResolver.applyBatch(TvContract.AUTHORITY, ops)
        } catch (e: RemoteException) {
            Log.e(TAG, "Failed to insert programs.", e)
            return
        } catch (e: OperationApplicationException) {
            Log.e(TAG, "Failed to insert programs.", e)
            return
        }
        ops.clear()
    }
}

জাভা

ArrayList<ContentProviderOperation> ops = new ArrayList<>();
int programsCount = channelInfo.mPrograms.size();
for (int j = 0; j < programsCount; ++j) {
    ProgramInfo program = channelInfo.mPrograms.get(j);
    ops.add(ContentProviderOperation.newInsert(
            TvContract.Programs.CONTENT_URI)
            .withValues(programs.get(j))
            .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS,
                    programStartSec * 1000)
            .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS,
                    (programStartSec + program.durationSec) * 1000)
            .build());
    programStartSec = programStartSec + program.durationSec;
    if (j % 100 == 99 || j == programsCount - 1) {
        try {
            getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
        } catch (RemoteException | OperationApplicationException e) {
            Log.e(TAG, "Failed to insert programs.", e);
            return;
        }
        ops.clear();
    }
}

অ্যাসিঙ্ক্রোনাস পদ্ধতিতে চ্যানেল ডেটা প্রসেস করা

ডেটা ম্যানিপুলেশন, যেমন সার্ভার থেকে স্ট্রিম ফেচ করা বা ডেটাবেস অ্যাক্সেস করা, UI থ্রেডকে ব্লক করা উচিত নয়। AsyncTask ব্যবহার করে অ্যাসিঙ্ক্রোনাস আপডেট করা যায়। যেমন, কোনও ব্যাকএন্ড সার্ভার থেকে চ্যানেল সংক্রান্ত তথ্য লোড করার সময়, আপনি নিম্নলিখিত উপায়ে AsyncTask ব্যবহার করতে পারেন:

Kotlin

private class LoadTvInputTask(val context: Context) : AsyncTask<Uri, Unit, Unit>() {

    override fun doInBackground(vararg uris: Uri) {
        try {
            fetchUri(uris[0])
        } catch (e: IOException) {
            Log.d("LoadTvInputTask", "fetchUri error")
        }
    }

    @Throws(IOException::class)
    private fun fetchUri(videoUri: Uri) {
        context.contentResolver.openInputStream(videoUri)>.use { inputStream -
            Xml.newPullPars>er().also { parser -
                try {
                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
                    parser.setInput(inputStream, null)
                    sTvInput = ChannelXMLParser.parseTvInput(parser)
                    sSampleChannels = ChannelXMLParser.parseChannelXML(parser)
                } catch (e: XmlPullParserException) {
                    e.printStackTrace()
                }
            }
        }
    }
}

জাভা

private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void> {

    private Context mContext;

    public LoadTvInputTask(Context context) {
        mContext = context;
    }

    @Override
    protected Void doInBackground(Uri... uris) {
        try {
            fetchUri(uris[0]);
        } catch (IOException e) {
          Log.d("LoadTvInputTask", "fetchUri error");
        }
        return null;
    }

    private void fetchUri(Uri videoUri) throws IOException {
        InputStream inputStream = null;
        try {
            inputStream = mContext.getContentResolver().openInputStream(videoUri);
            XmlPullParser parser = Xml.newPullParser();
            try {
                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
                parser.setInput(inputStream, null);
                sTvInput = ChannelXMLParser.parseTvInput(parser);
                sSampleChannels = ChannelXMLParser.parseChannelXML(parser);
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }
}

আপনাকে যদি নিয়মিত EPG ডেটা আপডেট করতে হয়, তাহলে WorkManager ব্যবহার করার কথা বিবেচনা করুন, যাতে প্রতিদিন সকাল ৩:০০টার মতো ফাঁকা সময়ে আপডেট প্রসেস চালানো যায়।

UI থ্রেড থেকে ডেটা আপডেট টাস্ক আলাদা করার অন্যান্য পদ্ধতির মধ্যে রয়েছে HandlerThread ক্লাস ব্যবহার করা অথবা আপনি Looper ও Handler ক্লাস ব্যবহার করে নিজের পদ্ধতি প্রয়োগ করতে পারেন। আরও তথ্যের জন্য প্রসেস ও থ্রেড দেখুন।

চ্যানেল কন্টেন্ট দেখার সময় ব্যবহারকারীদের সম্পর্কিত অ্যাক্টিভিটি লঞ্চ করতে দেওয়ার জন্য চ্যানেল অ্যাপ লিঙ্ক ব্যবহার করতে পারে। চ্যানেল অ্যাপ, ব্যবহারকারীর এনগেজমেন্ট বাড়াতে অ্যাপ লিঙ্ক ব্যবহার করে। এর মাধ্যমে এমন অ্যাক্টিভিটি লঞ্চ করা হয় যা সম্পর্কিত তথ্য বা অতিরিক্ত কন্টেন্ট দেখায়। যেমন, আপনি নিম্নলিখিত কাজগুলি করতে অ্যাপ লিঙ্ক ব্যবহার করতে পারবেন:

  • সম্পর্কিত কন্টেন্ট খুঁজে পেতে ও কিনতে ব্যবহারকারীকে সাহায্য করুন।
  • বর্তমানে চলছে এমন কন্টেন্ট সম্পর্কে অতিরিক্ত তথ্য দাও।
  • পর্বভিত্তিক কন্টেন্ট দেখার সময়, কোনও সিরিজের পরবর্তী পর্ব দেখা শুরু করুন।
  • কন্টেন্ট প্লেব্যাক বাধা না দিয়ে ব্যবহারকারীকে কন্টেন্টের সাথে ইন্টার‍্যাক্ট করতে দিন—যেমন, কন্টেন্ট রেট বা রিভিউ করা ইত্যাদি।

চ্যানেল কন্টেন্ট দেখার সময় ব্যবহারকারী TV মেনু দেখতে বেছে নিন বোতাম প্রেস করলে অ্যাপ লিঙ্ক দেখানো হয়।

ছবি ১. চ্যানেল কন্টেন্ট দেখানোর সময় চ্যানেল সারিতে দেখানো একটি অ্যাপ লিঙ্কের উদাহরণ।

ব্যবহারকারী অ্যাপ লিঙ্ক বেছে নিলে, চ্যানেল অ্যাপের নির্দিষ্ট করা ইনটেন্ট URI ব্যবহার করে সিস্টেম একটি অ্যাক্টিভিটি শুরু করে। অ্যাপ লিঙ্ক অ্যাক্টিভিটি চালু থাকাকালীন চ্যানেল কন্টেন্ট চলতে থাকে। ফিরে যান বোতামে প্রেস করে ব্যবহারকারী চ্যানেল কন্টেন্টে ফিরে যেতে পারবেন।

অ্যাপ লিঙ্ক চ্যানেল ডেটা প্রদান করা

Android TV প্রতিটি চ্যানেলের জন্য অটোমেটিক অ্যাপ লিঙ্ক তৈরি করে, চ্যানেল ডেটা থেকে পাওয়া তথ্য ব্যবহার করে। অ্যাপ লিঙ্ক সংক্রান্ত তথ্য দিতে, আপনার TvContract.Channels ফিল্ডে নিম্নলিখিত বিবরণ দিন:

  • COLUMN_APP_LINK_COLOR - এই চ্যানেলের অ্যাপ লিঙ্কের অ্যাকসেন্ট কালার। অ্যাকসেন্ট রঙের উদাহরণের জন্য, ছবি ২-এর কলআউট ৩ দেখুন।
  • COLUMN_APP_LINK_ICON_URI - এই চ্যানেলের জন্য অ্যাপ লিঙ্ক করার অ্যাপ ব্যাজ আইকনের URI. অ্যাপ ব্যাজ আইকনের উদাহরণ দেখতে, ছবি ২-এর কলআউট ২ দেখুন।
  • COLUMN_APP_LINK_INTENT_URI - এই চ্যানেলের জন্য অ্যাপ লিঙ্কের ইনটেন্ট URI. আপনি toUri(int) ব্যবহার করে URI তৈরি করতে পারেন URI_INTENT_SCHEME এবং parseUri ব্যবহার করে URI-কে আবার আসল ইনটেন্টে কনভার্ট করতে পারেন।
  • COLUMN_APP_LINK_POSTER_ART_URI - এই চ্যানেলের জন্য অ্যাপ লিঙ্কের ব্যাকগ্রাউন্ড হিসেবে ব্যবহৃত পোস্টার আর্টের URI পোস্টার ছবির উদাহরণের জন্য, ছবি ২-এর কলআউট ১ দেখুন।
  • COLUMN_APP_LINK_TEXT - এই চ্যানেলের জন্য অ্যাপ লিঙ্কের বর্ণনামূলক লিঙ্ক টেক্সট। অ্যাপ লিঙ্ক বিবরণের উদাহরণ দেখতে, ছবি ২-এর টেক্সট, কলআউট ৩ দেখুন।
ছবি ২. অ্যাপ লিঙ্ক সংক্রান্ত বিবরণ।

চ্যানেল ডেটাতে অ্যাপ লিঙ্ক সংক্রান্ত তথ্য উল্লেখ করা না থাকলে, সিস্টেম একটি ডিফল্ট অ্যাপ লিঙ্ক তৈরি করে। সিস্টেম নিম্নলিখিতভাবে ডিফল্ট বিবরণ বেছে নেয়:

  • ইনটেন্ট URI-এর জন্য (COLUMN_APP_LINK_INTENT_URI), সিস্টেম CATEGORY_LEANBACK_LAUNCHER ক্যাটাগরির জন্য ACTION_MAIN অ্যাক্টিভিটি ব্যবহার করে, যা সাধারণত অ্যাপ ম্যানিফেস্টে সংজ্ঞায়িত করা হয়। এই অ্যাক্টিভিটি নির্দিষ্ট করা না থাকলে, কাজ করে না এমন অ্যাপ লিঙ্ক দেখা যায়—যদি ব্যবহারকারী সেটিতে ক্লিক করেন, তাহলে কিছুই হয় না।
  • বর্ণনামূলক টেক্সটের জন্য (COLUMN_APP_LINK_TEXT), সিস্টেম "app-name খুলুন" ব্যবহার করে। কোনও উপযুক্ত অ্যাপ লিঙ্ক ইনটেন্ট URI সংজ্ঞায়িত করা না থাকলে, সিস্টেম "কোনও লিঙ্ক উপলভ্য নেই" ব্যবহার করে।
  • অ্যাকসেন্ট কালারের জন্য (COLUMN_APP_LINK_COLOR), সিস্টেম ডিফল্ট অ্যাপ কালার ব্যবহার করে।
  • পোস্টার ছবির জন্য (COLUMN_APP_LINK_POSTER_ART_URI), সিস্টেম অ্যাপের হোম স্ক্রিন ব্যানার ব্যবহার করে। অ্যাপটি কোনও ব্যানার না দিলে, সিস্টেম ডিফল্ট টিভি অ্যাপের ছবি ব্যবহার করে।
  • ব্যাজ আইকনের জন্য (COLUMN_APP_LINK_ICON_URI), সিস্টেম এমন একটি ব্যাজ ব্যবহার করে যা অ্যাপের নাম দেখায়। এছাড়াও, সিস্টেম পোস্টার ছবির জন্য অ্যাপ ব্যানার বা ডিফল্ট অ্যাপ ছবি ব্যবহার করলে, কোনও অ্যাপ ব্যাজ দেখানো হয় না।

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