অডিও গ্লাস ও ডিসপ্লে গ্লাসের জন্য আপনার প্রথম অ্যাক্টিভিটি তৈরি করা

প্রযোজ্য XR ডিভাইস
এইসব XR ডিভাইসের জন্য অভিজ্ঞতা তৈরি করতে এই নির্দেশিকা আপনাকে সাহায্য করে।
অডিও ও
ডিসপ্লে গ্লাস

অডিও গ্লাস ও ডিসপ্লে গ্লাসের জন্য অগমেন্টেড অভিজ্ঞতা আগে থেকেই থাকা Android Activity ফ্রেমওয়ার্ক API-এর উপর ভিত্তি করে তৈরি করা হয় এবং এতে অতিরিক্ত ধারণা অন্তর্ভুক্ত থাকে, যা এইসব গ্লাসের অনন্য দিকগুলিকে সাপোর্ট করে। ডিভাইসে সম্পূর্ণ APK রান করে এমন XR হেডসেটের মতো নয়, অডিও গ্লাস ও ডিসপ্লে গ্লাস একটি ডেডিকেটেড অ্যাক্টিভিটি ব্যবহার করে যা আপনার ফোনের বর্তমান অ্যাপের মধ্যে রান করে। এই অ্যাক্টিভিটি হোস্ট ডিভাইস থেকে গ্লাসে প্রজেক্ট করা হয়।

অডিও গ্লাস ও ডিসপ্লে গ্লাসের জন্য আপনার অ্যাপের অভিজ্ঞতা তৈরি করতে, আপনাকে নতুন প্রজেক্টেড Activity তৈরি করে আপনার বর্তমান ফোন অ্যাপ এক্সটেন্ড করতে হবে। এই অ্যাক্টিভিটি হল গ্লাসে আপনার অ্যাপ লঞ্চ করার প্রধান এন্ট্রি পয়েন্ট। এই পদ্ধতি ডেভেলপমেন্টকে সহজ করে তোলে কারণ আপনি ফোন ও গ্লাসে ব্যবসায়িক লজিক শেয়ার ও আবার ব্যবহার করতে পারেন।

ভার্সন কম্প্যাটিবিলিটি

Jetpack XR SDK-এর জন্য Android SDK-এর মানানসই হওয়ার প্রয়োজনীয়তা চেক করুন।

ডিপেন্ডেন্সি

নিম্নলিখিত অডিও গ্লাস ও ডিসপ্লে গ্লাসের জন্য লাইব্রেরি ডিপেন্ডেন্সি যোগ করুন:

গ্ৰুভি

dependencies {
    implementation "androidx.xr.runtime:runtime:1.1.0-alpha01"
    implementation "androidx.xr.glimmer:glimmer:1.0.0-alpha18"
    implementation "androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha18"
    implementation "androidx.xr.projected:projected:1.0.0-alpha09"
    implementation "androidx.xr.arcore:arcore:1.0.0-beta02"
}

Kotlin

dependencies {
    implementation("androidx.xr.runtime:runtime:1.1.0-alpha01")
    implementation("androidx.xr.glimmer:glimmer:1.0.0-alpha18")
    implementation("androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha18")
    implementation("androidx.xr.projected:projected:1.0.0-alpha09")
    implementation("androidx.xr.arcore:arcore:1.0.0-beta02")
}

আপনার অ্যাপের মেনিফেস্টে অ্যাক্টিভিটি ঘোষণা করা

অন্যান্য অ্যাক্টিভিটির মতোই, সিস্টেমকে দেখানোর ও চালানোর জন্য আপনাকে নিজের অ্যাপের ম্যানিফেস্ট ফাইলে অ্যাক্টিভিটি ঘোষণা করতে হবে।

<application>
  <activity
      android:name="com.example.xr.projected.ProjectedMainActivity"
      android:exported="true"
      android:requiredDisplayCategory="android.hardware.display.category.XR_PROJECTED"
      android:label="Example activity for audio glasses and display glasses">
      <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.XR_PROJECTED_LAUNCHER"/>
      </intent-filter>
  </activity>
</application>

কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট

  • সিস্টেমকে জানাতে android.hardware.display.category.XR_PROJECTED নির্দিষ্ট করে যে android:requiredDisplayCategory অ্যাট্রিবিউট এটি একটি প্রজেক্ট করা অ্যাক্টিভিটি এবং এটি অডিও ও ডিসপ্লে গ্লাসে প্রজেক্ট করা যেতে পারে।
  • android.intent.action.MAIN এই অ্যাক্টিভিটিকে ডিফল্ট লঞ্চ অ্যাক্টিভিটি হিসেবে সেট করে।
  • android.intent.category.XR_PROJECTED_LAUNCHER হল একটি বিশেষ বিভাগ যা আপনার প্রজেক্ট করা অ্যাক্টিভিটিকে Gemini ভয়েস কমান্ডের মাধ্যমে খুঁজে পাওয়ার উপযুক্ত করে তোলে।

    কোনও ব্যবহারকারী অ্যাপের নাম ব্যবহার করে ভয়েস কমান্ড দিলে (যেমন, "AI ক্যাটালগ স্যাম্পেল খোলো", "AI ক্যাটালগ স্যাম্পেল লঞ্চ করো" বা "AI ক্যাটালগ স্যাম্পেল শুরু করো"), অডিও বা ডিসপ্লে গ্লাসে নির্দিষ্ট অ্যাক্টিভিটি লোকেট ও শুরু করতে সিস্টেম এই বিভাগটি ব্যবহার করে।

আপনার অ্যাক্টিভিটি তৈরি করা

এরপরে, আপনি একটি ছোট অ্যাক্টিভিটি তৈরি করবেন যা ডিসপ্লে চালু থাকাকালীন AI গ্লাসে কিছু দেখাতে পারে।

@OptIn(ExperimentalProjectedApi::class)
class ProjectedMainActivity : ComponentActivity() {

    private var displayController: ProjectedDisplayController? = null
    private var isVisualUiSupported by mutableStateOf(false)
    private var areVisualsOn by mutableStateOf(true)
    private var isPermissionDenied by mutableStateOf(false)

    @Suppress("DEPRECATION")
    // TODO: Remove suppression once Android Emulator supports ProjectedActivityCompat.requestPermissions()
    // Register the permissions launcher using the ProjectedPermissionsResultContract.
    private val requestPermissionLauncher: ActivityResultLauncher<List<ProjectedPermissionsRequestParams>> =
        registerForActivityResult(ProjectedPermissionsResultContract()) { results ->
            if (results[Manifest.permission.CAMERA] == true) {
                isPermissionDenied = false
                initializeGlassesFeatures()
            } else {
                // Handle permission denial.
                isPermissionDenied = true
            }
        }

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

        lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onDestroy(owner: LifecycleOwner) {
                displayController?.close()
                displayController = null
            }
        })

        if (hasCameraPermission()) {
            initializeGlassesFeatures()
        } else {
            requestHardwarePermissions()
        }

        setContent {
            GlimmerTheme {
                HomeScreen(
                    areVisualsOn = areVisualsOn,
                    isVisualUiSupported = isVisualUiSupported,
                    isPermissionDenied = isPermissionDenied,
                    onRetryPermission = { requestHardwarePermissions() },
                    onClose = { finish() }
                )
            }
        }
    }

    private fun initializeGlassesFeatures() {
        lifecycleScope.launch {
            // Check device capabilities
            val projectedDeviceController = ProjectedDeviceController.create(this@ProjectedMainActivity)
            isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI)

            val controller = ProjectedDisplayController.create(this@ProjectedMainActivity)
            displayController = controller
            val observer = GlassesLifecycleObserver(
                context = this@ProjectedMainActivity,
                controller = controller,
                onVisualsChanged = { visualsOn -> areVisualsOn = visualsOn }
            )
            lifecycle.addObserver(observer)
        }
    }

    private fun hasCameraPermission(): Boolean {
        return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
                PackageManager.PERMISSION_GRANTED
    }

    private fun requestHardwarePermissions() {
        val params = ProjectedPermissionsRequestParams(
            permissions = listOf(Manifest.permission.CAMERA),
            rationale = "Camera access is required to overlay digital content on your physical environment."
        )
        requestPermissionLauncher.launch(listOf(params))
    }
}

কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট

কম্পোজেবল প্রয়োগ করা

আপনার তৈরি করা অ্যাক্টিভিটি একটি HomeScreen কম্পোজ করা যায় এমন ফাংশনকে রেফার করে যা আপনাকে প্রয়োগ করতে হবে। নিচের কোডটি Jetpack Compose Glimmer ব্যবহার করে এমন একটি কম্পোজ করার উপযুক্ত আইটেমকে সংজ্ঞায়িত করে যা গ্লাসের ডিসপ্লেতে কিছু টেক্সট দেখাতে পারে:

