1. शुरू करने से पहले
फ़ोल्ड किए जा सकने वाले फ़ोन में क्या खास है?
फ़ोल्ड किए जा सकने वाले फ़ोन, एक पीढ़ी में एक बार होने वाले इनोवेशन हैं. ये डिवाइस, उपयोगकर्ताओं को खास अनुभव देते हैं. साथ ही, इनसे आपको अपने उपयोगकर्ताओं को अलग-अलग फ़ीचर देने के खास मौके मिलते हैं. जैसे, बिना हाथ लगाए इस्तेमाल करने के लिए टेबलटॉप यूज़र इंटरफ़ेस (यूआई).
ज़रूरी शर्तें
- Android ऐप्लिकेशन डेवलप करने की बुनियादी जानकारी
- Hilt डिपेंडेंसी इंजेक्शन फ़्रेमवर्क की बुनियादी जानकारी
आपको क्या बनाने को मिलेगा
इस कोडलैब में, फ़ोल्ड किए जा सकने वाले डिवाइसों के लिए ऑप्टिमाइज़ किए गए लेआउट वाला कैमरा ऐप्लिकेशन बनाया जाता है.

आपको एक बेसिक कैमरा ऐप्लिकेशन मिलता है, जो डिवाइस की किसी भी पोज़िशन पर काम नहीं करता. साथ ही, बेहतर सेल्फ़ी लेने के लिए, पीछे वाले कैमरे का इस्तेमाल नहीं करता. डिवाइस को खोलने पर, झलक को छोटी डिसप्ले पर ले जाने के लिए सोर्स कोड अपडेट करें. साथ ही, फ़ोन को टेबलटॉप मोड में सेट करने पर प्रतिक्रिया दें.
कैमरा ऐप्लिकेशन के लिए, इस एपीआई का इस्तेमाल करना सबसे आसान है. हालांकि, इस कोडलैब में बताई गई दोनों सुविधाओं को किसी भी ऐप्लिकेशन पर लागू किया जा सकता है.
आपको क्या सीखने को मिलेगा
- डिवाइस की स्थिति बदलने पर, Jetpack Window Manager का इस्तेमाल करके प्रतिक्रिया देने का तरीका
- अपने ऐप्लिकेशन को फ़ोल्ड किए जा सकने वाले डिवाइस की छोटी स्क्रीन पर ले जाने का तरीका
आपको इन चीज़ों की ज़रूरत होगी
- Android Studio का नया वर्शन
- फ़ोल्ड किया जा सकने वाला डिवाइस या फ़ोल्ड किए जा सकने वाले डिवाइस का एम्युलेटर
2. सेट अप करें
शुरुआत में इस्तेमाल होने वाला कोड पाना
- अगर आपने Git इंस्टॉल किया है, तो नीचे दी गई कमांड चलाएं. यह देखने के लिए कि Git इंस्टॉल है या नहीं, टर्मिनल या कमांड लाइन में
git --versionटाइप करें. इसके बाद, पुष्टि करें कि यह सही तरीके से काम कर रहा है.
git clone https://github.com/android/large-screen-codelabs.git
- ज़रूरी नहीं: अगर आपके पास Git नहीं है, तो इस कोडलैब के सभी कोड डाउनलोड करने के लिए, यहां दिए गए बटन पर क्लिक करें:
पहला मॉड्यूल खोलें
- Android Studio में,
/step1में जाकर पहला मॉड्यूल खोलें.

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

