Jetpack की पिक्चर में पिक्चर लाइब्रेरी का इस्तेमाल करना

पिक्चर में पिक्चर (पीआईपी) Jetpack लाइब्रेरी, Android ऐप्लिकेशन डेवलपर के लिए पीआईपी की सुविधा लागू करने का आसान और मज़बूत तरीका उपलब्ध कराती है. खास तौर पर, मीडिया प्लेबैक, वीडियो कॉल, और नेविगेशन ऐप्लिकेशन के लिए. यह लाइब्रेरी, एक ही एपीआई उपलब्ध कराती है. इससे बॉयलरप्लेट कोड और ऐप्लिकेशन में होने वाली सामान्य गड़बड़ियां कम होती हैं. साथ ही, पीआईपी के उपयोगकर्ता अनुभव की क्वालिटी बेहतर होती है.

पीआईपी Jetpack लाइब्रेरी, मौजूदा पीआईपी एपीआई को बेहतर बनाती है. इसके लिए, Android इकोसिस्टम में आने वाली कई अहम चुनौतियों और गड़बड़ियों को ठीक किया जाता है:

  • ओएस फ़्रैगमेंटेशन: यह लाइब्रेरी, Android के अलग-अलग वर्शन में पीआईपी एपीआई कॉल के अंतर को अपने-आप मैनेज करती है. जैसे, Android 12 से पहले enterPictureInPictureMode और उसके बाद isAutoEnterEnabled का इस्तेमाल करना. इसलिए, डेवलपर को वर्शन के अंतर को मैनेज करने की ज़रूरत नहीं होती.
  • पीआईपी के गलत पैरामीटर: यह लाइब्रेरी, पीआईपी के पैरामीटर को सही तरीके से सेट करने के लिए एक ही तरीका उपलब्ध कराती है. जैसे, मीडिया प्लेबैक के दौरान, बेहतर क्वालिटी के ऐनिमेशन बनाने के लिए setSourceRectHint का इस्तेमाल करना.
  • पीआईपी की स्थिति के लिए एक ही कॉलबैक: यह लाइब्रेरी, स्थिति और यूज़र इंटरफ़ेस (यूआई) को आसानी से मैनेज करने के लिए, onPictureInPictureModeChanged और onPictureInPictureUiStateChanged को एक ही कॉलबैक इंटरफ़ेस (PictureInPictureDelegate.OnPictureInPictureEventListener) में जोड़ती है.
  • बॉयलरप्लेट कोड कम करना: यह लाइब्रेरी, सामान्य इस्तेमाल के उदाहरणों के लिए, RemoteActions के पहले से तय सेट उपलब्ध कराती है. जैसे, प्लेबैक कंट्रोल और वीडियो कॉल की कार्रवाइयां. इससे, बार-बार इस्तेमाल होने वाले बॉयलरप्लेट कोड की मात्रा कम हो जाती है.
  • आने वाले समय के लिए तैयारी: पीआईपी की अन्य सुविधाएं, Jetpack लाइब्रेरी के ज़रिए उपलब्ध कराई जाती हैं. इससे, इन सुविधाओं को अपनाने वाले लोगों को, कम से कम या बिना किसी मेहनत के अतिरिक्त सुविधाएं ऐक्सेस करने में मदद मिलती है.

माइग्रेशन का वर्कफ़्लो

ऐप्लिकेशन के इस्तेमाल के उदाहरण की कैटगरी और पुराने पीआईपी लॉजिक की पहचान करें:

कैटगरी: वीडियो प्लेबैक, नेविगेशन या वीडियो कॉल.

पुराना पीआईपी लॉजिक जिसकी पहचान करनी है:

  • onUserLeaveHint
  • setAutoEnterEnabled
  • onPictureInPictureModeChanged
  • onPictureInPictureUiStateChanged
  • setPictureInPictureParams.

2. AndroidManifest का कॉन्फ़िगरेशन

पक्का करें कि पीआईपी में जाने वाली गतिविधि, AndroidManifest.xml में ज़रूरी configChanges के साथ, पीआईपी की सुविधा के लिए सहायता का एलान करे. इससे, ऐप्लिकेशन को बार-बार रीस्टार्ट करने की ज़रूरत नहीं पड़ेगी:

<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>

3. एनवायरमेंट सेट अप करना

build.gradle में ज़रूरी डिपेंडेंसी जोड़ें:

dependencies {
implementation("androidx.core:core:1.18.0")
implementation("androidx.activity:activity:1.13.0")
implementation("androidx.core:core-pip:1.0.0-alpha02") }

डिपेंडेंसी के लिए, AndroidX की नई लाइब्रेरी का इस्तेमाल करें. साथ ही, इस बारे में जानकारी पाने के लिए, रिलीज़ पेज देखें.

4. टेंप्लेट चुनना और उसे शुरू करना

वह टेंप्लेट चुनें जो ऐप्लिकेशन के इस्तेमाल के उदाहरण के हिसाब से सबसे सही हो:

  • नेविगेशन और वीडियो कॉल: BasicPictureInPicture; आम तौर पर, साइज़ बदलने की सुविधा काम नहीं करती . साथ ही, आपको सोर्स रेक्ट हिंट की ज़रूरत नहीं होती.
  • वीडियो प्लेबैक: VideoPlaybackPictureInPicture; सोर्स रेक्ट हिंट के लिए, प्लेयर व्यू की सीमाओं को अपने-आप ट्रैक करता है. साथ ही, डिफ़ॉल्ट रूप से, साइज़ बदलने की सुविधा चालू करता है.

Jetpack लाइब्रेरी को अपनाने के लिए, पीआईपी को लागू करने के मौजूदा तरीके को Jetpack लाइब्रेरी के एपीआई से बदलें. इसे अपनाने में लगने वाली मुश्किल और लागत, ऐप्लिकेशन को लागू करने के मौजूदा तरीके के हिसाब से अलग-अलग होगी.

यहां पीआईपी के कुछ सामान्य इस्तेमाल के उदाहरण और उन्हें लागू करने के ज़रूरी चरणों के बारे में बताया गया है:

ऐप्लिकेशन, लाइब्रेरी को नेविगेशन की चालू या बंद स्थिति के बारे में बताता है. साथ ही, आसपेक्ट रेशियो सेट करता है. बाकी का काम Jetpack लाइब्रेरी करती है.

मुख्य अंतर:

  1. ऐप्लिकेशन की ओर से, ऑटो-एंटर और लेगसी-एंटर के बीच अंतर करने की ज़रूरत नहीं है.
  2. कॉलबैक इंटरफ़ेस को एक साथ इस्तेमाल किया जा सकता है.
  3. पिछले वर्शन के साथ काम करने की सुविधा के लिए, नया PictureInPictureParams बिल्डर.

वीडियो कॉल

ऐप्लिकेशन, लाइब्रेरी को कॉल की चालू या बंद स्थिति के बारे में बताता है. साथ ही, आसपेक्ट रेशियो सेट करता है.

मुख्य अंतर:

  1. ऐप्लिकेशन की ओर से, ऑटो-एंटर और लेगसी-एंटर के बीच अंतर करने की ज़रूरत नहीं है.
  2. कॉलबैक इंटरफ़ेस को एक साथ इस्तेमाल किया जा सकता है.
  3. पिछले वर्शन के साथ काम करने की सुविधा के लिए, नया PictureInPictureParams बिल्डर.
  4. वीडियो कॉल के लिए, कार्रवाई के आइकॉन को स्टैंडर्ड बनाना.

5. कोड माइग्रेट करना

  • एंट्री लॉजिक: एपीआई के हिसाब से तय लॉजिक को setEnabled से बदलें. जैसे, setAutoEnterEnabled Android 12 और उसके बाद वाले वर्शन के लिए या onUserLeaveHint Android 11 और उससे पहले वाले वर्शन के लिए . पीआईपी की सुविधा के लिए ज़रूरी शर्तें पूरी होने की स्थिति में बदलाव होने पर, इसे ट्रिगर करें.
  • कॉलबैक: onPictureInPictureModeChanged (लेआउट टॉगल करना) और onPictureInPictureUiStateChanged (ऐनिमेशन/स्थितियां) को एक ही इवेंट-आधारित कॉलबैक onPictureInPictureEvent में जोड़ें.
  • कार्रवाइयां और पैरामीटर: टेंप्लेट इंस्टेंस पर setActions और setAspectRatio का इस्तेमाल करके, पैरामीटर में बदलाव होने पर उन्हें अपडेट करें.
  • वीडियो को खास तौर पर मैनेज करना: वीडियो ऐप्लिकेशन के लिए, सोर्स रेक्ट हिंट के अपडेट को ऑटोमेट करने और बेहतर ट्रांज़िशन पक्का करने के लिए, setPlayerView का इस्तेमाल करें. ` ### 6. क्लीनअप करें

VideoPlaybackPictureInPicture के लिए, व्यू ट्रैक करने वाले टूल जैसे संसाधनों को रिलीज़ करने के लिए, onDispose या onDestroy में close को कॉल करें.

रेफ़रंस के तौर पर लागू करने के पैटर्न

लागू करने के उदाहरण.

नेविगेशन और वीडियो कॉल

class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener {
    private lateinit var pictureInPictureImpl: BasicPictureInPicture
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pictureInPictureImpl = BasicPictureInPicture(this)
        // BasicPictureInPicture is ideal for Navigation and Video call use cases.
        pictureInPictureImpl.addOnPictureInPictureEventListener(
            ContextCompat.getMainExecutor(this),
            this
        )
        setContent {
        }
    }
    override fun onPictureInPictureEvent(
        event: PictureInPictureDelegate.Event,
        config: Configuration?
    ) {
        when (event) {
            PictureInPictureDelegate.Event.ENTERED -> { /* Toggle to PiP layout */ }
            PictureInPictureDelegate.Event.EXITED -> { /* Toggle to Full-screen layout */ }
            PictureInPictureDelegate.Event.STASHED -> { /* Optional: PiP is stashed */ }
            PictureInPictureDelegate.Event.UNSTASHED -> { /* Optional: PiP is unstashed */ }
        }
    }
}

वीडियो प्लेबैक

class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener {
    private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pictureInPictureImpl = VideoPlaybackPictureInPicture(this)
        pictureInPictureImpl.addOnPictureInPictureEventListener(
            ContextCompat.getMainExecutor(this),
            this
        )
        setContent {
            ContentScreen(pictureInPictureImpl)
        }
    }
    override fun onPictureInPictureEvent(
        event: PictureInPictureDelegate.Event,
        config: Configuration?
    ) {
        when (event) {
            PictureInPictureDelegate.Event.ENTER_ANIMATION_START -> { /* Hide overlays */ }
            PictureInPictureDelegate.Event.ENTER_ANIMATION_END -> { /* Animation finished */ }
            PictureInPictureDelegate.Event.ENTERED -> { /* Switch to PiP layout */ }
            PictureInPictureDelegate.Event.STASHED -> { /* PiP stashed */ }
            PictureInPictureDelegate.Event.UNSTASHED -> { /* PiP unstashed */ }
            PictureInPictureDelegate.Event.EXITED -> { /* Return to full-screen */ }
        }
    }

    @Composable
    fun ContentScreen(pipController: VideoPlaybackPictureInPicture) {
        DisposableEffect(pipController) {
            onDispose {
                pipController.close()
            }
        }
    }
}