مهندس روابط توسعهدهنده
ازآنجاییکه کاربران بین تلفنها، دستگاههای پوشیدنی، و لوازم جانبی صوتی جابهجا میشوند، ارائه تجربههای بیندستگاهی روان برای تعامل ضروری شده است. بااینحال، تکیه بر رویکردهای شبکهسازی استاندارد اغلب منجر به خالی شدن باتری، جهشهای تأخیر، و جلسههای کاربری تکهتکه میشود.
این بررسی عمیق میاناهای برنامهسازی کاربردی تخصصی Android را معرفی میکند که برای ارتباط چنددستگاهی ساخته شدهاند و الگوهای کارآمد ازنظر مصرف حافظه و انرژی ارائه میدهند تا انتقال بین دستگاهها فوری، قابلاعتماد، و شهودی باشد.
آوانویسی
سلام به همه.
به «فعال کردن تعاملات بیندستگاهی یکپارچه» خوش آمدید.
من هانروی هستم و امروز همکارانم، یاسین و ناتاراج، به من ملحق شدهاند.
وقتی درباره اتصالپذیری صحبت میکنیم، آنچه اغلب به ذهن میرسد پیوند شبکه پایه یا هدستهای بلوتوث است.
در Android، اتصال بسیار گستردهتر از این است.
هدف اصلی این است که دستگاهها با یکدیگر بهتر کار کنند.
امروز، سناریوهای رایج بیندستگاهی را بررسی میکنیم و به شما نشان میدهیم که کدام میانای برنامهسازی کاربردی برای هرکدام مناسب است.
برای مدتی طولانی، ما برنامهها را فقط برای یک دستگاه، یعنی تلفن، میساختیم.
سپس پوشیدنیها محبوب شدند.
بهزودی داشتن ساعت هوشمند جفتشده با تلفن، همگامسازی دادهها در پسزمینه، یا پاسخ دادن مستقیم به تماسها بسیار رایج شد.
افزودن هدفون… افزودن هدفون توگوشی بیسیم.
اکنون کاربران میتوانند در اواسط تماس، دستگاه صوتی را از ساعت به تلفن، هدفون توگوشی، یا خودرو تغییر دهند.
اخیراً، بسیاری از کاربران دستگاه دومی با صفحهنمایش بزرگ، مانند دستگاه تاشو یا رایانه لوحی، نیز همراه خود دارند.
آنها انتظار دارند برنامه در این دستگاهها بهطور روان کار کند.
و این امر به رایانههای کیفی و Chromebookها نیز گسترش مییابد، جایی که کاربر میتواند برنامههای Android را اجرا کند و مستقیماً از میز کار خود متصل بماند.
حتی در سال ۲۰۲۳، یک کاربر معمولی بین سه تا چهار دستگاه داشت.
کاربران از این دستگاهها چیزی بیشتر از عملکرد مستقل انتظار دارند.
آنها بهدنبال تجربه فوقالعادهای از «باهم بهتر» هستند.
این تجربه «باهم بهتر بودن» بهویژه زمانی واضح است که افراد در یک اتاق باشند، مثل ما امروز، و بخواهند راهی آسان برای همرسانی عکسها، پیوندها، یا برنامهها با افراد کنارشان داشته باشند.
با نگاهی به اعداد، بومسازگان بسیار بزرگ است و بهسرعت درحال رشد است.
در این سال، حدود ۲۲ میلیارد دستگاه اینترنت اشیا در سراسر جهان وجود دارد که سالبهسال ۱۴٪ رشد میکند.
و در میان همه این نقاط پایانی، Wi-Fi یک سوم و بلوتوث یک چهارم را تشکیل میدهد.
اتصال بیندستگاهی دیگر یک مورد حاشیهای نیست.
این بخشی از زندگی روزمره ما است.
بااینحال، امروزه اکثر برنامهها هنوز ویژگیهای بیندستگاهی خود را بر روی میاناهای برنامهسازی کاربردی بلوتوث و شبکه سطح پایین میسازند که این امر سه چالش ایجاد میکند.
ابتدا، باتری و حافظه.
فعال نگه داشتن اسکنها و سرویسهای پسزمینه باعث تخلیه باتری و حافظه میشود.
دوم، تأخیر.
تأخیرهای غیرقابلپیشبینی باعث اختلال در تماسهای همزمان و همگامسازی دادهها میشود.
و سوم، اصطکاک اتصال.
مدیریت کردن شناسایی، مرتبطسازی، و اتصال مجدد در همه دستگاهها پیچیده است و بهراحتی میتواند به تجربه کاربری خراب منجر شود.
برای رهایی از این دردسرها، میاناهای برنامهسازی کاربردی هدفساختهای را در سه حوزه اصلی ایجاد کردهایم تا ویژگیهای بیندستگاهی را واقعاً ساده کنیم.
من با برنامههای همراه شروع میکنم، سپس ناتاراج برنامههای VoIP را پوشش میدهد، و بعد یاسین درباره همرسانی همتا به همتا صحبت خواهد کرد.
بیایید ابتدا نگاهی به برنامههای همراه بیندازیم.
برنامه همراه برنامهای است که در تلفنتان نصب میکنید تا دستگاه همپا، مثل ساعت هوشمند یا حسگر گلوکز، بتواند بهخوبی با آن کار کند.
در این بخش، بر دستگاههای متصلشده ازطریق بلوتوث تمرکز خواهیم کرد.
معمولاً چهار دسته دستگاه همپا وجود دارد.
اول، دستگاههای پزشکی، مثل حسگرهای گلوکز و پایشگرهای علائم حیاتی.
و پوشیدنیها، مثل ساعت هوشمند و مچبند تناسب اندام.
هدست، با «جفتسازی سریع» و کنترلهای هوشمند.
و کیتهای خودرو، برای مواردی مثل کلیدهای دیجیتالی خودرو و تماس دستآزاد.
هر دسته مسیرهای کاربر کلیدی خودش را دارد، اما همه آنها به اتصال مطمئن بین دستگاه و تلفن وابسته هستند.
برای دستگاههای پزشکی، این اتصال حتی میتواند برای زندگی حیاتی باشد.
بیایید چهار نیاز رایج را که در این دستهها میبینیم بررسی کنیم.
اول، مدیریت دستگاه.
برنامه باید دستگاه درست را از میان همه دستگاههای اطراف پیدا کند، مثلاً ساعت را در اینجا، نه هدفون توگوشی یا بلندگو، و سپس جریان مرتبطسازی را برای اتصال شروع کند.
دوم، بررسی حضور.
برنامه باید بداند چه زمانی دستگاه در اطراف است و چه زمانی دور شده است.
و وقتی دستگاه ظاهر میشود، برنامه باید راهی برای بیدار شدن داشته باشد تا بتواند کارش را انجام دهد، حتی اگر کاربر آن را باز نکرده باشد.
سوم، حفظ اتصال.
پساز اتصال، برنامهها میخواهند اتصال قابلاطمینان و پایدار باشد.
اگر اتصال قطع شود، مثلاً کاربر از محدوده خارج شود و سپس برگردد، برنامه باید بهطور خودکار دوباره متصل شود، حتی اگر در آن زمان درحال اجرا نباشد.
و چهارم، همگامسازی و انتقال دادهها.
ارزش واقعی در اینجا است.
همگامسازی خوانشهای حسگر، ارسال بهروزرسانیهای سفتافزار، یا ارسال تنظیمات به دستگاه.
این موضوع میتواند برای دستگاههای پزشکی حیاتی باشد.
ازدست رفتن خوانش میتواند بهمعنای ازدست رفتن هشدار باشد.
همه اینها به این نیاز دارند که فرایند برنامه در زمان مناسب فعال و متصل باشد.
بنابراین، برنامهها چگونه همه این نیازها را برآورده کردهاند؟
بهطور سنتی، برنامهها بهشدت به سرویس پیشزمینهای متکی بودند.
در اینجا یک پیادهسازی معمول آورده شده است.
اگر قبلاً برنامه همراهی ساخته باشید، این ممکن است آشنا بهنظر برسد.
اجازههای اسکن و اتصال بلوتوث، اجازههای سرویس پیشزمینهای، و نوع سرویس پیشزمینهای دستگاه متصلشده را اعلام میکنید و سپس اجازه اعلان را اعلام میکنید زیرا سرویس پیشزمینهای باید اعلان ثابتی را نشان دهد.
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />سپس سرویس چسبندهای را اجرا میکنید تا فرایند برنامه را فعال نگه دارید.
class BleForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
…
return START_STICKY
}
}میدانید، این روش کار میکند، اما باتری و حافظه مصرف میکند چون سرویس همیشه درحال اجرا است، حتی وقتی دستگاه در این اطراف نباشد.
بههمین دلیل است که «مدیر دستگاه همپا» یا بهاختصار CDM را ساختهایم.
اول اینکه میتواند برنامه شما را براساس حضور دستگاه بیدار کند و تا زمانی که دستگاه در محدوده باقی بماند، سیستم فرایند برنامه شما را فعال نگه میدارد.
به سرویس پیشزمینهای نیاز نیست.
دوم، CDM از نمایههای دستگاه پشتیبانی میکند.
با نمایه دستگاه، کاربر همه اجازههایی را که برنامهتان نیاز دارد با یک تکضرب بهجای زنجیرهای از کادرهای گفتگوی اجازه اعطا میکند.
این کار تجربه کاربری و نرخ تبدیل را بهطور قابلتوجهی بهبود میبخشد.
نمایه ساعت از Android 12 و نمایه عینک از Android 14 دردسترس بوده است.
در Android 17، دو نمایه جدید بهطور خاص برای دستگاههای پزشکی و پایشگرهای تناسب اندام اضافه کردهایم.
امروزه همچنین برخی نمایههای ممتاز فقط برای برنامههای سیستم وجود دارد.
اگر نیاز گستردهتری ازسوی بومسازگان احساس کنیم، ممکن است آنها را برای توسعهدهندگان بیشتری دردسترس قرار دهیم.
استفاده از CDM واقعاً آسان است.
بیایید از یک دستگاه پزشکی بهعنوان نمونه استفاده کنیم.
ابتدا، اجازههای لازم را اعلام کنید: یکی برای دستگاه پزشکی، یکی برای اجرا در پسزمینه، و یکی برای مشاهده حضور دستگاه.
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_MEDICAL" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" />یک نکته قابلتوجه این است که برخلاف اجازههایی که در پیادهسازی سرویس پیشزمینهای دیدیم، همه اجازههای فهرستشده در اینجا اجازههای معمولی هستند، یعنی در زمان نصب اعطا میشوند و کادر گفتگوی درخواست نمایش داده نمیشود.
تنها چیزی که کاربر باید تأیید کند، ارتباط است.
بیایید ببینیم چطور میتوانیم این منسوبسازی را ایجاد کنیم.
در اینجا درخواست ارتباط را با فیلتر دستگاه میسازیم تا فقط دستگاه درست نشان داده شود.
val paringRequest = AssociationRequest.Builder()
.addDeviceFilter(deviceFilter)
.setSingleDevice(true)
.setDeviceProfile(AssociationRequest.DEVICE_PROFILE_MEDICAL)
.build()
companionDeviceManager.associate(paringRequest, ContextCompat.getMainExecutor(this),
object : CompanionDeviceManager.Callback() {
override fun onAssociationPending(intentSender: IntentSender) {
intentSenderLauncher.launch(IntentSenderRequest.Builder(intentSender).build())
}
override fun onAssociationCreated(associationInfo: AssociationInfo) { … }
override fun onFailure(error: CharSequence?) { … }
}
)setSingleDevice را روی true تنظیم کنید چون فقط یک مطابقت میخواهیم، و نمایه را روی medical تنظیم کنید.
سپس با همکار تماس بگیرید.
CDM اسکن را انجام میدهد.
در onAssociationPending، IntentSender دریافت خواهیم کرد.
آن را راهاندازی کنید، سیستم کادر گفتگوی ارتباط را نشان میدهد.
با نمایه پزشکی، این تکضربهای که اینجا میزنید نهتنها خود ارتباط را تأیید میکند، بلکه همه اجازههایی را که نمایه نیاز دارد اعطا میکند، حتی «مزاحم نشوید» را دور میزند که برای هشدارهای بحرانی ضروری است.
وقتی کاربر روی «مجاز کردن» تکضرب بزند، ما به onAssociationCreated با AssociationInfo میرسیم که شامل یک شناسه ارتباط است.
سپس میتوانیم حضور دستگاه را بررسی کنیم-- سپس میتوانیم با فراخوانی startObservingDevicePresence با آن شناسه مرتبطسازی، حضور دستگاه را بررسی کنیم.
و همچنین باید سرویسی را پیادهسازی کنید که CompanionDeviceService را گسترش دهد.
class BleCompanionDeviceService : CompanionDeviceService() {
override fun onDevicePresenceEvent(event: DevicePresenceEvent) {
when (event.event) {
DevicePresenceEvent.EVENT_BLE_APPEARED -> onDeviceAppeared(event.associationId)
DevicePresenceEvent.EVENT_BLE_DISAPPEARED -> onDeviceDisappeared(event.associationId)
}
}
private fun onDeviceAppeared(associationId: Int) { … }
private fun onDeviceDisappeared(associationId: Int) { … }
}وقتی دستگاه در محدوده قرار میگیرد، سیستم به سرویس شما متصل میشود، حتی اگر برنامه شما درحال اجرا نباشد.
و سیستم با onDevicePresenceEvent با EVENT_BLE_APPEARED تماس خواهد گرفت.
سپس میتوانید متصل شوید و همگامسازی کنید.
وقتی دستگاه از آنجا خارج شود، EVENT_BLE_DISAPPEARED دریافت خواهید کرد.
سپس میتوانید پاکسازی را انجام دهید.
بدون سرویس همیشه روشن و بدون اعلان فقط برای فعال ماندن.
اکنون باید اعتراف کنیم که CDM چند محدودیت دارد که توسعهدهندگان باید آنها را درنظر داشته باشند.
ابتدا، نسخههای سیستمعامل.
بیشتر ویژگیهای CDM در Android 12 و 13 ارائه شده است.
اما برخی بهبودهای جالب در Android 16 و نمایههای جدید در Android 17 ارائه شد.
بنابراین، نسخه سیستمعامل را در زمان اجرا بررسی کنید و یک گزینه جایگزین برای نسخههای قدیمیتر نگه دارید.
بااینحال، «کیت توسعه نرمافزار هدف» هر سال افزایش مییابد و «حداقل کیت توسعه نرمافزار» معمولاً از آن پیروی میکند.
برای مقابله با فشار حافظهای که برنامهها اکنون با آن مواجه هستند و در آینده مواجه خواهند شد، اکیداً توصیه میکنیم برنامهها در اسرع وقت از سرویس پیشزمینهای به CDM منتقل شوند.
و اگر مورد استفادهای دارید که درحالحاضر CDM نمیتواند از آن پشتیبانی کند، به ما اطلاع دهید.
دوم اینکه درحالحاضر CDM از اسکن پسزمینه پشتیبانی میکند، اما از تبلیغات پسزمینه پشتیبانی نمیکند.
بهندرت میشنویم که به این ویژگی نیاز باشد.
اما اگر برنامهتان نیاز دارد که تلفن بهعنوان دستگاه جانبی عمل کند و بهطور مداوم در پسزمینه تبلیغ کند، خوشحال میشویم اطلاعات بیشتری درباره آن کسب کنیم.
سوم، CDM از حالت اسکن کممصرف استفاده میکند.
این حالت کمی از پاسخگویی همزمان را با باتری و حافظه معاوضه میکند.
بنابراین، انتظار بیدار شدن فوری را نداشته باشید.
درنهایت، هیچگونه سرک کشیدن به بار مفید وجود ندارد.
نمیتوانید برای مشخصه یا دادههای تبلیغاتی خاصی ثبتنام کنید.
اما وقتی دستگاه در محدوده قرار میگیرد، برنامه شما بیدار میشود.
بنابراین، میتواند متصل شود و محموله را درصورت نیاز بررسی کند.
این همه اطلاعات درباره برنامههای همراه و CDM بود.
I'll hand it over to Nataraj for VoIP apps.
اگر برنامهتان دستگاه جانبی اختصاصی دارد و بهطور مداوم با آن ارتباط برقرار میکند، CompanionDeviceManager ابزار مناسبی است.
سلام به همه.
من ناتاراج کی آر هستم.
اکنون درباره سناریو بعدی بیندستگاهی صحبت خواهم کرد که تماس همزمان است.
امروزه، نحوه مدیریت تماسها توسط افراد درحال تغییر است.
بیشاز ۴۰٪ تماسها ازطریق شبکههای VoIP برقرار میشود.
و Android نقش مهمی در این امر ایفا میکند.
درواقع، ۷۱٪ از این تماسها در دستگاههای Android برقرار میشود، یعنی تقریباً یک تماس درمیان.
فقط تماسها نیست.
بومسازگان دستگاههایی که مردم دارند نیز بهطور گستردهای درحال رشد است.
بیشاز ۲۵۰ میلیون خودرو با Android Auto در جادهها درحال حرکت هستند و ما همچنین بیشاز یک میلیارد دستگاه بلوتوث برای کاربران ارسال میکنیم.
هدفونهای توگوشی، ساعتهای Wear OS، خودروهای Android با Android Auto، و بلندگوهای بلوتوثی همه دستگاههایی هستند که کاربران در اطراف تلفن خود دارند و میخواهند تماسهایشان بهطور یکپارچه در همه این دستگاهها جریان داشته باشد.
برای مثال، کاربر تماسی دریافت میکند و برای اطلاع از اطلاعات تماسگیرنده، نگاهی به ساعتش میاندازد.
او تماس را در ساعتش پاسخ میدهد.
آنها درصورت لزوم صدا را قطع یا وصل میکردند، هدفونهای توگوشی را در گوششان میگذاشتند، و به تماسشان ادامه میدادند.
اگر بخواهند بیرون بروند، وارد تماسشان میشوند-- وارد خودروشان میشوند، و انتظار دارند تماسشان بهطور یکپارچه به کابین خودرو منتقل شود.
بنابراین، این کار به روالی عادی برای کاربر تبدیل شده است و این تجربه ممتازی نیست.
این یک الزام اساسی برای کاربر است و ما توسعهدهندگان Android وظیفه داریم این تجربه یکپارچه را در برنامههای VoIP خود به کاربران ارائه دهیم.
اما اگر بخواهید همه اینها را خودتان از ابتدا بسازید، درنهایت با سه مشکل عمده مواجه خواهید شد.
یکی تکهتکهسازی با سوکتهای بلوتوث است.
چون سیستم نمیداند که تماس شما درحال مدیریت تماس است، درنهایت سوکتهای بلوتوث سفارشی و جلسههای رسانه سفارشی میسازید تا تماستان بتواند روی ساعت یا Android Auto نمایش داده شود.
مشکل دوم تمرکز صوتی است.
چون باید با میاناهای برنامهسازی کاربردی AudioManager سطح پایین و میاناهای برنامهسازی کاربردی بلوتوث سروکار داشته باشید، نشت صدا اتفاق میافتد و این برای سیستم اولویت ندارد، درست است؟
برنامه شما تماس VoIP دارد، بنابراین هرگاه تماس شرکت مخابراتی برقرار شود، تماس را قطع میکند و مودم صدا را میگیرد و برنامه شما بدون صدا میماند.
بدتر از آن، هر زمان که تماس حتی اگر تماس تلفنی تمام شود، تمرکز صوتی به برنامه شما برگردانده نمیشود.
و مشکل سوم همگامسازی است.
کاربر روی بیصدا کردن در ساعت تکضرب میزد، اما واسط کاربر تلفن همچنان تماس را بهعنوان صدادار نشان میداد.
این کار باعث سردرگمی زیادی میشود.
کاربر تماس را از ستون فرمان خودرو قطع میکند، اما تلفن همچنان اعلان تماس درحال انجام را نشان میدهد.
وقتی این اتفاق میافتد، کاربر واقعاً ناامید میشود.
بنابراین، برای حل دقیق همه این مشکلات، Jetpack Telecom را ساختهایم.
بنابراین، تماس خود را یکبار بااستفاده از CallsManager در Jetpack Telecom ثبت میکنید و Jetpack Telecom بهعنوان هماهنگکننده مرکزی تماس سیستم عمل میکند.
این سرویس مسئولیت نمایش تماس شما را در ساعت، Android Auto، و دیگر سطوح ازراهدور متعلق به کاربر برعهده دارد.
و همچنین مدیریت تمرکز صوتی را برعهده میگیرد.
و مهمتر از همه، همه اینها بهطور یکپارچه تا Android 8 کار میکند.
انتقال تماس به ساعت یا صفحهنمایش خودرو فقط نیمی از داستان است، درست است، زیرا کاربر بدیهی است که میخواهد وضعیت تماس را از این دستگاهها کنترل کند.
آنها میخواهند تماس را از ساعتشان بیصدا کنند یا درصورت نیاز آن را باصدا کنند.
آنها میخواهند تماس را از ستون فرمان خودرو یا با دو بار تکضرب زدن روی هدفون توگوشی قطع کنند.
بنابراین، Jetpack Telecom به این رویدادهای سختافزاری گوش میدهد و آنها را بهعنوان فراخوانهای برگشتی در یک مکان ارائه میدهد که همان CallControlScope است.
کافی است به این فراخوانیهای برگشتی گوش دهید، موتور تماس خود را مدیریت کنید، و وضعیت تماس را مطابق با آن بهروز کنید.
و Jetpack Telecom مسئولیت همگامسازی این وضعیت تماس بین دستگاهها را برعهده میگیرد.
بنابراین، در پشت صحنه، کتابخانه مزایا را در دو دسته ارائه میدهد.
یکی اینکه، بدیهی است که همه مشکلاتی را که دربارهشان صحبت کردیم حل میکند.
بنابراین، بهجای اینکه شما با میاناهای برنامهسازی کاربردی AudioManager سطح پایین سروکار داشته باشید، کتابخانه نقاط پایانی تماس اعلانی ارائه میدهد، اولویت صوتی را مدیریت میکند، و بین دستگاهها همگامسازی میکند.
این سرویس مسئولیت انتقال تماس بین دستگاهها را برعهده دارد.
و همچنین هر زمان که تماس تلفن همراه برقرار شود، به آن رسیدگی میکند.
اما مزیت دیگری که این کتابخانه دارد این است که چون این کتابخانه بر Kotlin ساخته شده است، هروقت تماسی با Jetpack Telecom اضافه میکنید، یک محدوده همروال Kotlin باز میشود.
بنابراین، از روالهای مشترک Kotlin بهره میبرد و این روال مشترک Kotlin دقیقاً تا پایان مدت تماس فعال میماند.
بنابراین، هر کاری که دررابطهبا تماس اجرا کردهاید بهمحض قطع شدن تماس بهطور خودکار پاک میشود.
بنابراین، هیچ کار سرکشی باقی نمیماند و چرخه حیات تماس نیز توسط کتابخانه مدیریت میشود.
بنابراین، اکنون، شما تعجب خواهید کرد که این کتابخانه عالی عالی است و میخواهید آن را پیادهسازی کنید، درست است؟
و به همین سادگی است.
مرحله ۱ این است که باید وابستگی را بهوضوح اعلام کنید.
پساز آن، اجازه لازم را که MANAGE_OWN_CALLS است اعلام کنید و سپس، یک شیء CallsManager را نمونهسازی کنید.
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />پساز نمونهسازی شیء CallsManager، برنامهتان را با قابلیتهایی که میخواهید با برنامه ارائه دهید، مانند تماس تصویری یا جاریسازی تماس، ثبت کنید.
// Initialize CallsManager & register supported capabilities once
val callsManager = CallsManager(context)
callsManager.registerAppWithTelecom(
capabilities = CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING or
CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING
)پساز آن، مرحله شماره ۲.
اکنون که برنامهتان را در Jetpack Telecom ثبت کردهاید، وقت آن است که تماسهای ورودی و خروجی را مدیریت کنید.
برای این کار، باید شیء CallAttributesCompat را بسازید که فراداده تماس را نگه میدارد، مانند displayName تماسگیرنده یا تماسگیرنده، نشانی، که نقطه پایانی تماسگیرنده است، جهت، مشخص کردن اینکه تماس ورودی است یا خروجی تا Jetpack Telecom بتواند واسط کاربر مربوطه را در این سطوح ازراهدور ارائه دهد، و سپس، callType، اینکه تماس صوتی است یا تصویری تا بتوانید منابع موردنیاز را دریافت کنید.
val attributes = CallAttributesCompat(
displayName = "Kukk Nat",
address = Uri.parse("tel:+16505550199"),
direction = CallAttributesCompat.DIRECTION_INCOMING,
callType = CallAttributesCompat.CALL_TYPE_AUDIO_CALL
)
callsManager.addCall(
attributes, onAnswer, onDisconnect, onSetActive, onSetInactive
) {
// Coroutine CallControlScope active for the duration of the call
}وقتی شیء فراداده آماده شد، آن را بااستفاده از روش callsManager.addCall اضافه کنید.
با این کار، یک محدوده همروال کنترل تماس باز میشود.
بنابراین، وقتی افزودن تماس را تمام کردید، حالا مرحله ۳.
مرحله ۳ به کنشهایی گوش میدهد که کاربر قرار است از آن سطوح راه دور انجام دهد.
بنابراین، چون CallControlScope دارید، تنها کاری که باید انجام دهید این است که جریانهای Kotlin را جمعآوری کنید.
callsManager.addCall(attributes, onAnswer, onDisconnect, onSetActive, onSetInactive) {
// 1. React to hardware mute toggles from Watch, Car, or Headset
launch {
isMuted.collect { muted -> audioEngine.setMicMuted(muted) }
}
// 2. Observe available endpoints & request declarative switches
launch {
currentCallEndpoint.collect { endpoint -> updateUi(endpoint) }
}
requestEndpointChange(preferredBluetoothEndpoint)
}هرگاه کاربر تماس را از آن سطوح راه دور بیصدا کند، isMuted را برای واکنش نشان دادن جمعآوری میکنید و براساس آن میتوانید وضعیت تماس را بهروز کنید.
currentCallEndpoint را جمعآوری کنید تا بدانید تماس درحالحاضر در کدام نقطه پایانی برقرار است و بتوانید واسط کاربریتان را براساس آن بهروز کنید.
میتوانید آن دستگاههای خاص را در واسط کاربر تلفنتان نشان دهید.
و هرگاه کاربر بخواهد از دستگاهی به دستگاه دیگر برود، کافی است روش requestEndpointChange را با هر نقطه پایانی ترجیحی که کاربر میخواهد فراخوانی کند.
به همین سادگی است.
با این سه مرحله، برنامهتان آماده استفاده است و Jetpack Telecom را ادغام کرده است.
اما این همه ماجرا نیست.
با Jetpack Telecom 1.1.0، ویژگیهای بسیار بیشتری مانند سابقه تماس یکپارچه را ارائه میدهیم، به این معنی که همه تماسهای VoIP شما اکنون بهصورت بومی در شمارهگیر سیستم نشان داده میشوند.
بنابراین، مزیت آن این است که کاربران شما میتوانند تماس برگشتی با یک ضربه را مستقیماً از شمارهگیر سیستم در برنامه شما نمونهسازی کنند.
بنابراین، نیازی نیست که برنامه خاص خود را در میان همه برنامهها جستجو کنید.
آنها میتوانند بهراحتی از شمارهگیر سیستم که به آن عادت دارند، تماس برگشتی برقرار کنند.
اما برای برخی، اما اگر شما، اما ما همچنین کنترلهای دانهای ارائه میدهیم.
اگر نمیخواهید تماسی، تماس خاصی، در سیستم ثبت شود، فقط باید isLogExcluded پرچم را در داخل شیء CallAttributesCompat بهعنوان درست تنظیم کنید.
شیء فرادادهای که قبلاً درباره آن صحبت کردم، شما فقط آن را بهعنوان درست تنظیم میکنید و تماس شما در سیستم ثبت نمیشود و در شمارهگیر سیستم نشان داده نمیشود.
و دومین ویژگی عالی که با نسخه ۱.۱.۰ ارائه میشود، برش شبکه 5G است.
بنابراین، در شبکههای مخابراتی پشتیبانیشده، اتفاقی که میافتد این است که شبکهها برشهای شبکه ممتاز را به شما ارائه میدهند، به این معنی که برای یک مورد استفاده خاص، شرکت مخابراتی یک برش شبکه با تأخیر کم و بدون وقفه دیگر به شما میدهد تا کیفیت و تجربه بهتری در آن داشته باشید.
بنابراین، در شبکههای شرکت مخابراتی پشتیبانیشده، این ویژگی را بهصورت آماده دریافت خواهید کرد، بنابراین هیچ تلاش اضافی از جانب شما لازم نیست، حتی یک خط کد.
وقتی کتابخانه را ادغام کنید، تماسهایتان از آن برشهای شبکه بهرهمند میشوند و تأخیر کمتر و عملکرد بهتری خواهید داشت.
اما اگر به هر دلیلی این ویژگی را نمیخواهید، میتوانید بااستفاده از پرچم CAPABILITY_OPT_OUT_OF_PREMIUM_NETWORK از آن انصراف دهید.
Jetpack Telecom قبلاً توسط برنامههای ارتباطی برتر جهانی مانند Google Meet، WhatsApp، KakaoTalk، و Zoho Arattai پیادهسازی شده است.
بنابراین، اکنون به شما بستگی دارد، اکنون به شما بستگی دارد که این کتابخانه را در برنامه خود ادغام کنید و آن تجربه بهتر و یکپارچه را برای کاربر فراهم کنید.
با این اوصاف، اکنون که تماس همزمان پوشش داده شده است، میخواهم میکروفون را به همکار فوقالعادهام یاسین بسپارم تا درباره همرسانی با تکضرب صحبت کند.
متشکریم.
بسیار خوب دوستان، میدانم کمی خستهاید، پس نگران نباشید، این بخش آخر است.
بنابراین، من همرسانی فایل همتا به همتا را پوشش خواهم داد، اما موارد استفاده دیگری نیز وجود دارد که میتوانیم با همرسانی همتا به همتا انجام دهیم.
آنچه در سراسر بومسازگان تلفن همراه دیدهایم این است که هر روز بیشاز ۱۴ میلیارد عکس و ویدیو در رسانههای اجتماعی و برنامههای پیامرسانی همرسانی میشود، بنابراین این مقدار بسیار زیاد است.
و بیشتر اوقات همه این فایلها ازطریق فضای ابری همرسانی میشوند، بنابراین شما عکس میگیرید، آن را ازطریق سرویس موردعلاقهتان بارگذاری میکنید و سپس فرد مقابل باید آن را به همان روش بارگیری کند.
و همین اتفاق زمانی میافتد که میخواهید کسی یا دوستی را به یک شبکه اجتماعی دعوت کنید تا در یک فهرست پخش یا سند همکاری کند، معمولاً باید نام کاربری یا ایمیل او را تایپ کنید، که کمی طول میکشد.
ممکن است اشتباه کنید و این واقعاً تجربه خوبی نیست، بهویژه وقتی هر دو نفر دقیقاً کنار هم نشستهاید، بنابراین مثل این است که، اوه، باید ازطریق فضای ابری ارسال کنم، باید منتظر بمانم.
اصلاً خوب نیست.
و امروزه، همه ما تلفنها، دستگاهها، رایانههای لوحی، و دستگاههای تاشو شیکی داریم و عکسهای فوقالعادهای با آنها میگیریم.
اما وقتی ازطریق برخیاز این رسانههای اجتماعی یا برخیاز این برنامههای پیامرسانی همرسانی میکنید، بهدلیل مشکلات عملکرد و فضای ذخیرهسازی، این فایلها فشرده میشوند و درنهایت، روی صفحهنمایش فوقالعاده شما، بهصورت پیکسلی یا با کیفیت پایین نمایش داده میشوند.
این بهترین تجربهای نیست که میخواهید در دستگاه ممتازتان داشته باشید.
نکته دوم اینکه فایلها درحالحاضر واقعاً بزرگ هستند.
یک عکس بهراحتی ۱۰ مگابایت است، یک ویدیو ۵ دقیقهای حدود ۵۰۰ مگابایت است، بنابراین بارگذاری و بارگیری همه اینها زمان زیادی میبرد.
و درنهایت، در طرف دیگر، باید منتظر آن تأخیر باشید.
بنابراین، گاهی اوقات ممکن است سوار اتوبوس شوید، اتوبوس شلوغ است و درحالیکه اتوبوس درحال حرکت است، بهترین تأخیر، بهترین پوشش شبکه را ندارید و گاهی اوقات انتقال فایل قطع میشود، گاهی اوقات بهدلیل آن تأخیر واقعاً طولانی، ممکن است زماناتمام داشته باشید و انتقال فایل لغو شود، که واقعاً برای کاربر ناامیدکننده است.
بنابراین، بهجای تکیه بر فضای ابری برای همرسانی همه آن فایلها و منتظر ماندن برای انجام آن، چرا از شبکه محلی مانند بلوتوث یا Wi-Fi استفاده نکنیم؟
اما اگر بخواهید خودتان این کار را انجام دهید، بدیهی است که باید با مدیر بلوتوث، مدیر Wi-Fi، و نوشتن پروتکل خودتان سروکار داشته باشید، منظورم این است که همه نمیخواهند وقتشان را صرف این کار کنند.
اما درواقع، چیزی ازقبل در Android وجود دارد.
این ویژگی ازقبل عرضه شده است و شاید نام آن را شنیده باشید: «همرسانی سریع».
«همرسانی سریع» در Android به شما امکان میدهد این انتزاع را ازقبل در سیستم داشته باشید و بتوانید فایلها را با دستگاه دیگری همرسانی کنید و مجبور نباشید همه اینها را مدیریت کنید.
این ویژگی درحالحاضر در برگه همرسانی شما دردسترس است و بین هر دو دستگاه، بهترین پروتکل را برای دستگاه شما انتخاب میکند و در Marshmallow و نسخههای بالاتر دردسترس است که این روزها مخاطبان بسیار زیادی دارد، درست است؟
و درنهایت، آن واسط کاربر و تجربه کاربری آشنایی که در کنار میبینید واقعاً به کاربران خودتان کمک میکند.
بنابراین، آنها میدانند که رابط کاربری چیست، نمیتوانند مجبور نیستند یاد بگیرند که چگونه از آن استفاده کنند، قبلاً آن را دیدهاند.
اما ما توسعهدهندگان را میشناسیم، آنها هم میخواهند تجربه را ازآن خود کنند.
شما برنامهتان را میشناسید، احتمالاً کاربرانتان را بهتر از ما میشناسید.
پس چرا «همرسانی سریع» را مستقیماً در برنامهتان نداشته باشید؟
بنابراین، شروع به صحبت با توسعهدهندگان کردیم و دیدیم که «همرسانی سریع» درحالحاضر بسیار گسترده است.
این ویژگی در Android، Chromebook، اخیراً در «کتابهای Google»، و همچنین در Windows دردسترس است، یعنی بیشاز ۳ میلیارد دستگاه، که رقم بسیار بزرگی است.
و همچنین با Pixel 11، قابلیت همرسانی با تکضرب را ارائه کردیم.
بنابراین، بهجای اینکه مجبور باشید دستگاهی را که میخواهید با آن همرسانی کنید انتخاب کنید، میتوانید فقط تکضرب بزنید و با NFC، ما این قابلیت شناسایی را مستقیماً برای شما انجام میدهیم.
بنابراین، میتوانید عکسی را انتخاب کنید، اما میتوانید فایلی را هم انتخاب کنید، فقط کافی است روی دو دستگاه تکضرب بزنید و آنها مستقیماً با یک ضربه همرسانی میشوند.
و باز هم، توسعهدهندگانی را میشناسیم که میخواهند مالک تجربه باشند.
میدانیم که این بهترین راه برای کاربران شما است تا بتوانند از ویژگی خاصی استفاده کنند، در این مورد، همرسانی فایل.
پس چرا «همرسانی سریع» را در برنامهتان جاسازی نکنید؟
به همین دلیل است که سال آینده، کیت توسعه نرمافزار «همرسانی سریع» را مستقیماً برای شما ارائه میکنیم.
میتوانید مستقیماً از آن در برنامهتان استفاده کنید و امنیت آن ازقبل بهصورت رمزگذاری سرتاسر بین هر دو دستگاه تعبیه شده است.
ازآنجاییکه انتقال بین این شبکههای محلی انجام میشود، سرعت کامل را خواهید داشت، بدیهی است که در Wi-Fi خانگیتان، اما همچنین بین هر دو شما میتوانید یک نقطه اتصال بهطور خودکار ایجاد کنید، لازم نیست خودتان این کار را انجام دهید.
و برای دستگاههایی که از Wi-Fi Direct پشتیبانی میکنند، این ویژگی ازقبل گنجانده شده است.
و فایلهایی که با دوربین فوقالعادهتان گرفتهاید، بهترین وضوح و جزئیات خود را حفظ خواهند کرد، بنابراین میتوانید آنها را بهخوبی در صفحهنمایش بزرگ خود ببینید.
و با توجه به اینکه به شبکههای محلی متکی است، میتوانید از آن در مترو استفاده کنید، حتی در عمیقترین بخش آن، همچنان شبکه خواهید داشت تا بتوانید با دوستتان همرسانی کنید.
در هواپیما هم، نمیدانم چند هزار پا بالاتر، در آسمان خواهید بود و همچنان میتوانید آن فایلها را همرسانی کنید.
و درنهایت، برای شما بهعنوان توسعهدهنده، ازنظر داده صرفهجویی زیادی در هزینه میشود، زیرا اولاً، کاربران شما مجبور نیستند از آن طرح داده استفاده کنند و این موضوع زمانی که بهعنوان گردشگر سفر میکنند بسیار مهم است.
اما همچنین، در سرورهای خودتان، لازم نیست آن را نگهداری کنید.
و سال گذشته، به توسعهدهندگان و همچنین کاربران گفتیم که با «همرسانی سریع» میتوانند فایلها را ازطریق AirDrop با دستگاههای iPhone همرسانی کنند.
خب، با «همرسانی سریع»، همین قابلیت را برای توسعهدهندگان ارائه میکنیم.
ما قصد داریم یک کیت توسعه نرمافزار «همرسانی سریع» برای iOS برای توسعهدهندگانی که برنامههای iOS دارند ارائه دهیم تا بتوانند بدون توجه به دستگاه، فایلها را بین هر دو پلاتفرم همرسانی کنند.
بنابراین، همه اینها ارائه فانتزی است، اکنون، بیایید نگاهی سریع به کد بیندازیم.
سعی کردیم آن را تا حد ممکن ساده کنیم.
شما بهوضوح یک QuickShareClient را آغاز میکنید، باید فایلهایی را که میخواهید همرسانی کنید پیوست کنید، و سپس وقتی جلسه را شروع کردید، این پنجره مقید زیبا را که در کنار میبینید خواهید داشت، برگه پایینی که کاربران میتوانند فایلهایی را که ارسال میشوند بهصورت پیشنمایش ببینند، و اگر محتوا را حساس میدانید، میتوانید آن را نیز با گزینهای که روی صفحه میبینید فعال کنید.
// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val attachment = FileAttachment(
fileName = "report.pdf",
size = 1_048_576L,
uri = fileUri,
mimeType = "application/pdf",
// Sensitive content will be masked in preview
isSensitiveContent = false,
)
val content = ShareableContent.withFiles(listOf(attachment))
val request = SharingRequest.withContent(content)
val session = quickShareClient.startSharingSession(request, lifecycleOwner)وقتی میخواهید فایلی را دریافت کنید، دوباره، در برنامهتان، همان چیز، البته، در این مورد، شما پیوستی اضافه نمیکنید، و سپس بهمحض شروع جلسه، نیازی به سروکار داشتن با هیچکدام از اینها ندارید.
// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val request = SharingRequest.withoutContent()
val session = quickShareClient.startSharingSession(request, lifecycleOwner)
// Open the Quick Share overlay in receive mode
session.launchWindow(activity, contentSelectionIntent = null)تا اینجا، آنچه دیدهاید واقعاً فقط ۲۰ خط کد در هر دو طرف است.
میدانیم که با «همرسانی سریع» و همرسانی با تکضرب، امکان همرسانی فایلها فقط با لمس کردن دستگاهها درواقع مورد استفاده جدیدی را نیز فعال میکند.
برنامه شما میتواند در دستگاه دیگر بسته شود و با تکضرب زدن، بهمحض انتقال فایل، برنامه شما بهطور خودکار باز میشود و شناسه منبع یکسان را دریافت خواهید کرد تا بتوانید مثلاً پردازش دروندستگاهی انجام دهید یا چیزی را مستقیماً در برنامه خود به کاربر نمایش دهید.
// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val urlAttachment = TextAttachment(
text = groupAddUrl,
type = TextAttachment.Type.URL,
)
val content = ShareableContent.withText(urlAttachment)
val appLink = AppLink("https://myapp.com/group/invite")
val request = SharingRequest.withContent(content, appLink)
val session = quickShareClient.startSharingSession(request, lifecycleOwner)بنابراین، باز هم لازم نیست برنامهتان را در دستگاه گیرنده باز کنید.
با ضربه زدن برای همرسانی، آن را بهطور خودکار باز میکند.
و البته، اگر میخواهید واسط کاربری شما به آن انتقال فایل واکنش نشان دهد، فقط یک Kotlin Flow دارید و همه رویدادها را برای پردازش آن خواهید داشت.
// Final API may undergo further updates
scope.launch {
for (event in session.sharingEvents) {
when (event) {
is SharingEvent.SendComplete ->
println("Send finished")
is SharingEvent.ReceiveComplete ->
println("Received, files=${event.content?.files?.size}")
else -> println("other event")
}
}
}خب، تا اینجا چیزهای زیادی دیدهایم، پس من جمعبندی میکنم.
و نگران نباشید، چند دقیقه وقت خواهید داشت تا استراحت کنید.
CompanionDeviceManager بهترین میانای برنامهسازی کاربردی برای شما است تا هرگونه تعامل بلوتوث، تعاملات مداوم بلوتوث را مدیریت کنید، همانطور که هانروی قبلاً ارائه کرد.
اگر برنامه شما تماسهای صوتی ازطریق پروتکل اینترنت دارد، لازم نیست این مورد استفاده اصلی آن باشد، مثلاً میتواند اینگونه باشد که شما بخشی از برنامه خود را به مشتریان پشتیبانی اختصاص دادهاید که در آن کاربر میتواند با پشتیبانی تماس بگیرد.
در این مورد هم بهترین راه برای مدیریت تماسهای صوتی ازطریق IP در Android، Jetpack Com-- Jetpack Telecom است.
و درنهایت، برای هر برنامه همرسانی یا باز کردن فایل همتا به همتا، همانطور که اشاره کردم، با «تکضرب برای همرسانی»، «همرسانی سریع» راهحل است.
بنابراین، از همه شما سپاسگزارم، و قبلاز اینکه بروید، فقط یک نکته سریع، ما بعداز این بیرون خواهیم بود تا به سؤالات شما پاسخ دهیم.
اگر سؤالی دارید، ما ساعات اداری داریم، میدانید، در تمام موارد استفادهای که ارائه کردهایم.
و فراموش نکنید که هرآنچه را از سخنرانیتان یا جلسات دیگر یاد گرفتهاید، در رسانههای اجتماعی همرسانی کنید.
ما #AndroidDevSummit را داریم.
باز هم میگویم، برای ما بسیار مهم است که بدانیم تعامل داریم، افرادی داریم که واقعاً از رویداد لذت میبرند.
این به ما کمک میکند بدانیم که قطعاً انجمنی داریم که میخواهیم دوباره آن را ببینیم.
و آویز اجتماعیتان را که میتوانید به حلقهکلید روبات Android خود اضافه کنید از میز اطلاعات دریافت کنید.
آیا درست انجام دادم؟
متشکریم.
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />class BleForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
…
return START_STICKY
}
}<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_MEDICAL" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" />val paringRequest = AssociationRequest.Builder()
.addDeviceFilter(deviceFilter)
.setSingleDevice(true)
.setDeviceProfile(AssociationRequest.DEVICE_PROFILE_MEDICAL)
.build()
companionDeviceManager.associate(paringRequest, ContextCompat.getMainExecutor(this),
object : CompanionDeviceManager.Callback() {
override fun onAssociationPending(intentSender: IntentSender) {
intentSenderLauncher.launch(IntentSenderRequest.Builder(intentSender).build())
}
override fun onAssociationCreated(associationInfo: AssociationInfo) { … }
override fun onFailure(error: CharSequence?) { … }
}
)class BleCompanionDeviceService : CompanionDeviceService() {
override fun onDevicePresenceEvent(event: DevicePresenceEvent) {
when (event.event) {
DevicePresenceEvent.EVENT_BLE_APPEARED -> onDeviceAppeared(event.associationId)
DevicePresenceEvent.EVENT_BLE_DISAPPEARED -> onDeviceDisappeared(event.associationId)
}
}
private fun onDeviceAppeared(associationId: Int) { … }
private fun onDeviceDisappeared(associationId: Int) { … }
}<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />// Initialize CallsManager & register supported capabilities once
val callsManager = CallsManager(context)
callsManager.registerAppWithTelecom(
capabilities = CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING or
CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING
)val attributes = CallAttributesCompat(
displayName = "Kukk Nat",
address = Uri.parse("tel:+16505550199"),
direction = CallAttributesCompat.DIRECTION_INCOMING,
callType = CallAttributesCompat.CALL_TYPE_AUDIO_CALL
)
callsManager.addCall(
attributes, onAnswer, onDisconnect, onSetActive, onSetInactive
) {
// Coroutine CallControlScope active for the duration of the call
}callsManager.addCall(attributes, onAnswer, onDisconnect, onSetActive, onSetInactive) {
// 1. React to hardware mute toggles from Watch, Car, or Headset
launch {
isMuted.collect { muted -> audioEngine.setMicMuted(muted) }
}
// 2. Observe available endpoints & request declarative switches
launch {
currentCallEndpoint.collect { endpoint -> updateUi(endpoint) }
}
requestEndpointChange(preferredBluetoothEndpoint)
}// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val attachment = FileAttachment(
fileName = "report.pdf",
size = 1_048_576L,
uri = fileUri,
mimeType = "application/pdf",
// Sensitive content will be masked in preview
isSensitiveContent = false,
)
val content = ShareableContent.withFiles(listOf(attachment))
val request = SharingRequest.withContent(content)
val session = quickShareClient.startSharingSession(request, lifecycleOwner)// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val request = SharingRequest.withoutContent()
val session = quickShareClient.startSharingSession(request, lifecycleOwner)
// Open the Quick Share overlay in receive mode
session.launchWindow(activity, contentSelectionIntent = null)// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val urlAttachment = TextAttachment(
text = groupAddUrl,
type = TextAttachment.Type.URL,
)
val content = ShareableContent.withText(urlAttachment)
val appLink = AppLink("https://myapp.com/group/invite")
val request = SharingRequest.withContent(content, appLink)
val session = quickShareClient.startSharingSession(request, lifecycleOwner)// Final API may undergo further updates
scope.launch {
for (event in session.sharingEvents) {
when (event) {
is SharingEvent.SendComplete ->
println("Send finished")
is SharingEvent.ReceiveComplete ->
println("Received, files=${event.content?.files?.size}")
else -> println("other event")
}
}
}