अपने ऐप्लिकेशन को फ़ोल्ड करें

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

यह समझने से कि कोई डिवाइस, टेबलटॉप या बुक पोस्चर जैसे कॉन्फ़िगरेशन के साथ काम करता है या नहीं, अलग-अलग लेआउट के साथ काम करने या खास सुविधाएं देने के बारे में फ़ैसले लिए जा सकते हैं.

विंडो की जानकारी

Jetpack WindowManager में मौजूद WindowInfoTracker इंटरफ़ेस, विंडो के लेआउट की जानकारी दिखाता है. इंटरफ़ेस का windowLayoutInfo() तरीका, WindowLayoutInfo डेटा की एक स्ट्रीम दिखाता है. इससे आपके ऐप्लिकेशन को फ़ोल्ड किए जा सकने वाले डिवाइस की फ़ोल्डिंग की स्थिति के बारे में पता चलता है. WindowInfoTracker#getOrCreate() तरीका, WindowInfoTracker का इंस्टेंस बनाता है.

WindowManager, Kotlin फ़्लो और Java कॉलबैक का इस्तेमाल करके WindowLayoutInfo डेटा इकट्ठा करने की सुविधा देता है.

Kotlin फ़्लो

WindowLayoutInfo का डेटा कलेक्शन शुरू और बंद करने के लिए, लाइफ़साइकल के बारे में जानने वाली ऐसी को-रूटीन का इस्तेमाल किया जा सकता है जिसे फिर से शुरू किया जा सकता है. इसमें repeatOnLifecycle कोड ब्लॉक तब एक्ज़ीक्यूट होता है, जब लाइफ़साइकल कम से कम STARTED पर होता है. साथ ही, यह तब बंद हो जाता है, जब लाइफ़साइकल STOPPED पर होता है. लाइफ़साइकल के STARTED होने पर, कोड ब्लॉक अपने-आप फिर से शुरू हो जाता है. यहां दिए गए उदाहरण में, कोड ब्लॉक WindowLayoutInfo डेटा इकट्ठा करता है और उसका इस्तेमाल करता है:

class DisplayFeaturesActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDisplayFeaturesBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                    .windowLayoutInfo(this@DisplayFeaturesActivity)
                    .collect { newLayoutInfo ->
                        // Use newLayoutInfo to update the layout.
                    }
            }
        }
    }
}

Java कॉलबैक

androidx.window:window-java डिपेंडेंसी में शामिल कॉलबैक कंपैटिबिलिटी लेयर की मदद से, Kotlin फ़्लो का इस्तेमाल किए बिना WindowLayoutInfo अपडेट इकट्ठा किए जा सकते हैं. आर्टफ़ैक्ट में WindowInfoTrackerCallbackAdapter क्लास शामिल है. यह WindowInfoTracker को इस तरह से अडैप्ट करता है कि WindowLayoutInfo अपडेट पाने के लिए, कॉलबैक रजिस्टर (और अनरजिस्टर) किए जा सकें. उदाहरण के लिए:

public class SplitLayoutActivity extends AppCompatActivity {

    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    private ActivitySplitLayoutBinding binding;
    private final LayoutStateChangeCallback layoutStateChangeCallback =
            new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoTracker =
                new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoTracker
           .removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo newLayoutInfo) {
           SplitLayoutActivity.this.runOnUiThread( () -> {
               // Use newLayoutInfo to update the layout.
           });
       }
   }
}

RxJava की सुविधा

अगर पहले से ही RxJava (वर्शन 2 या 3) का इस्तेमाल किया जा रहा है, तो ऐसे आर्टफ़ैक्ट का फ़ायदा लिया जा सकता है जिनकी मदद से, Kotlin फ़्लो का इस्तेमाल किए बिना WindowLayoutInfo अपडेट इकट्ठा किए जा सकते हैं. इसके लिए, Observable या Flowable का इस्तेमाल किया जा सकता है.