@Composable
fun HomeScreen(
    areVisualsOn: Boolean,
    isVisualUiSupported: Boolean,
    isPermissionDenied: Boolean,
    onRetryPermission: () -> Unit,
    onClose: () -> Unit,
    modifier: Modifier = Modifier
) {
    Box(
        modifier = modifier
            .surface()
            .focusable(false)
            .fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        if (isPermissionDenied) {
            ActionCard(
                title = { Text("Permission Required") },
                action = { Button(onClick = onClose) { Text("Exit") } }
            ) {
                Text("Camera access is needed to use display glasses features.")
                Button(onClick = onRetryPermission) { Text("Retry") }
            }
        } else if (isVisualUiSupported) {
            ActionCard(
                title = { Text("Android XR") },
                action = {
                    Button(onClick = onClose) {
                        Text("Close")
                    }
                }
            ) {
                if (areVisualsOn) {
                    Text("Hello, Display Glasses!")
                } else {
                    Text("Display is off. Audio guidance active.")
                }
            }
        } else {
            Text("Audio Guidance Mode Active")
        }
    }
}

কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট

  • আপনি আগে অ্যাক্টিভিটিতে যেভাবে বর্ণনা করেছেন, HomeScreen ফাংশনে কম্পোজ করা যায় এমন কন্টেন্ট অন্তর্ভুক্ত থাকে যা গ্লাসের ডিসপ্লে চালু থাকলে ব্যবহারকারী দেখতে পান।
  • Jetpack Compose Glimmer Text কম্পোনেন্ট, গ্লাসের ডিসপ্লেতে "হ্যালো, AI গ্লাস!" টেক্সট দেখায়।
  • প্রজেক্ট করা অ্যাক্টিভিটিতে Jetpack Compose Glimmer Button onClose-এর মাধ্যমে finish() কল করে অ্যাক্টিভিটি বন্ধ করে দেয়।

অডিও বা ডিসপ্লে গ্লাস কানেক্ট করা আছে কিনা তা চেক করুন

আপনার অ্যাক্টিভিটি লঞ্চ করার আগে কোনও ব্যবহারকারীর অডিও বা ডিসপ্লে গ্লাস তার ফোনের সাথে কানেক্ট করা আছে কিনা তা নির্ধারণ করতে, ProjectedContext.isProjectedDeviceConnected পদ্ধতি ব্যবহার করুন। এই পদ্ধতিটি Flow<Boolean> রিটার্ন করে যা আপনার অ্যাপ কানেকশন স্ট্যাটাস সম্পর্কে রিয়েল-টাইম আপডেট পেতে পর্যবেক্ষণ করতে পারে।

আপনার অ্যাক্টিভিটি শুরু করা

আপনি যেহেতু একটি প্রাথমিক অ্যাক্টিভিটি তৈরি করেছেন, তাই এখন এটি আপনার গ্লাসে লঞ্চ করতে পারবেন। গ্লাসের হার্ডওয়্যার অ্যাক্সেস করতে, আপনার অ্যাপকে অবশ্যই নির্দিষ্ট বিকল্পের সাথে অ্যাক্টিভিটি শুরু করতে হবে যা সিস্টেমকে প্রজেক্ট করা প্রসঙ্গ ব্যবহার করতে বলে, যেমনটি নিম্নলিখিত কোডে দেখানো হয়েছে:

val options = ProjectedContext.createProjectedActivityOptions(context)
val intent = Intent(context, ProjectedMainActivity::class.java)
context.startActivity(intent, options.toBundle())

ProjectedContext-এর createProjectedActivityOptions পদ্ধতি প্রজেক্ট করা কন্টেক্সটে আপনার অ্যাক্টিভিটি শুরু করার জন্য প্রয়োজনীয় বিকল্প তৈরি করে। context প্যারামিটারটি ফোন বা গ্লাস ডিভাইস থেকে নেওয়া কোনও প্রসঙ্গ হতে পারে।

পরবর্তী ধাপ

অডিও গ্লাস ও ডিসপ্লে গ্লাসের জন্য প্রথম অ্যাক্টিভিটি তৈরি করার পরে, এর কার্যকারিতা বাড়ানোর অন্যান্য উপায় এক্সপ্লোর করুন: