# Glasses app — notes

### 1. Initialization

#### Glasses SDK

```kotlin
GlassSdkUtils.initSdk()

fun initSdk() {
        // No-op if SDK is already ready
        if (GlassSdk.isReady()) {
            Log.d(TAG, "SDK already initialized")
            return
        }
        GlassSdk.bindSecurityService(Utils.getApp(), object : IServiceConnectionCallback {
            override fun onServiceConnected() {
                // clientId must match the phone app so routing knows which APK to target
                GlassSdk.registerClient("GlassSample", mClientMessageCallback)
            }

            override fun onServiceDisconnected() {
                Log.i(TAG, "onServiceDisconnected: ")
                GlobalData.setSdkInitState(false)
            }

            override fun onBindingDied() {
                Log.i(TAG, "onBindingDied: ")
                GlobalData.reset()

                Scopes.mainScope.launch {
                    delay(4000)
                    Log.i(TAG, "onBindingDied: rebinding service")
                    initSdk() // auto rebind
                }
            }
        })
}
```

#### Phone SDK

```kotlin
private fun initSDK() {
        lifecycleScope.launch {
            // System id + app id — must match glasses settings
            val clientIds = arrayListOf("SecurityPhone", "GlassSample")
            val param = EngineParam(clientIds, envType = EnvType.Companion.PUBLIC)
            mAbsSecurityEngineService.initSDK(param) {
                if (it.isSuccess) {
                    Log.d(TAG, "Phone SDK initialized")
                    GlobalData.setSdkInitState(it.isSuccess)
                    if (it.isSuccess) {
                        DeviceLinkerManager.addSystemInfoMonitor(systemInfoCallback)
                        DeviceLinkerManager.initObserver()
                        autoConnectBt()
                    }
                } else {
                    Log.e(TAG, "Phone SDK init failed")
                }
            }
        }
}
```

### 2. ASR / TTS online

<video src="/02-SDK初始化与语音文本转化.mp4" controls  width="1280x" height="720px" type="video/mp4" alt="SDK init video">Video failed to load</video>

1. Enable online ASR/TTS inside the SDK bootstrap.
2. **One-time onboarding** — obtain **AppId + AK/SK** from sales, then paste them into `UserAuthInfo`.
3. Daily use — open **SendMessageActivity** on glasses (see screenshot):

   ![SendMessageActivity](../../../assets/image-20260226163650087.png)

   - **ASR** — tap the button, speech is captured on glasses, forwarded to the phone, then the cloud, transcripts return to the phone and finally the glasses UI.
   - **TTS** — text is sent to the phone, synthesized in the cloud, audio returns to the glasses speaker.

```kotlin
private fun initSDK() {
        lifecycleScope.launch {
            val clientIds = arrayListOf("SecurityPhone", "GlassSample")

            val userAuthInfo = UserAuthInfo("xxx","xxx")

            val banServiceList: List<NetServiceType>? = arrayListOf(NetServiceType.TranslateService, NetServiceType.RtcService)

            val param = EngineParam(clientIds = clientIds, userAuthInfo = userAuthInfo, banServiceList = banServiceList, envType = EnvType.Companion.PUBLIC)
            mPhoneEngineService.initSDK(param) {
                if (it.isSuccess) {
                    Log.d(TAG, "Phone SDK initialized")
                } else {
                    Log.e(TAG, "Phone SDK init failed")
                }
            }
        }

    }
```

### 3. Screen always on

```kotlin
override fun onPause() {
    super.onPause()
    window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
override fun onResume() {
        super.onResume()
        window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
```

### 4. Paths

- **Sending files**: place files under **public external storage** — private app dirs may lack read access (future releases may relax this).
- **Messages**: include `clientId` from the **phone** side so the correct glasses app receives routing; **not** required from the glasses sender.

```text
# Receiver path on glasses
storage/emulated/0/Download/receiver

# Receiver path on sample phone build
/storage/emulated/0/Android/data/com.rokid.phone/files/receiver/gonglu.png
```

### 5. Wi-Fi via QR scan (glasses)

<video src="/扫一扫连接WIFI.mp4" controls  width="1280x" height="720px" type="video/mp4">Video failed to load</video>