androidx.window:window-rxjava2 और androidx.window:window-rxjava3 डिपेंडेंसी से मिली कंपैटबिलिटी लेयर में, WindowInfoTracker#windowLayoutInfoFlowable() और WindowInfoTracker#windowLayoutInfoObservable() तरीके शामिल होते हैं. इनकी मदद से, आपका ऐप्लिकेशन WindowLayoutInfo अपडेट पा सकता है. उदाहरण के लिए:

class RxActivity: AppCompatActivity {

    private lateinit var binding: ActivityRxBinding

    private var disposable: Disposable? = null
    private lateinit var observable: Observable<WindowLayoutInfo>

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

        // Create a new observable.
        observable = WindowInfoTracker.getOrCreate(this@RxActivity)
            .windowLayoutInfoObservable(this@RxActivity)
   }

   @Override
   protected void onStart() {
       super.onStart();

        // Subscribe to receive WindowLayoutInfo updates.
        disposable?.dispose()
        disposable = observable
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { newLayoutInfo ->
            // Use newLayoutInfo to update the layout.
        }
   }

   @Override
   protected void onStop() {
       super.onStop();

        // Dispose of the WindowLayoutInfo observable.
        disposable?.dispose()
   }
}

फ़ोल्ड किए जा सकने वाले डिसप्ले की सुविधाएं

Jetpack WindowManager की WindowLayoutInfo क्लास, डिसप्ले विंडो की सुविधाओं को DisplayFeature एलिमेंट की सूची के तौर पर उपलब्ध कराती है.

FoldingFeature, DisplayFeature का एक टाइप है. यह फ़ोल्ड किए जा सकने वाले डिसप्ले के बारे में जानकारी देता है. इसमें ये प्रॉपर्टी शामिल हैं:

  • state: डिवाइस के मुड़े होने की स्थिति, FLAT या HALF_OPENED

  • orientation: फ़ोल्ड या हिंज का ओरिएंटेशन, HORIZONTAL या VERTICAL

  • occlusionType: फ़ोल्ड या हिंज, डिसप्ले के किसी हिस्से को छिपाता है या नहीं, NONE या FULL

  • isSeparating: फ़ोल्ड या हिंज की वजह से, दो लॉजिकल डिसप्ले एरिया बनते हैं या नहीं. इसकी वैल्यू सही या गलत होती है

फ़ोल्ड किए जा सकने वाले डिवाइस में, HALF_OPENED हमेशा isSeparating के तौर पर रिपोर्ट करता है, क्योंकि स्क्रीन को दो डिसप्ले एरिया में बांटा जाता है. इसके अलावा, दो स्क्रीन वाले डिवाइस पर isSeparating हमेशा सही होता है. ऐसा तब होता है, जब ऐप्लिकेशन दोनों स्क्रीन पर दिखता है.

FoldingFeature bounds प्रॉपर्टी (DisplayFeature से इनहेरिट की गई) फ़ोल्डिंग सुविधा के बाउंडिंग रेक्टैंगल को दिखाती है. जैसे, फ़ोल्ड या हिंज. बाउंड्री का इस्तेमाल करके, स्क्रीन पर एलिमेंट को सुविधा के हिसाब से सेट किया जा सकता है:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }
        }
    }
}

Java

private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    // ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}

टेबलटॉप पॉस्चर

FoldingFeature ऑब्जेक्ट में शामिल जानकारी का इस्तेमाल करके, आपका ऐप्लिकेशन टेबलटॉप जैसे पोस्चर के साथ काम कर सकता है. टेबलटॉप पोस्चर में, फ़ोन को किसी सतह पर रखा जाता है, हिंज हॉरिज़ॉन्टल स्थिति में होता है, और फ़ोल्ड की जा सकने वाली स्क्रीन आधी खुली होती है.

टेबलटॉप मोड में फ़ोन को बिना हाथ में लिए इस्तेमाल किया जा सकता है. टेबलटॉप मोड में डिवाइस को रखकर मीडिया देखा जा सकता है, फ़ोटो खींची जा सकती हैं, और वीडियो कॉल किए जा सकते हैं.

पहली इमेज. टेबलटॉप मोड में वीडियो प्लेयर ऐप्लिकेशन—स्क्रीन के वर्टिकल हिस्से पर वीडियो और हॉरिज़ॉन्टल हिस्से पर प्लेबैक कंट्रोल.

