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

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

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

অনুমতি নিন

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

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

ডাটাবেসে চ্যানেলগুলি নিবন্ধন করুন

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

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

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

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

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

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

কোটলিন

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 )
    • ১৬:৯ এবং ৪:৩ এর মধ্যে আকৃতির অনুপাত
চিত্র ১. সিস্টেম টিভি অ্যাপের চ্যানেল ও প্রোগ্রাম তথ্য উপস্থাপক।

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

চিত্র ২. সিস্টেম টিভি অ্যাপের প্রোগ্রাম গাইড।

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

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

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

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

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

কোটলিন

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 ব্যবহার করতে পারেন:

কোটলিন

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.newPullParser().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 ক্লাস ব্যবহার করে নিজের মতো করে এটি তৈরি করতে পারেন। আরও তথ্যের জন্য প্রসেস এবং থ্রেড দেখুন।

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

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

চ্যানেলের কন্টেন্ট দেখার সময় ব্যবহারকারী যখন টিভি মেনু দেখানোর জন্য 'সিলেক্ট' চাপেন, তখন অ্যাপের লিঙ্কগুলো প্রদর্শিত হয়।

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

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

অ্যাপ লিঙ্ক চ্যানেল ডেটা সরবরাহ করুন

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

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

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

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

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

,

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

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

অনুমতি নিন

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

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

ডাটাবেসে চ্যানেলগুলি নিবন্ধন করুন

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

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

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

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

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

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

কোটলিন

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 )
    • ১৬:৯ এবং ৪:৩ এর মধ্যে আকৃতির অনুপাত
চিত্র ১. সিস্টেম টিভি অ্যাপের চ্যানেল ও প্রোগ্রাম তথ্য উপস্থাপক।

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

চিত্র ২. সিস্টেম টিভি অ্যাপের প্রোগ্রাম গাইড।

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

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

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

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

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

কোটলিন

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 ব্যবহার করতে পারেন:

কোটলিন

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.newPullParser().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 ক্লাস ব্যবহার করে নিজের মতো করে এটি তৈরি করতে পারেন। আরও তথ্যের জন্য প্রসেস এবং থ্রেড দেখুন।

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

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

চ্যানেলের কন্টেন্ট দেখার সময় ব্যবহারকারী যখন টিভি মেনু দেখানোর জন্য 'সিলেক্ট' চাপেন, তখন অ্যাপের লিঙ্কগুলো প্রদর্শিত হয়।

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

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

অ্যাপ লিঙ্ক চ্যানেল ডেটা সরবরাহ করুন

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

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

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

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

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

,

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

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

অনুমতি নিন

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

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

ডাটাবেসে চ্যানেলগুলি নিবন্ধন করুন

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

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

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

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

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

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

কোটলিন

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 )
    • ১৬:৯ এবং ৪:৩ এর মধ্যে আকৃতির অনুপাত
চিত্র ১. সিস্টেম টিভি অ্যাপের চ্যানেল ও প্রোগ্রাম তথ্য উপস্থাপক।

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

চিত্র ২. সিস্টেম টিভি অ্যাপের প্রোগ্রাম গাইড।

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

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

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

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

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

কোটলিন

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 ব্যবহার করতে পারেন:

কোটলিন

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.newPullParser().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 ক্লাস ব্যবহার করে নিজের মতো করে এটি তৈরি করতে পারেন। আরও তথ্যের জন্য প্রসেস এবং থ্রেড দেখুন।

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

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

চ্যানেলের কন্টেন্ট দেখার সময় ব্যবহারকারী যখন টিভি মেনু দেখানোর জন্য 'সিলেক্ট' চাপেন, তখন অ্যাপের লিঙ্কগুলো প্রদর্শিত হয়।

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

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

অ্যাপ লিঙ্ক চ্যানেল ডেটা সরবরাহ করুন

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

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

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

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

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

,

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

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

অনুমতি নিন

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

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

ডাটাবেসে চ্যানেলগুলি নিবন্ধন করুন

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

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

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

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

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

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

কোটলিন

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 )
    • ১৬:৯ এবং ৪:৩ এর মধ্যে আকৃতির অনুপাত
চিত্র ১. সিস্টেম টিভি অ্যাপের চ্যানেল ও প্রোগ্রাম তথ্য উপস্থাপক।

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

চিত্র ২. সিস্টেম টিভি অ্যাপের প্রোগ্রাম গাইড।

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

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

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

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

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

কোটলিন

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 ব্যবহার করতে পারেন:

কোটলিন

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.newPullParser().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 ক্লাস ব্যবহার করে নিজের মতো করে এটি তৈরি করতে পারেন। আরও তথ্যের জন্য প্রসেস এবং থ্রেড দেখুন।

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

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

চ্যানেলের কন্টেন্ট দেখার সময় ব্যবহারকারী যখন টিভি মেনু দেখানোর জন্য 'সিলেক্ট' চাপেন, তখন অ্যাপের লিঙ্কগুলো প্রদর্শিত হয়।

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

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

অ্যাপ লিঙ্ক চ্যানেল ডেটা সরবরাহ করুন

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

  • COLUMN_APP_LINK_COLOR - এই চ্যানেলের অ্যাপ লিঙ্কের অ্যাকসেন্ট কালার। অ্যাকসেন্ট কালারের একটি উদাহরণের জন্য, চিত্র ২, কলআউট ৩ দেখুন।
  • COLUMN_APP_LINK_ICON_URI - এই চ্যানেলের অ্যাপ লিঙ্কের অ্যাপ ব্যাজ আইকনের URI। একটি নমুনা অ্যাপ ব্যাজ আইকনের জন্য, চিত্র ২, কলআউট ২ দেখুন।
  • COLUMN_APP_LINK_INTENT_URI - এই চ্যানেলের অ্যাপ লিঙ্কের ইন্টেন্ট URI। আপনি URI_INTENT_SCHEME ব্যবহার করে toUri(int) এর মাধ্যমে URI তৈরি করতে পারেন এবং parseUri ব্যবহার করে URI-টিকে আবার মূল ইন্টেন্টে রূপান্তর করতে পারেন।
  • COLUMN_APP_LINK_POSTER_ART_URI - The URI for the poster art used as the background of the app link for this channel. For an example poster image, see figure 2, callout 1.
  • COLUMN_APP_LINK_TEXT - The descriptive link text of the app link for this channel. For an example app link description, see the text in figure 2, callout 3.
Figure 2. App link details.

If the channel data doesn't specify app link information, the system creates a default app link. The system chooses default details as follows:

  • For the intent URI ( COLUMN_APP_LINK_INTENT_URI ), the system uses the ACTION_MAIN activity for the CATEGORY_LEANBACK_LAUNCHER category, typically defined in the app manifest. If this activity is not defined, a non-functioning app link appears—if the user clicks it, nothing happens.
  • For the descriptive text ( COLUMN_APP_LINK_TEXT ), the system uses "Open app-name ". If no viable app link intent URI is defined, the system uses "No link available".
  • For the accent color ( COLUMN_APP_LINK_COLOR ), the system uses the default app color.
  • For the poster image ( COLUMN_APP_LINK_POSTER_ART_URI ), the system uses the app's home screen banner. If the app doesn't provide a banner, the system uses a default TV app image.
  • For the badge icon ( COLUMN_APP_LINK_ICON_URI ), the system uses a badge that shows the app name. If the system is also using the app banner or default app image for the poster image, no app badge is shown.

You specify app link details for your channels in your app's setup activity. You can update these app link details at any point, so if an app link needs to match channel changes, update app link details and call ContentResolver.update as needed. For more details on updating channel data, see Update channel data .