* [Provisioning guide](https://glassar.rokid.com/userguide/rokid-glass/wifi)

* Systems after **Jan 16** bundle the Wi‑Fi scanner; earlier builds need OTA. See the infographic below for OTA steps:

  ![OTA reference](../../../assets/image-20260123155607000.png)

  ###### Adb Wi-Fi toggles

  ```bash
  # Enable Wi-Fi
  adb shell svc wifi enable

  # Disable glasses Wi-Fi
  adb shell svc wifi disable

  # Open system settings (use scrcpy to finish setup)
  adb shell am start -a android.settings.SETTINGS
  ```

### 6. Operating glasses through scrcpy

* Install [scrcpy v3.3.3](https://github.com/Genymobile/scrcpy/releases/tag/v3.3.3) on your PC.

<video src="/眼镜端投屏后进行操作.mp4" controls  width="1280x" height="720px" type="video/mp4">Video failed to load</video>

### 7. Wear detection & temple hinges

```kotlin
	val ACTION_TAKE_STATUS: String = "com.rokid.sprite.ACTION_TAKE_STATUS_CHANGED"
    val ACTION_LEG_STATUS: String = "com.rokid.sprite.ACTION_LEG_STATUS_CHANGED"

	private val eventReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action ?: return

            when (action) {
                ACTION_TAKE_STATUS -> {
                    /** 1 = worn, 0 = off face */
                    if ("1" == intent.extras?.getString("glasses_take_state")) {
                        Log.d(TAG, "----- wearing glasses")
                    } else if ("0" == intent.extras?.getString("glasses_take_state")) {
                        Log.d(TAG, "----- removed glasses")
                    }
                }

                ACTION_LEG_STATUS -> {
                    /** 1 = unfolded, 0 = folded */
                    if ("1" == intent.extras?.getString("glasses_leg_state")) {
                        Log.d(TAG, "----- temples open")
                    } else if ("0" == intent.extras?.getString("glasses_leg_state")) {
                        Log.d(TAG, "----- temples folded")
                    }
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                registerReceiver(eventReceiver, intentFilter, RECEIVER_EXPORTED)
            } else {
                registerReceiver(eventReceiver, intentFilter)
            }
    }


	private val intentFilter = IntentFilter().apply {
        addAction(MyApplication.ACTION_TAKE_STATUS)
        addAction(MyApplication.ACTION_LEG_STATUS)
        priority = 100
    }

	
	override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(eventReceiver)
    }
```

### 8. Temple hardware keys

```kotlin
		const val ORDER_ACTION_BUTTON_CLICK = "com.rokid.glass3.action.button.CLICK"
        const val ORDER_ACTION_BUTTON_DOUBLE_CLICK = "com.rokid.glass3.action.button.DOUBLE_CLICK"
        const val ACTION_BUTTON_DOWN: String = "com.android.action.ACTION_SPRITE_BUTTON_DOWN"
        const val ACTION_BUTTON_UP: String = "com.android.action.ACTION_SPRITE_BUTTON_UP"
        

 	private val mHandler = Handler(Looper.getMainLooper())
    private val mRunnable = Runnable { Log.d(TAG, "long-press fired on temple") }

	private val eventReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action ?: return
            when (action) {
                MyApplication.ORDER_ACTION_BUTTON_CLICK -> {
                    Log.d(TAG, "Single click received")
                    screenShot()
                }

                MyApplication.ORDER_ACTION_BUTTON_DOUBLE_CLICK -> {
                    Log.d(TAG, "Double click received")
                }

                MyApplication.ACTION_BUTTON_DOWN -> {
                    mHandler.postDelayed(mRunnable, 1200)
                    Log.d(TAG, "ACTION_BUTTON_DOWN received")
                }

                MyApplication.ACTION_BUTTON_UP -> {
                    mHandler.removeCallbacks(mRunnable)
                    Log.d(TAG, "ACTION_BUTTON_UP received")
                }
            }
        }
    }

    private val intentFilter = IntentFilter().apply {
        addAction(MyApplication.ORDER_ACTION_BUTTON_CLICK)
        addAction(MyApplication.ORDER_ACTION_BUTTON_DOUBLE_CLICK)
        addAction(MyApplication.ACTION_BUTTON_DOWN)
        addAction(MyApplication.ACTION_BUTTON_UP)
        addAction(MyApplication.ACTION_CLICK)
        addAction(MyApplication.ACTION_DOUBLE_CLICK)
        addAction(MyApplication.ACTION_AI_START)
        priority = 100
    }
```
