১. শুরু করার আগে
ফোল্ডেবল ফোনের বিশেষত্ব কী?
ফোল্ডেবল ডিভাইসগুলো এক প্রজন্মের মধ্যে আসা উদ্ভাবন। এগুলো অনন্য অভিজ্ঞতা প্রদান করে এবং এর সাথে আসে ব্যবহারকারীদের মুগ্ধ করার বিশেষ সুযোগ, যেমন হ্যান্ডস-ফ্রি ব্যবহারের জন্য টেবিলটপ ইউআই-এর মতো স্বতন্ত্র বৈশিষ্ট্য।
পূর্বশর্ত
- অ্যান্ড্রয়েড অ্যাপ তৈরির প্রাথমিক জ্ঞান
- হিল্ট ডিপেন্ডেন্সি ইনজেকশন ফ্রেমওয়ার্ক সম্পর্কে প্রাথমিক জ্ঞান
আপনি যা তৈরি করবেন
এই কোডল্যাবে, আপনি ফোল্ডেবল ডিভাইসের জন্য অপ্টিমাইজ করা লেআউটসহ একটি ক্যামেরা অ্যাপ তৈরি করবেন।

আপনি একটি সাধারণ ক্যামেরা অ্যাপ দিয়ে শুরু করেন যা ডিভাইসের কোনো ভঙ্গির প্রতি সাড়া দেয় না বা উন্নত সেলফির জন্য এর উন্নততর পেছনের ক্যামেরার সুবিধা নেয় না। আপনি সোর্স কোডটি আপডেট করেন যাতে ডিভাইসটি খোলা হলে প্রিভিউটি ছোট ডিসপ্লেতে চলে আসে এবং ফোনটি টেবিলটপ মোডে রাখলেও এটি সাড়া দেয়।
যদিও ক্যামেরা অ্যাপ এই এপিআই-এর জন্য সবচেয়ে সুবিধাজনক ব্যবহার, এই কোডল্যাবে শেখা উভয় বৈশিষ্ট্যই যেকোনো অ্যাপে প্রয়োগ করা যেতে পারে।
আপনি যা শিখবেন
- পসচার পরিবর্তনের প্রতিক্রিয়া জানাতে জেটপ্যাক উইন্ডো ম্যানেজার কীভাবে ব্যবহার করবেন
- আপনার অ্যাপটিকে ফোল্ডেবল ফোনের ছোট ডিসপ্লেতে কীভাবে স্থানান্তর করবেন
আপনার যা যা লাগবে
- অ্যান্ড্রয়েড স্টুডিওর একটি সাম্প্রতিক সংস্করণ
- একটি ভাঁজযোগ্য ডিভাইস বা ভাঁজযোগ্য এমুলেটর
২. প্রস্তুত হন
প্রারম্ভিক কোডটি নিন
- আপনার যদি Git ইনস্টল করা থাকে, তাহলে আপনি সরাসরি নিচের কমান্ডটি চালাতে পারেন। Git ইনস্টল করা আছে কিনা তা পরীক্ষা করতে, টার্মিনাল বা কমান্ড লাইনে
git --versionটাইপ করুন এবং যাচাই করুন যে এটি সঠিকভাবে কার্যকর হচ্ছে।
git clone https://github.com/android/large-screen-codelabs.git
- ঐচ্ছিক: আপনার যদি গিট (Git) না থাকে, তাহলে এই কোডল্যাবের সমস্ত কোড ডাউনলোড করতে আপনি নিম্নলিখিত বোতামটি ক্লিক করতে পারেন:
প্রথম মডিউলটি খুলুন
- অ্যান্ড্রয়েড স্টুডিওতে,
/step1এর অধীনে প্রথম মডিউলটি খুলুন।

আপনাকে যদি গ্রেডলের সর্বশেষ সংস্করণ ব্যবহার করতে বলা হয়, তবে তা আপডেট করে নিন।
৩. দৌড়ান এবং পর্যবেক্ষণ করুন
-
step1মডিউলে কোডটি চালান।
যেমনটা দেখতে পাচ্ছেন, এটি একটি সাধারণ ক্যামেরা অ্যাপ। আপনি সামনের ও পেছনের ক্যামেরার মধ্যে পরিবর্তন করতে এবং অ্যাস্পেক্ট রেশিও ঠিক করতে পারবেন। তবে, বাম দিক থেকে প্রথম বাটনটি বর্তমানে কোনো কাজ করে না—কিন্তু এটিই হবে রিয়ার সেলফি মোডের প্রবেশপথ।

- এখন, ডিভাইসটিকে অর্ধ-উন্মুক্ত অবস্থায় রাখার চেষ্টা করুন, যেখানে কব্জাটি পুরোপুরি সমতল বা বন্ধ না থেকে একটি ৯০-ডিগ্রি কোণ তৈরি করে।
যেমনটি দেখতে পাচ্ছেন, অ্যাপটি ডিভাইসের বিভিন্ন ভঙ্গির সাথে সাড়া দেয় না এবং তাই লেআউটও পরিবর্তন হয় না, ফলে হিঞ্জটি ভিউফাইন্ডারের মাঝখানেই থেকে যায়।
৪. জেটপ্যাক উইন্ডো ম্যানেজার সম্পর্কে জানুন
Jetpack WindowManager লাইব্রেরিটি অ্যাপ ডেভেলপারদের ফোল্ডেবল ডিভাইসের জন্য অপ্টিমাইজড অভিজ্ঞতা তৈরি করতে সাহায্য করে। এতে FoldingFeature ক্লাসটি রয়েছে, যা একটি ফ্লেক্সিবল ডিসপ্লের ভাঁজ অথবা দুটি ফিজিক্যাল ডিসপ্লে প্যানেলের মধ্যে একটি হিঞ্জের বর্ণনা দেয়। এর এপিআই ডিভাইস সম্পর্কিত গুরুত্বপূর্ণ তথ্যে অ্যাক্সেস প্রদান করে:
- হিঞ্জটি ১৮০ ডিগ্রিতে খোলা থাকলে
state()FLATরিটার্ন করে, অন্যথায়HALF_OPENEDরিটার্ন করে। -
orientation()FoldingFeature.Orientation.HORIZONTALরিটার্ন করে যদিFoldingFeatureপ্রস্থ তার উচ্চতার চেয়ে বেশি হয়; অন্যথায়,FoldingFeature.Orientation.VERTICALরিটার্ন করে। -
bounds()FoldingFeatureএর সীমানা একটিRectফরম্যাটে প্রদান করে।
FoldingFeature ক্লাসে occlusionType() বা isSeparating() এর মতো অতিরিক্ত তথ্য থাকে, কিন্তু এই কোডলেবেলে সেগুলো গভীরভাবে আলোচনা করা হয়নি।
সংস্করণ 1.2.0-beta01 থেকে শুরু করে, লাইব্রেরিটি WindowAreaController ব্যবহার করে, যা একটি API। এটি রিয়ার ডিসপ্লে মোড সক্ষম করে বর্তমান উইন্ডোটিকে পেছনের ক্যামেরার সাথে সারিবদ্ধ ডিসপ্লেতে নিয়ে যায়, যা পেছনের ক্যামেরা দিয়ে সেলফি তোলা এবং অন্যান্য অনেক ব্যবহারের জন্য চমৎকার!
নির্ভরতা যোগ করুন
- আপনার অ্যাপে Jetpack WindowManager ব্যবহার করার জন্য, আপনাকে আপনার মডিউল-স্তরের
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 উভয় ক্লাসই অ্যাক্সেস করতে পারবেন। এগুলো ব্যবহার করে আপনি সেরা ফোল্ডেবল ক্যামেরা অভিজ্ঞতা তৈরি করতে পারেন!
৫. রিয়ার সেলফি মোড বাস্তবায়ন করুন
রিয়ার ডিসপ্লে মোড দিয়ে শুরু করুন।
যে এপিআইটি এই মোডটি সমর্থন করে, সেটি হলো WindowAreaController , যা একটি ডিভাইসের ডিসপ্লে বা ডিসপ্লে এলাকার মধ্যে উইন্ডো স্থানান্তরের তথ্য ও আচরণ সরবরাহ করে।
এর মাধ্যমে আপনি বর্তমানে ব্যবহারযোগ্য WindowAreaInfo এর তালিকা অনুসন্ধান করতে পারবেন।
WindowAreaInfo ব্যবহার করে আপনি WindowAreaSession অ্যাক্সেস করতে পারেন, যা একটি সক্রিয় উইন্ডো এরিয়া ফিচার এবং একটি নির্দিষ্ট WindowAreaCapability.
- আপনার
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()মেথডে সেগুলোকে ইনিশিয়ালাইজ করুন:
ধাপ১/প্রধান কার্যকলাপ.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()ফাংশনটি প্রয়োগ করুন:
ধাপ১/প্রধান কার্যকলাপ.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ফাংশনটি কল করবে:
ধাপ১/ক্যামেরাভিউমডেল.কেটি
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 এর ব্যবহারটি লক্ষ্য করুন।
রিয়ার ডিসপ্লে এপিআই একটি লিসেনার পদ্ধতিতে কাজ করে: যখন আপনি কন্টেন্টটি অন্য ডিসপ্লেতে সরানোর অনুরোধ করেন, তখন আপনি একটি সেশন শুরু করেন যা লিসেনারের onSessionStarted() মেথডের মাধ্যমে ফেরত আসে। যখন আপনি এর পরিবর্তে ভেতরের (এবং বড়) ডিসপ্লেতে ফিরে যেতে চান, তখন আপনি সেশনটি বন্ধ করে দেন এবং onSessionEnded() মেথডে একটি কনফার্মেশন পান। এই ধরনের একটি লিসেনার তৈরি করতে, আপনাকে WindowAreaSessionCallback ইন্টারফেসটি ইমপ্লিমেন্ট করতে হবে।
-
MainActivityডিক্লারেশনটি এমনভাবে পরিবর্তন করুন যাতে এটিWindowAreaSessionCallbackইন্টারফেসটি ইমপ্লিমেন্ট করে:
ধাপ১/প্রধান কার্যকলাপ.kt
class MainActivity : AppCompatActivity(), WindowAreaSessionCallback
এখন, MainActivity এর ভিতরে onSessionStarted এবং onSessionEnded মেথডগুলো ইমপ্লিমেন্ট করুন। সেশনের অবস্থা সম্পর্কে অবহিত হতে এবং সেই অনুযায়ী অ্যাপ আপডেট করতে এই কলব্যাক মেথডগুলো অত্যন্ত দরকারি।
কিন্তু এবার, সরলতার জন্য, ফাংশন বডির মধ্যেই কোনো ত্রুটি আছে কিনা তা পরীক্ষা করুন এবং অবস্থাটি লগ করুন।
ধাপ১/প্রধান কার্যকলাপ.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]")
}
- অ্যাপটি বিল্ড করে চালান। এরপর যদি আপনি আপনার ডিভাইসটি খুলে পেছনের ডিসপ্লে বোতামে ট্যাপ করেন, তাহলে এই ধরনের একটি বার্তা দেখতে পাবেন:

