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

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

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

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

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

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

Kotlin फ़्लो

WindowLayoutInfo डेटा कलेक्शन को शुरू और बंद करने के लिए, फिर से शुरू किए जा सकने वाले, लाइफ़साइकल के बारे में जानकारी रखने वाले कोरुटाइन का इस्तेमाल किया जा सकता है. इसमें, लाइफ़साइकल कम से कम STARTED होने पर repeatOnLifecycle कोड ब्लॉक को शुरू किया जाता है और लाइफ़साइकल 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 से इनहेरिट की गई) फ़ोल्ड करने की सुविधा के बाउंडिंग रेक्टैंगल को दिखाती है, जैसे कि फ़ोल्ड या हिंज. बॉउंड का इस्तेमाल, स्क्रीन पर एलिमेंट को फ़ीचर के हिसाब से पोज़िशन करने के लिए किया जा सकता है:

KotlinJava
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.
               
}

       
}
   
}
}
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 का इस्तेमाल करके यह पता लगाएं कि डिवाइस, टेबलटॉप पोज़िशन में है या नहीं:

KotlinJava

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


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

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

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

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

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

उदाहरण

बुक पॉस्चर

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

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

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

KotlinJava
fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract
{ returns(true) implies (foldFeature != null) }
   
return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature
.orientation == FoldingFeature.Orientation.VERTICAL
}
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 की मदद से टेबलटॉप पोज़िशन लागू करना

कोडलैब