1. शुरू करने से पहले
फ़ोल्ड किए जा सकने वाले डिवाइसों में खास क्या है?
फ़ोल्ड किए जा सकने वाले डिवाइस, एक पीढ़ी से दूसरी पीढ़ी में आते हैं. इनसे उपयोगकर्ताओं को बेहतरीन अनुभव मिलता है. साथ ही, उपयोगकर्ताओं को टेबलटॉप यूआई जैसी अलग-अलग सुविधाओं से खुश करने का मौका मिलता है. ये सुविधाएं बोलकर इस्तेमाल करने की सुविधा देती हैं.
ज़रूरी शर्तें
- Android ऐप्लिकेशन डेवलप करने की बुनियादी जानकारी
- हिल्ट डिपेंडेंसी इंजेक्शन फ़्रेमवर्क के बारे में बुनियादी जानकारी
आपको क्या बनाना होगा
इस कोडलैब में, फ़ोल्ड किए जा सकने वाले डिवाइसों के लिए, ऑप्टिमाइज़ किए गए लेआउट वाला कैमरा ऐप्लिकेशन बनाया जाता है.
शुरुआत में एक ऐसे कैमरा ऐप्लिकेशन का इस्तेमाल करें जो डिवाइस के किसी भी पॉस्चर के हिसाब से काम न करता हो. इसके अलावा, बेहतर सेल्फ़ी के लिए, पीछे के बेहतर कैमरे का इस्तेमाल करें. सोर्स कोड को अपडेट किया जाता है, ताकि डिवाइस के अनफ़ोल्ड होने पर, झलक को छोटे डिसप्ले पर देखा जा सके और टेबलटॉप मोड में सेट किए गए फ़ोन के हिसाब से प्रतिक्रिया दी जा सके.
वैसे तो इस एपीआई का इस्तेमाल करने के लिए Camera ऐप्लिकेशन सबसे आसान है, लेकिन इस कोडलैब में सीखी गई दोनों सुविधाओं को किसी भी ऐप्लिकेशन पर लागू किया जा सकता है.
आपको इनके बारे में जानकारी मिलेगी
- पॉस्चर में होने वाले बदलाव पर प्रतिक्रिया देने के लिए, Jetpack विंडो मैनेजर इस्तेमाल करने का तरीका
- अपने ऐप्लिकेशन को फ़ोल्ड किए जा सकने वाले छोटे डिसप्ले पर ले जाने का तरीका
आपको इन चीज़ों की ज़रूरत होगी
- 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
दिखाता है, नहीं तोHALF_OPENED
दिखाता है. - अगर
FoldingFeature
की चौड़ाई, ऊंचाई से ज़्यादा है, तोorientation()
नतीजे के तौर परFoldingFeature.Orientation.HORIZONTAL
दिखाता है; ऐसा न होने पर,FoldingFeature.Orientation.VERTICAL
दिखाया जाता है. bounds()
,Rect
फ़ॉर्मैट मेंFoldingFeature
की सीमाएं उपलब्ध कराता है.
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
)
}
}
}
WindowAreaSessionCallback
के तौर पर, MainActivity
के इस्तेमाल पर ध्यान दें.
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
है जो आपके लेआउट पर असर डालता है. इसलिए, आपको अपने कॉन्टेंट को दूसरी जगह ले जाना होगा.
- देखें कि
FoldingFeature
HALF_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 का इस्तेमाल करके, उन्हें कैसे अनलॉक किया जा सकता है.
आप अपने Camera ऐप्लिकेशन के लिए, बेहतरीन उपयोगकर्ता अनुभव लागू करने के लिए तैयार हैं.