- আপনার কন্টেন্ট বাইরের ডিসপ্লেতে স্থানান্তরিত হতে দেখতে " এখনই স্ক্রিন পরিবর্তন করুন" নির্বাচন করুন!
৬. টেবিলটপ মোড বাস্তবায়ন করুন
এখন আপনার অ্যাপকে ফোল্ড-অ্যাওয়ার করার পালা: ফোল্ডের ওরিয়েন্টেশনের উপর ভিত্তি করে আপনি আপনার কন্টেন্টকে ডিভাইসের হিঞ্জের পাশে বা উপরে সরাবেন। এটি করার জন্য, আপনি FoldingStateActor এর ভিতরে কাজ করবেন, যাতে আপনার কোড Activity থেকে বিচ্ছিন্ন থাকে এবং সহজে পড়া যায়।
এই API-এর মূল অংশটি হলো WindowInfoTracker ইন্টারফেস, যা একটি স্ট্যাটিক মেথড দিয়ে তৈরি করা হয় এবং এর জন্য একটি Activity প্রয়োজন।
step1/CameraCodelabDependencies.kt
@Provides
fun provideWindowInfoTracker(activity: Activity) =
WindowInfoTracker.getOrCreate(activity)
এই কোডটি লেখার প্রয়োজন নেই কারণ এটি আগে থেকেই রয়েছে, কিন্তু WindowInfoTracker কীভাবে গঠিত হয়েছে তা বোঝার জন্য এটি জানা দরকারি।
- যেকোনো উইন্ডো পরিবর্তনের জন্য, আপনার
ActivityরonResume()মেথডে এই পরিবর্তনগুলো শুনুন:
ধাপ১/প্রধান কার্যকলাপ.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() কল করার মাধ্যমে DisplayFeature এ উপলব্ধ সমস্ত তথ্য সম্বলিত WindowLayoutInfo এর একটি Flow সংগ্রহ করতে পারেন।
শেষ ধাপটি হলো এই পরিবর্তনগুলোর প্রতি সাড়া দেওয়া এবং সেই অনুযায়ী কন্টেন্ট সরানো। এই কাজটি আপনি ` 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 হলে, আপনি আপনার কন্টেন্ট ডানদিকে সরাবেন, অন্যথায় এটিকে ফোল্ড পজিশনের উপরে সরাবেন।
- আপনার অ্যাপটি তৈরি করে চালান, এবং তারপর আপনার ডিভাইসটি খুলে টেবিলটপ মোডে রাখুন যাতে বিষয়বস্তুগুলো সেই অনুযায়ী সরে যেতে দেখতে পারেন!
৭. অভিনন্দন!
এই কোডল্যাবে আপনারা ফোল্ডেবল ডিভাইসের কিছু অনন্য বৈশিষ্ট্য, যেমন রিয়ার ডিসপ্লে মোড বা টেবিলটপ মোড এবং জেটপ্যাক উইন্ডো ম্যানেজার ব্যবহার করে কীভাবে সেগুলো আনলক করতে হয়, সে সম্পর্কে শিখেছেন।
আপনি আপনার ক্যামেরা অ্যাপের জন্য চমৎকার ব্যবহারকারী অভিজ্ঞতা বাস্তবায়ন করতে প্রস্তুত।