- अब डिवाइस को आधी खुली हुई स्थिति में रखें. इस स्थिति में, डिवाइस का हिंज पूरी तरह से फ़्लैट या बंद नहीं होता है, बल्कि 90 डिग्री का कोण बनाता है.
जैसा कि आप देख सकते हैं, ऐप्लिकेशन अलग-अलग डिवाइस पोस्चर के हिसाब से काम नहीं करता है. इसलिए, लेआउट नहीं बदलता है और हिंज, व्यूफ़ाइंडर के बीच में ही रहता है.
4. Jetpack WindowManager के बारे में जानकारी
Jetpack WindowManager लाइब्रेरी की मदद से, ऐप्लिकेशन डेवलपर फ़ोल्ड किए जा सकने वाले डिवाइसों के लिए बेहतर अनुभव बना सकते हैं. इसमें FoldingFeature क्लास शामिल है. यह क्लास, फ़्लेक्सिबल डिसप्ले में फ़ोल्ड या दो फ़िज़िकल डिसप्ले पैनल के बीच मौजूद हिंज के बारे में बताती है. इसका एपीआई, डिवाइस से जुड़ी ज़रूरी जानकारी ऐक्सेस करने की सुविधा देता है:
- अगर हिंज को 180 डिग्री पर खोला जाता है, तो
state(),FLATदिखाता है. अगर हिंज को 180 डिग्री पर नहीं खोला जाता है, तोstate(),HALF_OPENEDदिखाता है. - अगर
FoldingFeatureकी चौड़ाई, ऊंचाई से ज़्यादा है, तोorientation(),FoldingFeature.Orientation.HORIZONTALदिखाता है. अगर चौड़ाई, ऊंचाई से ज़्यादा नहीं है, तोorientation(),FoldingFeature.Orientation.VERTICALदिखाता है. bounds(),FoldingFeatureकी सीमाओं कोRectफ़ॉर्मैट में दिखाता है.
FoldingFeature क्लास में अतिरिक्त जानकारी होती है, जैसे कि occlusionType() या isSeparating(). हालांकि, इस कोडलैब में इनके बारे में ज़्यादा जानकारी नहीं दी गई है.
वर्शन 1.2.0-beta01 से, लाइब्रेरी WindowAreaController का इस्तेमाल करती है. यह एक ऐसा एपीआई है जो रियर डिसप्ले मोड को चालू करता है. इससे मौजूदा विंडो को रियर कैमरे के साथ अलाइन किए गए डिसप्ले पर ले जाया जा सकता है. यह रियर कैमरे से सेल्फ़ी लेने और कई अन्य कामों के लिए बहुत अच्छा है!
डिपेंडेंसी जोड़ना
- अपने ऐप्लिकेशन में Jetpack WindowManager का इस्तेमाल करने के लिए, आपको मॉड्यूल-लेवल की
build.gradleफ़ाइल में ये डिपेंडेंसी जोड़नी होंगी:
step1/build.gradle
def work_version = '1.2.0-beta01'
implementation "androidx.window:window:$work_version"
implementation "androidx.window:window-java:$work_version"
implementation "androidx.window:window-core:$work_version"
अब आपके ऐप्लिकेशन में, FoldingFeature और WindowAreaController, दोनों क्लास ऐक्सेस की जा सकती हैं. इनका इस्तेमाल करके, फ़ोल्ड किए जा सकने वाले फ़ोन पर कैमरा इस्तेमाल करने का बेहतरीन अनुभव दिया जा सकता है!
5. रीयर सेल्फ़ी मोड लागू करना
रियर डिसप्ले मोड से शुरू करें.
इस मोड को चालू करने की अनुमति देने वाला एपीआई WindowAreaController है. यह एपीआई, किसी डिवाइस पर डिसप्ले या डिसप्ले एरिया के बीच विंडो को मूव करने के बारे में जानकारी और व्यवहार के बारे में बताता है.
इसकी मदद से, WindowAreaInfo की उन सुविधाओं के बारे में क्वेरी की जा सकती है जिनके साथ फ़िलहाल इंटरैक्ट किया जा सकता है.
WindowAreaInfo का इस्तेमाल करके, WindowAreaSession को ऐक्सेस किया जा सकता है. यह एक इंटरफ़ेस है, जो चालू विंडो एरिया सुविधा और किसी WindowAreaCapability. के लिए उपलब्धता की स्थिति को दिखाता है
- इन वैरिएबल का एलान अपने
MainActivityमें करें:
step1/MainActivity.kt
private lateinit var windowAreaController: WindowAreaController
private lateinit var displayExecutor: Executor
private var rearDisplaySession: WindowAreaSession? = null
private var rearDisplayWindowAreaInfo: WindowAreaInfo? = null
private var rearDisplayStatus: WindowAreaCapability.Status =
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED
private val rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
- इसके बाद, उन्हें
onCreate()तरीके में शुरू करें:
step1/MainActivity.kt
displayExecutor = ContextCompat.getMainExecutor(this)
windowAreaController = WindowAreaController.getOrCreate()
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
windowAreaController.windowAreaInfos
.map{info->info.firstOrNull{it.type==WindowAreaInfo.Type.TYPE_REAR_FACING}}
.onEach { info -> rearDisplayWindowAreaInfo = info }
.map{it?.getCapability(rearDisplayOperation)?.status?: WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED }
.distinctUntilChanged()
.collect {
rearDisplayStatus = it
updateUI()
}
}
}
- अब
updateUI()फ़ंक्शन को लागू करें, ताकि मौजूदा स्थिति के हिसाब से रियर सेल्फ़ी बटन को चालू या बंद किया जा सके:
step1/MainActivity.kt
private fun updateUI() {
if(rearDisplaySession != null) {
binding.rearDisplay.isEnabled = true
// A session is already active, clicking on the button will disable it
} else {
when(rearDisplayStatus) {
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> {
binding.rearDisplay.isEnabled = false
// RearDisplay Mode is not supported on this device"
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> {
binding.rearDisplay.isEnabled = false
// RearDisplay Mode is not currently available
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> {
binding.rearDisplay.isEnabled = true
// You can enable RearDisplay Mode
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE -> {
binding.rearDisplay.isEnabled = true
// You can disable RearDisplay Mode
}
else -> {
binding.rearDisplay.isEnabled = false
// RearDisplay status is unknown
}
}
}
}
यह आखिरी चरण ज़रूरी नहीं है, लेकिन WindowAreaCapability. की सभी संभावित स्थितियों के बारे में जानने के लिए यह बहुत काम का है
- अब
toggleRearDisplayModeफ़ंक्शन लागू करें. इससे सेशन बंद हो जाएगा. अगर यह सुविधा पहले से चालू है, तोtransferActivityToWindowAreaफ़ंक्शन को कॉल करें:
step1/CameraViewModel.kt
private fun toggleRearDisplayMode() {
if(rearDisplayStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) {
if(rearDisplaySession == null) {
rearDisplaySession = rearDisplayWindowAreaInfo?.getActiveSession(rearDisplayOperation)
}
rearDisplaySession?.close()
} else {
rearDisplayWindowAreaInfo?.token?.let { token ->
windowAreaController.transferActivityToWindowArea(
token = token,
activity = this,
executor = displayExecutor,
windowAreaSessionCallback = this
)
}
}
}
ध्यान दें कि MainActivity का इस्तेमाल WindowAreaSessionCallback के तौर पर किया गया है.
Rear Display API, लिसनर के तौर पर काम करता है: जब कॉन्टेंट को दूसरे डिसप्ले पर ले जाने का अनुरोध किया जाता है, तब एक सेशन शुरू होता है. यह सेशन, लिसनर की onSessionStarted() तरीके से वापस मिलता है. अगर आपको इनर (और बड़े) डिसप्ले पर वापस जाना है, तो सेशन बंद करें. इसके बाद, आपको onSessionEnded() तरीके से पुष्टि का मैसेज मिलेगा. इस तरह का लिसनर बनाने के लिए, आपको WindowAreaSessionCallback इंटरफ़ेस लागू करना होगा.
MainActivityएलान में बदलाव करें, ताकि यहWindowAreaSessionCallbackइंटरफ़ेस को लागू करे:
step1/MainActivity.kt
class MainActivity : AppCompatActivity(), WindowAreaSessionCallback
अब, MainActivity के अंदर onSessionStarted और onSessionEnded तरीकों को लागू करें. ये कॉलबैक मेथड, सेशन की स्थिति के बारे में सूचना पाने और ऐप्लिकेशन को उसके हिसाब से अपडेट करने के लिए बहुत काम के होते हैं.
हालांकि, इस बार आसानी के लिए, फ़ंक्शन बॉडी में सिर्फ़ यह देखें कि कोई गड़बड़ी है या नहीं. साथ ही, स्थिति को लॉग करें.
step1/MainActivity.kt
override fun onSessionEnded(t: Throwable?) {
if(t != null) {
Log.d("Something was broken: ${t.message}")
}else{
Log.d("rear session ended")
}
}
override fun onSessionStarted(session: WindowAreaSession) {
Log.d("rear session started [session=$session]")
}
- ऐप्लिकेशन बनाएं और उसे चलाएं. इसके बाद, डिवाइस को खोलें और पीछे की स्क्रीन पर मौजूद बटन पर टैप करें. आपको इस तरह का मैसेज दिखेगा:

- अपने कॉन्टेंट को बाहरी डिसप्ले पर ले जाने के लिए, "अभी स्क्रीन स्विच करें" को चुनें!
6. टेबलटॉप मोड लागू करना
अब अपने ऐप्लिकेशन को फ़ोल्ड किए जाने की सुविधा के साथ काम करने के लिए तैयार करें: फ़ोल्ड किए जाने की सुविधा के हिसाब से, अपने कॉन्टेंट को डिवाइस के किनारे या हिंज के ऊपर ले जाएं. ऐसा करने के लिए, आपको FoldingStateActor के अंदर काम करना होगा, ताकि आपका कोड Activity से अलग हो जाए और उसे आसानी से पढ़ा जा सके.
इस एपीआई का मुख्य हिस्सा WindowInfoTracker इंटरफ़ेस है. इसे एक स्टैटिक तरीके से बनाया जाता है. इसके लिए, Activity की ज़रूरत होती है:
step1/CameraCodelabDependencies.kt
@Provides
fun provideWindowInfoTracker(activity: Activity) =
WindowInfoTracker.getOrCreate(activity)
आपको यह कोड लिखने की ज़रूरत नहीं है, क्योंकि यह पहले से मौजूद है. हालांकि, यह समझने के लिए यह कोड काम का है कि WindowInfoTracker कैसे बनाया गया है.
- विंडो में होने वाले किसी भी बदलाव को सुनने के लिए, अपने
ActivityकेonResume()तरीके में इन बदलावों को सुनें:
step1/MainActivity.kt
lifecycleScope.launch {
foldingStateActor.checkFoldingState(
this@MainActivity,
binding.viewFinder
)
}
- अब
FoldingStateActorफ़ाइल खोलें, क्योंकि अबcheckFoldingState()तरीका भरने का समय आ गया है.
जैसा कि आपने पहले ही देखा है, यह आपके Activity के RESUMED फ़ेज़ में चलता है. साथ ही, लेआउट में हुए किसी भी बदलाव को सुनने के लिए, यह WindowInfoTracker का इस्तेमाल करता है.
step1/FoldingStateActor.kt
windowInfoTracker.windowLayoutInfo(activity)
.collect { newLayoutInfo ->
activeWindowLayoutInfo = newLayoutInfo
updateLayoutByFoldingState(cameraViewfinder)
}
WindowInfoTracker इंटरफ़ेस का इस्तेमाल करके, windowLayoutInfo() को कॉल किया जा सकता है. इससे WindowLayoutInfo का Flow इकट्ठा किया जा सकता है. इसमें DisplayFeature में मौजूद सभी जानकारी शामिल होती है.
आखिरी चरण में, इन बदलावों के हिसाब से कॉन्टेंट में बदलाव करना होता है. यह काम updateLayoutByFoldingState() तरीके से किया जाता है. इसके लिए, एक बार में एक चरण पूरा करना होता है.
- पक्का करें कि
activityLayoutInfoमें कुछDisplayFeatureप्रॉपर्टी शामिल हों और उनमें से कम से कम एकFoldingFeatureहो. अगर ऐसा नहीं है, तो आपको कुछ भी नहीं करना है:
step1/FoldingStateActor.kt
val foldingFeature = activeWindowLayoutInfo?.displayFeatures
?.firstOrNull { it is FoldingFeature } as FoldingFeature?
?: return
- फ़ोल्ड की पोज़िशन का हिसाब लगाएं, ताकि यह पक्का किया जा सके कि डिवाइस की पोज़िशन से आपके लेआउट पर असर पड़ रहा है और वह आपकी क्रम-व्यवस्था की सीमाओं से बाहर नहीं है:
step1/FoldingStateActor.kt
val foldPosition = FoldableUtils.getFeaturePositionInViewRect(
foldingFeature,
cameraViewfinder.parent as View
) ?: return
अब आपको पता चल गया है कि आपके पास एक ऐसा FoldingFeature है जिससे आपके लेआउट पर असर पड़ता है. इसलिए, आपको अपना कॉन्टेंट दूसरी जगह ले जाना होगा.
- देखें कि
FoldingFeatureHALF_OPENहै या नहीं. अगर ऐसा नहीं है, तो अपने कॉन्टेंट की जगह को वापस लाएं. अगर यहHALF_OPENहै, तो आपको एक और जांच करनी होगी. साथ ही, फ़ोल्ड के ओरिएंटेशन के आधार पर अलग-अलग कार्रवाई करनी होगी:
step1/FoldingStateActor.kt
if (foldingFeature.state == FoldingFeature.State.HALF_OPENED) {
when (foldingFeature.orientation) {
FoldingFeature.Orientation.VERTICAL -> {
cameraViewfinder.moveToRightOf(foldPosition)
}
FoldingFeature.Orientation.HORIZONTAL -> {
cameraViewfinder.moveToTopOf(foldPosition)
}
}
} else {
cameraViewfinder.restore()
}
अगर फ़ोल्ड VERTICAL है, तो अपने कॉन्टेंट को दाईं ओर ले जाएं. अगर फ़ोल्ड है, तो अपने कॉन्टेंट को फ़ोल्ड की जगह के ऊपर ले जाएं.
- अपने ऐप्लिकेशन को बनाएं और चलाएं. इसके बाद, डिवाइस को खोलें और टेबलटॉप मोड में रखें. इससे आपको दिखेगा कि कॉन्टेंट, डिवाइस की स्क्रीन के हिसाब से अपने-आप अडजस्ट हो रहा है!
7. बधाई हो!
इस कोडलैब में, आपने फ़ोल्ड किए जा सकने वाले डिवाइसों की कुछ खास सुविधाओं के बारे में जाना. जैसे, रियर डिसप्ले मोड या टेबलटॉप मोड. साथ ही, आपने यह भी जाना कि Jetpack WindowManager का इस्तेमाल करके, इन सुविधाओं को कैसे अनलॉक किया जा सकता है.
अब आपके पास अपने कैमरा ऐप्लिकेशन में, उपयोगकर्ताओं को बेहतरीन अनुभव देने का विकल्प है.