यह पता लगाने के लिए कि डिवाइस टेबलटॉप मोड में है या नहीं, FoldingFeature.State और FoldingFeature.Orientation का इस्तेमाल करें:

Kotlin

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

Java

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

जब आपको पता चल जाए कि डिवाइस को टेबलटॉप मोड में रखा गया है, तब अपने ऐप्लिकेशन के लेआउट को उसके हिसाब से अपडेट करें. मीडिया ऐप्लिकेशन के लिए, इसका मतलब आम तौर पर यह होता है कि फ़ोल्ड के ऊपर वीडियो चलाने की सुविधा दी जाए. साथ ही, बिना हाथ लगाए वीडियो देखने या संगीत सुनने के लिए, कंट्रोल और अन्य कॉन्टेंट को फ़ोल्ड के ठीक नीचे रखा जाए.

Android 15 (एपीआई लेवल 35) और इसके बाद के वर्शन पर, सिंक्रोनस एपीआई को कॉल किया जा सकता है. इससे यह पता लगाया जा सकता है कि डिवाइस, टेबलटॉप मोड के साथ काम करता है या नहीं. भले ही, डिवाइस की मौजूदा स्थिति कुछ भी हो.

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

Kotlin

if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}

Java

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}

उदाहरण

किताब पढ़ने की मुद्रा

फ़ोल्ड किए जा सकने वाले डिवाइस की एक और खास सुविधा है, बुक पोस्चर. इसमें डिवाइस को आधा खोला जाता है और हिंज वर्टिकल होता है. किताब की तरह डिवाइस को पकड़ने की मुद्रा, ई-बुक पढ़ने के लिए सबसे सही है. बड़ी स्क्रीन वाले फ़ोल्ड किए जा सकने वाले डिवाइस पर, किताब को दो पेज वाले लेआउट में दिखाया जाता है. इससे किताब पढ़ने का अनुभव मिलता है.

अगर आपको हाथ का इस्तेमाल किए बिना फ़ोटो लेते समय, अलग आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) में फ़ोटो लेनी है, तो इसका इस्तेमाल फ़ोटोग्राफ़ी के लिए भी किया जा सकता है.

किताब की तरह डिवाइस को पकड़ने के लिए, टेबलटॉप मोड में डिवाइस को पकड़ने के लिए इस्तेमाल की जाने वाली तकनीकों का इस्तेमाल करें. इन दोनों कोड में सिर्फ़ यह अंतर है कि कोड को यह जांच करनी चाहिए कि फ़ोल्डिंग की सुविधा का ओरिएंटेशन हॉरिज़ॉन्टल के बजाय वर्टिकल है या नहीं:

Kotlin

fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}

Java

boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

विंडो के साइज़ में बदलाव

डिवाइस के कॉन्फ़िगरेशन में बदलाव होने की वजह से, ऐप्लिकेशन का डिसप्ले एरिया बदल सकता है. उदाहरण के लिए, जब डिवाइस को फ़ोल्ड या अनफ़ोल्ड किया जाता है, घुमाया जाता है या मल्टी‑विंडो मोड में किसी विंडो का साइज़ बदला जाता है.

Jetpack WindowManager WindowMetricsCalculator क्लास की मदद से, मौजूदा और ज़्यादा से ज़्यादा विंडो मेट्रिक को वापस पाया जा सकता है. एपीआई लेवल 30 में पेश किए गए प्लैटफ़ॉर्म WindowMetrics की तरह ही, WindowManager WindowMetrics विंडो की सीमाएं तय करता है. हालांकि, यह एपीआई, एपीआई लेवल 14 तक के वर्शन के साथ काम करता है.

विंडो के साइज़ के क्लास का इस्तेमाल करना लेख पढ़ें.

अन्य संसाधन

सैंपल

  • Jetpack WindowManager: Jetpack WindowManager लाइब्रेरी इस्तेमाल करने का उदाहरण
  • Jetcaster : Compose की मदद से टेबलटॉप मोड लागू करना

कोडलैब