چگونه‌ها

معرفی Cahier: یک نمونه جدید اندروید گیت‌هاب برای بهره‌وری و خلاقیت در صفحه نمایش بزرگ

مطالعه ۱۱ دقیقه‌ای
Chris Assigbe
مهندس روابط توسعه‌دهنده

رابط برنامه‌نویسی کاربردی Ink اکنون در مرحله بتا است و آماده ادغام در برنامه شما می‌باشد. این نقطه عطف با بازخورد ارزشمند توسعه‌دهندگان امکان‌پذیر شده است و منجر به بهبود مستمر در عملکرد، پایداری و کیفیت بصری API شده است.

برنامه‌های گوگل مانند Google Docs ، Pixel Studio ، Google Photos ، Chrome PDF ، Youtube Effect Maker و ویژگی‌های منحصر به فرد در اندروید مانند Circle to Search، همگی از جدیدترین APIها استفاده می‌کنند.

برای بزرگداشت این نقطه عطف، مفتخریم که از عرضه Cahier ، یک نمونه برنامه یادداشت‌برداری جامع که برای دستگاه‌های اندرویدی در همه اندازه‌ها، به‌ویژه تبلت‌ها و تلفن‌های تاشو، بهینه شده است، خبر دهیم.

کایه چیست؟

Cahier (به معنی دفترچه یادداشت در زبان فرانسوی) یک برنامه نمونه است که برای نشان دادن چگونگی ساخت برنامه‌ای طراحی شده است که به کاربران امکان می‌دهد افکار خود را با ترکیب متن، نقاشی و تصاویر ثبت و سازماندهی کنند.

این نمونه می‌تواند به عنوان مرجعی برای افزایش بهره‌وری و خلاقیت کاربر در صفحه نمایش‌های بزرگ عمل کند. این نمونه، بهترین شیوه‌ها را برای ساخت چنین تجربیاتی، تسریع درک توسعه‌دهندگان و پذیرش APIها و تکنیک‌های قدرتمند مرتبط، به نمایش می‌گذارد. این پست شما را با ویژگی‌های اصلی Cahier، APIهای کلیدی و تصمیمات معماری که این نمونه را به مرجعی عالی برای برنامه‌های شما تبدیل می‌کند، آشنا می‌کند.

ویژگی‌های کلیدی نشان داده شده در نمونه عبارتند از:

  • ایجاد یادداشت‌های چندمنظوره: نحوه پیاده‌سازی یک سیستم ایجاد محتوای انعطاف‌پذیر را نشان می‌دهد که از قالب‌های مختلف در یک یادداشت واحد، از جمله متن، نقشه‌های آزاد و پیوست‌های تصویر، پشتیبانی می‌کند.
  • ابزارهای خلاقانه‌ی جوهرسازی : با استفاده از Ink API، تجربه‌ی طراحی با کارایی بالا و تأخیر کم را پیاده‌سازی می‌کند. این نمونه، مثالی عملی از ادغام قلم‌موهای مختلف، انتخابگر رنگ، قابلیت لغو/بازگرداندن کار و ابزار پاک‌کن را ارائه می‌دهد.
  • یکپارچه‌سازی روان محتوا با کشیدن و رها کردن : نحوه مدیریت محتوای ورودی و خروجی را با استفاده از کشیدن و رها کردن نشان می‌دهد. این شامل پذیرش تصاویر رها شده از برنامه‌های دیگر و فعال کردن امکان بیرون کشیدن محتوا از برنامه شما برای اشتراک‌گذاری یکپارچه می‌شود.
  • سازماندهی یادداشت‌ها : یادداشت‌ها را برای دسترسی سریع به عنوان موارد دلخواه علامت‌گذاری کنید. برای مرتب ماندن، نمای آنها را فیلتر کنید.
  • معماری آفلاین: با معماری آفلاین با استفاده از Room ساخته شده است و تضمین می‌کند که تمام داده‌ها به صورت محلی ذخیره می‌شوند و برنامه بدون اتصال به اینترنت کاملاً کاربردی باقی می‌ماند.
  • پشتیبانی قدرتمند از چند پنجره و چند نمونه : نحوه پشتیبانی از چند نمونه را نشان می‌دهد و به برنامه شما اجازه می‌دهد در چندین پنجره اجرا شود تا کاربران بتوانند روی یادداشت‌های مختلف در کنار هم کار کنند و بهره‌وری و خلاقیت را در صفحه نمایش‌های بزرگ افزایش دهند.
  • رابط کاربری تطبیق‌پذیر برای همه صفحه نمایش‌ها : رابط کاربری با استفاده از ListDetailPaneScaffold و NavigationSuiteScaffold به طور یکپارچه با اندازه‌ها و جهت‌های مختلف صفحه نمایش سازگار می‌شود تا یک تجربه کاربری بهینه در تلفن‌ها، تبلت‌ها و دستگاه‌های تاشو ارائه دهد.
  • یکپارچه‌سازی عمیق سیستم : راهنمایی در مورد چگونگی تبدیل برنامه خود به برنامه یادداشت‌برداری پیش‌فرض در اندروید ۱۴ و بالاتر با پاسخ به اهداف یادداشت‌های سراسری سیستم، که امکان ضبط سریع محتوا از نقاط ورودی مختلف سیستم را فراهم می‌کند، ارائه می‌دهد.

ساخته شده برای بهره‌وری و خلاقیت در صفحه نمایش‌های بزرگ

برای عرضه اولیه، ما تمرکز اطلاعیه را بر روی چند ویژگی اصلی قرار داده‌ایم که Cahie r را به یک منبع یادگیری کلیدی برای موارد استفاده از بهره‌وری و خلاقیت تبدیل می‌کند.

پایه و اساس سازگاری

Cahier از پایه طوری ساخته شده که قابلیت تطبیق‌پذیری داشته باشد. این نمونه از کتابخانه‌های material3-adaptive، به ویژه ListDetailPaneScaffold و NavigationSuiteScaffold، برای تطبیق یکپارچه طرح‌بندی برنامه با اندازه‌ها و جهت‌های مختلف صفحه نمایش استفاده می‌کند. این یک عنصر حیاتی برای یک برنامه اندروید مدرن است و Cahier نمونه‌ای واضح از نحوه پیاده‌سازی مؤثر آن ارائه می‌دهد.

رابط کاربری تطبیقی ​​Cahier که با کتابخانه تطبیقی ​​Material 3 ساخته شده است..gif

رابط کاربری تطبیقی ​​Cahier که با کتابخانه تطبیقی ​​Material 3 ساخته شده است

نمایش APIها و یکپارچه‌سازی‌های کلیدی

این نمونه بر نمایش APIهای قدرتمند بهره‌وری متمرکز است که می‌توانید در برنامه‌های خود از آنها استفاده کنید، از جمله:

نگاهی دقیق‌تر به APIهای کلیدی

بیایید عمیق‌تر به دو مورد از APIهای اساسی که Cahier برای ارائه یک تجربه یادداشت‌برداری درجه یک در خود ادغام کرده است، بپردازیم.

ایجاد تجربه‌های طبیعی در زمینه‌ی جوهرسازی با Ink API

ورودی قلم، دستگاه‌های صفحه نمایش بزرگ را به نوت‌بوک‌ها و دفترچه‌های طراحی دیجیتال تبدیل می‌کند. برای کمک به شما در ساخت تجربیات روان و طبیعی در زمینه‌ی طراحی با قلم، ما Ink API را به سنگ بنای نمونه تبدیل کرده‌ایم. Ink API ایجاد، رندر و دستکاری خطوط قلم زیبا را با بهترین کیفیت و کمترین تأخیر در کلاس خود آسان می‌کند.

Ink API یک معماری ماژولار ارائه می‌دهد، بنابراین می‌توانید آن را با نیازهای خاص برنامه خود تطبیق دهید. ماژول‌های API شامل موارد زیر هستند:

  • ماژول‌های نوشتن ( نوشتن - نمایش‌ها ): ورودی جوهر را به صورت آنی مدیریت می‌کنند تا خطوط روان با کمترین تأخیری که یک دستگاه می‌تواند ارائه دهد، ایجاد شود.
    • در DrawingSurface ، Cahier از InProgressStrokes که به تازگی معرفی شده است، برای مدیریت ورودی‌های لمسی یا قلم نوری به صورت بلادرنگ استفاده می‌کند. این ماژول مسئول ثبت رویدادهای اشاره‌گر و رندر کردن ضربات جوهر خیس با کمترین تأخیر ممکن است.
  • ماژول Strokes : ورودی جوهر و نمایش بصری آن را نشان می‌دهد. وقتی کاربر رسم یک خط را تمام می‌کند، تابع onStrokesFinished یک شیء Stroke نهایی/خشک شده را به برنامه ارائه می‌دهد. این شیء تغییرناپذیر، که نشان دهنده‌ی خط جوهر تکمیل شده است، سپس در DrawingCanvasViewModel مدیریت می‌شود.
  • ماژول رندرینگ: به طور کارآمد خطوط جوهر را نمایش می‌دهد و به آنها اجازه می‌دهد تا با Jetpack Compose یا نماهای اندروید ترکیب شوند.
    • برای نمایش خطوط موجود و خطوط تازه خشک‌شده، Cahier از CanvasStrokeRenderer در DrawingSurface برای طراحی فعال و از DrawingDetailPanePreview برای نمایش پیش‌نمایش استاتیک یادداشت استفاده می‌کند. این ماژول به طور مؤثر اشیاء Stroke را روی یک Canvas ترسیم می‌کند.
  • ماژول‌های قلم‌مو ( Compose - views ): روشی اعلانی برای تعریف سبک بصری خطوط ارائه می‌دهند. به‌روزرسانی‌های اخیر (از زمان انتشار alpha03) شامل یک قلم‌مو با خط چین جدید است که به ویژه برای ویژگی‌هایی مانند انتخاب با لاسو مفید است. DrawingCanvasViewModel حالت قلم‌مو فعلی را نگه می‌دارد. یک جعبه ابزار در DrawingCanvas به کاربران امکان می‌دهد خانواده‌های قلم‌موهای مختلف (مانند StockBrushes.pressurePen() یا StockBrushes.highlighter() ) را انتخاب کرده و رنگ‌ها را تغییر دهند. ViewModel شیء قلم‌مو را به‌روزرسانی می‌کند، که سپس توسط InProgressStrokes قابل ترکیب برای خطوط جدید استفاده می‌شود.
  • ماژول‌های هندسه ( ایجاد - نمایش ): از دستکاری و تجزیه و تحلیل خطوط برای قابلیت‌هایی مانند پاک کردن و انتخاب پشتیبانی می‌کنند.
    • ابزار پاک‌کن در جعبه ابزار و عملکرد آن در DrawingCanvasViewModel به ماژول geometry متکی است. وقتی پاک‌کن فعال است، یک MutableParallelogram در اطراف مسیر حرکت کاربر ایجاد می‌کند. سپس پاک‌کن تقاطع‌های بین شکل و کادرهای محدودکننده‌ی خطوط موجود را بررسی می‌کند تا مشخص کند کدام خطوط را باید پاک کند، که باعث می‌شود پاک‌کن بصری و دقیق به نظر برسد.
  • ماژول ذخیره‌سازی : قابلیت‌های سریال‌سازی و حذف سریال‌سازی کارآمد برای داده‌های ink را فراهم می‌کند که منجر به صرفه‌جویی قابل توجه در اندازه دیسک و شبکه می‌شود. برای ذخیره ترسیمات، Cahier اشیاء Stroke را در پایگاه داده Room خود حفظ می‌کند. در Converters ، نمونه از تابع رمزگذاری ماژول ذخیره‌سازی برای سریال‌سازی StrokeInputBatch (داده‌های خام نقطه‌ای) در یک ByteArray استفاده می‌کند. آرایه بایت، همراه با ویژگی‌های قلم‌مو، به عنوان یک رشته JSON ذخیره می‌شود. تابع رمزگشایی برای بازسازی strokeها هنگام بارگذاری یک یادداشت استفاده می‌شود.
اوریون.png

فراتر از این ماژول‌های اصلی، به‌روزرسانی‌های اخیر قابلیت‌های Ink API را گسترش داده‌اند:

  • APIهای آزمایشی جدید برای اشیاء سفارشی BrushFamily به توسعه‌دهندگان این امکان را می‌دهد تا انواع قلم‌موهای خلاقانه و منحصر به فرد ایجاد کنند و امکاناتی مانند قلم‌موهای Pencil و Laser Pointer را فراهم کنند.

کایه از قلم‌موهای سفارشی، از جمله قلم‌مو موسیقی منحصر به فرد که در زیر نمایش داده شده است، برای نشان دادن امکانات خلاقانه پیشرفته استفاده می‌کند.

لیزر رنگین‌کمانی که با قلم‌موهای سفارشی Ink API ساخته شده است..gif

لیزر رنگین‌کمانی که با قلم‌موهای سفارشی Ink API ساخته شده است

یادداشت‌ها.png

قلم‌مو موسیقی با قلم‌موهای سفارشی Ink API ساخته شده است

  • ماژول‌های بومی Jetpack Compose برای ایجاد قابلیت همکاری، ادغام قابلیت‌های inking را مستقیماً در رابط‌های کاربری Compose شما ساده می‌کنند تا یک تجربه توسعه اصطلاحاً کاربردی‌تر و کارآمدتر ایجاد شود.

Ink API مزایای متعددی ارائه می‌دهد که آن را به انتخابی ایده‌آل برای برنامه‌های بهره‌وری و خلاقیت نسبت به پیاده‌سازی سفارشی تبدیل می‌کند:

  • سهولت استفاده: رابط برنامه‌نویسی کاربردی Ink پیچیدگی‌های گرافیک و هندسه را کنار می‌گذارد و به شما امکان می‌دهد روی ویژگی‌های اصلی Cahier تمرکز کنید.
  • عملکرد: پشتیبانی داخلی از تأخیر کم و رندر بهینه، تجربه‌ی روان و واکنش‌گرای جوهرافشانی را تضمین می‌کند.
  • انعطاف‌پذیری: طراحی ماژولار به شما امکان می‌دهد اجزای مورد نیاز را انتخاب و گزینش کنید، که این امر امکان ادغام یکپارچه‌ی Ink API را در معماری Cahier فراهم می‌کند.

رابط برنامه‌نویسی کاربردی Ink در حال حاضر در بسیاری از برنامه‌های گوگل، از جمله برای نشانه‌گذاری در Docs و Circle to Search و همچنین برنامه‌های همکار مانند Orion Notes و PDF Scanner ، به کار گرفته شده است.

«Ink API اولین انتخاب ما برای Circle-to-Search (CtS) بود. با استفاده از مستندات گسترده آنها، ادغام Ink API بسیار آسان بود و به ما این امکان را داد که تنها در عرض یک هفته به اولین نمونه اولیه کاربردی خود برسیم. پشتیبانی از بافت قلم موی سفارشی و انیمیشن Ink به ما این امکان را داد که به سرعت طراحی stroke را تکرار کنیم.» - جردن کومودا، مهندس نرم‌افزار - گوگل

تبدیل شدن به برنامه یادداشت‌های پیش‌فرض با نقش یادداشت‌ها

یادداشت‌برداری یک قابلیت اصلی است که بهره‌وری کاربر را در دستگاه‌های صفحه نمایش بزرگ افزایش می‌دهد. با ویژگی نقش یادداشت‌ها ، کاربران می‌توانند از صفحه قفل یا در حالی که سایر برنامه‌ها در حال اجرا هستند، به برنامه‌های سازگار شما دسترسی داشته باشند. این ویژگی برنامه‌های یادداشت‌برداری پیش‌فرض در سطح سیستم را شناسایی و تنظیم می‌کند و به آنها اجازه می‌دهد تا برای ضبط محتوا راه‌اندازی شوند.

پیاده‌سازی در Cahier

پیاده‌سازی نقش یادداشت‌ها شامل چند مرحله کلیدی است که همه در نمونه نشان داده شده‌اند:

  1. اعلان مانیفست : ابتدا، برنامه باید قابلیت خود را برای مدیریت intentهای یادداشت‌برداری اعلام کند. در AndroidManifest.xml ، Cahier یک <intent-filter> برای اکشن android.intent.action.CREATE_NOTE قرار می‌دهد. این به سیستم نشان می‌دهد که برنامه یک کاندید بالقوه برای نقش یادداشت‌ها است.
  2. بررسی وضعیت نقش : SettingsViewModel از RoleManager اندروید برای تعیین وضعیت فعلی استفاده می‌کند. SettingsViewModel بررسی می‌کند که آیا نقش notes در دستگاه موجود است ( isRoleAvailable ) و آیا Cahier در حال حاضر آن نقش را دارد ( isRoleHeld ). این وضعیت با استفاده از جریان‌های Kotlin در معرض رابط کاربری قرار می‌گیرد.
  3. درخواست نقش : در فایل Settings.kt ، اگر نقش موجود باشد اما در اختیار کاربر نباشد، یک دکمه (Button) به کاربر نمایش داده می‌شود. وقتی روی دکمه کلیک شود، تابع requestNotesRole را در ViewModel فراخوانی می‌کند. این تابع یک intent برای باز کردن صفحه تنظیمات پیش‌فرض برنامه ایجاد می‌کند که در آن کاربر می‌تواند Cahier را انتخاب کند. این فرآیند با استفاده از API rememberLauncherForActivityResult مدیریت می‌شود که اجرای intent و دریافت نتیجه را مدیریت می‌کند.
  4. به‌روزرسانی رابط کاربری : پس از بازگشت کاربر از صفحه تنظیمات، تابع فراخوانی ActivityResultLauncher تابعی را در ViewModel فعال می‌کند تا وضعیت نقش را به‌روزرسانی کند و اطمینان حاصل شود که رابط کاربری به طور دقیق نشان می‌دهد که آیا برنامه اکنون پیش‌فرض است یا خیر.

یاد بگیرید که چگونه نقش یادداشت‌ها را در برنامه خود در راهنمای ایجاد یک برنامه یادداشت‌برداری ما ادغام کنید.

سلام دنیا.png

Cahier به عنوان برنامه یادداشت‌برداری پیش‌فرض روی تبلت لنوو، در یک پنجره شناور راه‌اندازی شد.

یک گام بزرگ به جلو: لنوو قابلیت یادداشت‌برداری را فعال می‌کند

ما مفتخریم که یک گام بزرگ رو به جلو برای بهره‌وری اندروید روی صفحه نمایش بزرگ را اعلام کنیم: لنوو پشتیبانی از نقش یادداشت‌ها را در تبلت‌های دارای اندروید ۱۵ و بالاتر فعال کرده است! با این به‌روزرسانی، اکنون می‌توانید برنامه‌های یادداشت‌برداری خود را به‌روزرسانی کنید تا به کاربران دارای دستگاه‌های سازگار لنوو اجازه دهید آنها را به عنوان پیش‌فرض تنظیم کنند و دسترسی بی‌وقفه از صفحه قفل و باز کردن قفل ویژگی‌های ضبط محتوا در سطح سیستم را فراهم کنند.

این تعهد از سوی یک تولیدکننده اصلی تجهیزات (OEM) پیشرو، اهمیت روزافزون نقش نوت‌ها را در ارائه یک تجربه کاربری واقعاً یکپارچه و پربار در اندروید نشان می‌دهد.

چند نمونه‌ای، چند پنجره‌ای و پنجره‌ای کردن دسکتاپ

بهره‌وری در یک صفحه نمایش بزرگ، تماماً به مدیریت اطلاعات و گردش کار به طور کارآمد بستگی دارد. به همین دلیل است که Cahier طوری ساخته شده است که به طور کامل از قابلیت‌های پیشرفته پنجره‌بندی اندروید پشتیبانی کند و یک فضای کاری انعطاف‌پذیر را فراهم کند که با نیازهای کاربر سازگار باشد. این برنامه از موارد زیر پشتیبانی می‌کند:

  • چند پنجره‌ای : قابلیت اساسی اجرا در کنار یک برنامه دیگر در حالت تقسیم صفحه یا حالت آزاد. این قابلیت برای کارهایی مانند مراجعه به یک صفحه وب هنگام یادداشت‌برداری در Cahier ضروری است.
  • چند نمونه‌ای : اینجاست که چندوظیفگی واقعی می‌درخشد. Cahier به کاربران اجازه می‌دهد چندین پنجره مستقل از برنامه را همزمان باز کنند. تصور کنید که دو یادداشت مختلف را در کنار هم مقایسه می‌کنید یا در یک پنجره به یک یادداشت متنی ارجاع می‌دهید در حالی که روی یک نقاشی در پنجره دیگر کار می‌کنید. Cahier نحوه مدیریت این نمونه‌های جداگانه، هر کدام با حالت خاص خود را نشان می‌دهد و برنامه شما را به ابزاری قدرتمند و چندوجهی تبدیل می‌کند.
  • پنجره‌بندی دسکتاپ : وقتی به یک نمایشگر خارجی متصل می‌شود، حالت دسکتاپ اندروید، یک تبلت یا دستگاه تاشو را به یک ایستگاه کاری تبدیل می‌کند. از آنجا که Cahier با یک رابط کاربری تطبیقی ​​ساخته شده و از چند نمونه پشتیبانی می‌کند، این برنامه در این محیط به زیبایی عمل می‌کند. کاربران می‌توانند چندین پنجره Cahier را درست مانند یک دسکتاپ سنتی باز کنند، اندازه آنها را تغییر دهند و در جای خود قرار دهند و گردش‌های کاری پیچیده‌ای را که قبلاً در دستگاه‌های تلفن همراه قابل دسترسی نبودند، امکان‌پذیر کنند.
پنجره‌بندی دفترچه یادداشت رومیزی.webp

اجرای Cahier در حالت پنجره دسکتاپ روی تبلت پیکسل

در اینجا نحوه پیاده‌سازی این ویژگی‌ها در Cahier آورده شده است:

برای فعال کردن چند نمونه‌ای بودن، ابتدا باید با اضافه کردن ویژگی PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI به اعلان MainActivity در AndroidManifest ، به سیستم اطلاع می‌دادیم که برنامه از چندین بار اجرا شدن پشتیبانی می‌کند:

<activity

    android:name="com.example.cahier.MainActivity"

    android:exported="true"

    android:label="@string/app_name"

    android:theme="@style/Theme.MyApplication"

    android:showWhenLocked="true"

    android:turnScreenOn="true"

    android:resizeableActivity="true"

    android:launchMode="singleInstancePerTask">


    <property

        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"

        android:value="true"/>

    ...

</activity>

در مرحله بعد، منطق لازم برای اجرای یک نمونه جدید از برنامه را پیاده‌سازی کردیم. در CahierHomeScreen.kt ، وقتی کاربر تصمیم می‌گیرد یادداشتی را در یک پنجره جدید باز کند، ما یک Intent جدید با پرچم‌های خاصی ایجاد می‌کنیم که به سیستم نحوه مدیریت راه‌اندازی فعالیت جدید را آموزش می‌دهند. ترکیب FLAG_ACTIVITY_NEW_TASK ، FLAG_ACTIVITY_MULTIPLE_TASK و FLAG_ACTIVITY_LAUNCH_ADJACENT تضمین می‌کند که یادداشت در یک پنجره جدید و جداگانه در کنار پنجره موجود باز می‌شود.

fun openNewWindow(activity: Activity?, note: Note) {

    val intent = Intent(activity, MainActivity::class.java)

    intent.putExtra(AppArgs.NOTE_TYPE_KEY, note.type)

    intent.putExtra(AppArgs.NOTE_ID_KEY, note.id)

    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or

        Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT


    activity?.startActivity(intent)

}

برای پشتیبانی از حالت چند پنجره‌ای، باید با تنظیم عنصر <activity> یا <application> در Manifest، به سیستم اطلاع می‌دادیم که برنامه از قابلیت تغییر اندازه پشتیبانی می‌کند.

<activity

    android:name="com.example.cahier.MainActivity"

    android:resizeableActivity="true"

    ...>

</activity>

خود رابط کاربری که با کتابخانه تطبیقی ​​Material 3 ساخته شده است، آن را قادر می‌سازد تا به طور یکپارچه در سناریوهای چند پنجره‌ای مانند حالت تقسیم صفحه نمایش اندروید سازگار شود.

برای بهبود تجربه کاربری، پشتیبانی از کشیدن و رها کردن (drag and drop) را اضافه کرده‌ایم. در زیر نحوه پیاده‌سازی این قابلیت در Cahier را مشاهده می‌کنید.

بکشید و رها کنید

یک اپلیکیشن واقعاً سازنده یا خلاق، به صورت جداگانه کار نمی‌کند؛ بلکه به طور یکپارچه با بقیه اکوسیستم دستگاه در تعامل است. کشیدن و رها کردن (drag and drop) سنگ بنای این تعامل است، به خصوص در صفحه نمایش‌های بزرگ که کاربران اغلب در چندین پنجره برنامه کار می‌کنند. Cahier با پیاده‌سازی قابلیت کشیدن و رها کردن (drag and drop) برای افزودن و اشتراک‌گذاری محتوا، این امر را به طور کامل پذیرفته است.

  • وارد کردن آسان : کاربران می‌توانند تصاویر را از برنامه‌های دیگر - مانند مرورگر وب، گالری عکس یا مدیر فایل - بکشند و مستقیماً روی بوم یادداشت رها کنند. برای این کار، Cahier از اصلاح‌کننده dragAndDropTarget برای تعریف یک منطقه رها کردن، بررسی محتوای سازگار (مانند image/* ) و پردازش URI ورودی استفاده می‌کند.
  • اشتراک‌گذاری ساده : محتوای داخل Cahier به راحتی محتوای سایر برنامه‌ها قابل اشتراک‌گذاری است. کاربران می‌توانند روی یک تصویر در یک یادداشت متنی، یا روی کل بوم یک یادداشت طراحی و ترکیب تصویر، لمس طولانی داشته باشند و آن را به برنامه دیگری بکشند.

بررسی عمیق فنی: کشیدن از بوم نقاشی

پیاده‌سازی ژست کشیدن روی بوم نقاشی، چالش منحصر به فردی را ارائه می‌دهد. در DrawingSurface ما، کامپوننت‌هایی که ورودی نقاشی زنده ( InProgressStrokes از Ink API) را مدیریت می‌کنند و Box که ژست فشار طولانی برای شروع کشیدن را تشخیص می‌دهد، کامپوننت‌های خواهرخوانده هستند.

به طور پیش‌فرض، سیستم ورودی اشاره‌گر Jetpack Compose به گونه‌ای طراحی شده است که فقط یک composable خواهرخوانده - اولین مورد در ترتیب اعلان که با محل لمس همپوشانی دارد - رویداد را دریافت می‌کند. در مورد Cahier، ما می‌خواهیم منطق مدیریت ورودی کشیدن و رها کردن ما فرصتی برای اجرا و مصرف ورودی‌ها داشته باشد، قبل از اینکه Composable InProgressStrokes از تمام ورودی‌های مصرف نشده برای ترسیم استفاده کند و سپس آن ورودی را مصرف کند. اگر همه چیز را به ترتیب صحیح مرتب نکنیم، Box ما حرکت فشار طولانی را برای شروع کشیدن تشخیص نمی‌دهد، یا InProgressStrokes ورودی را برای ترسیم دریافت نمی‌کند.

برای حل این مشکل، ما یک اصلاح‌کننده‌ی سفارشی pointerInputWithSiblingFallthrough ایجاد کردیم و Box خود را با استفاده از آن اصلاح‌کننده قبل از InProgressStrokes در کد composable قرار دادیم. این ابزار یک پوشش نازک در اطراف سیستم استاندارد pointerInput است، اما با یک تغییر اساسی: تابع sharePointerInputWithSiblings() را برای برگرداندن true لغو می‌کند. این به چارچوب Compose می‌گوید که به رویدادهای اشاره‌گر اجازه دهد تا حتی پس از مصرف شدن، به composableهای همزاد منتقل شوند.

internal fun Modifier.pointerInputWithSiblingFallthrough(

    pointerInputEventHandler: PointerInputEventHandler

) = this then PointerInputSiblingFallthroughElement(pointerInputEventHandler)


private class PointerInputSiblingFallthroughModifierNode(

    pointerInputEventHandler: PointerInputEventHandler

) : PointerInputModifierNode, DelegatingNode() {


    var pointerInputEventHandler: PointerInputEventHandler

        get() = delegateNode.pointerInputEventHandler

        set(value) {

            delegateNode.pointerInputEventHandler = value

        }


    val delegateNode = delegate(

        SuspendingPointerInputModifierNode(pointerInputEventHandler)

    )


    override fun onPointerEvent(

        pointerEvent: PointerEvent,

        pass: PointerEventPass,

        bounds: IntSize

    ) {

        delegateNode.onPointerEvent(pointerEvent, pass, bounds)

    }


    override fun onCancelPointerInput() {

        delegateNode.onCancelPointerInput()

    }


    override fun sharePointerInputWithSiblings() = true

}


private data class PointerInputSiblingFallthroughElement(

    val pointerInputEventHandler: PointerInputEventHandler

) : ModifierNodeElement<PointerInputSiblingFallthroughModifierNode>() {


    override fun create() = PointerInputSiblingFallthroughModifierNode(pointerInputEventHandler)


    override fun update(node: PointerInputSiblingFallthroughModifierNode) {

        node.pointerInputEventHandler = pointerInputEventHandler

    }


    override fun InspectorInfo.inspectableProperties() {

        name = "pointerInputWithSiblingFallthrough"

        properties["pointerInputEventHandler"] = pointerInputEventHandler

    }

}

نحوه استفاده از آن در DrawingSurface به صورت زیر است:

Box(

    modifier = Modifier

        .fillMaxSize()

        // Our custom modifier enables this gesture to coexist with the drawing input.

        .pointerInputWithSiblingFallthrough {

            detectDragGesturesAfterLongPress(

                onDragStart = { onStartDrag() },

                onDrag = { _, _ -> /* consume drag events */ },

                onDragEnd = { /* No action needed */ }

            )

        }

) 

// The Ink API's composable for live drawing sits here as a sibling.

InProgressStrokes(...)

با این کار، سیستم به طور صحیح هم خطوط رسم و هم حرکت کشیدن با فشار طولانی را به طور همزمان تشخیص می‌دهد. پس از شروع کشیدن، ما یک content:// URI قابل اشتراک‌گذاری با FileProvider ایجاد می‌کنیم و URI را با استفاده از view.startDragAndDrop() به چارچوب کشیدن و رها کردن سیستم ارسال می‌کنیم. این راه‌حل، یک تجربه کاربری قوی و شهودی را تضمین می‌کند و نحوه غلبه بر تداخل‌های پیچیده حرکت در رابط‌های کاربری لایه‌ای را نشان می‌دهد.

ساخته شده با معماری مدرن

فراتر از API های خاص، Cahier الگوهای معماری حیاتی برای ساخت برنامه‌های کاربردی با کیفیت بالا و تطبیقی ​​را نشان می‌دهد.

لایه ارائه: Jetpack Compose و سازگاری

لایه ارائه (presentation layer) کاملاً با Jetpack Compose ساخته شده است. همانطور که گفته شد، Cahier از کتابخانه material3-adaptive برای سازگاری رابط کاربری استفاده می‌کند. مدیریت وضعیت (state management) از یک الگوی جریان داده یک‌طرفه (Unidirectional Data Flow) (UDF) پیروی می‌کند، و نمونه‌های ViewModel به عنوان محفظه‌های داده‌ای که اطلاعات یادداشت‌ها و وضعیت رابط کاربری را در خود نگه می‌دارند، استفاده می‌شوند.

لایه داده: مخازن و اتاق

برای لایه داده، Cahier از یک رابط NoteRepository برای انتزاع تمام عملیات داده استفاده می‌کند. این انتخاب طراحی به برنامه اجازه می‌دهد تا به طور واضح بین یک منبع داده محلی (Room) و یک backend از راه دور بالقوه در آینده جابجا شود. جریان داده برای عملی مانند ویرایش یک یادداشت ساده است:

  1. رابط کاربری Jetpack Compose یک متد را در ViewModel فعال می‌کند.
  2. ViewModel یادداشت را از NoteRepository دریافت می‌کند، منطق را مدیریت می‌کند و یادداشت به‌روزرسانی‌شده را به مخزن برمی‌گرداند.
  3. NoteRepository به‌روزرسانی را در پایگاه داده Room ذخیره می‌کند.

پشتیبانی جامع از ورودی‌ها

برای اینکه یک برنامه واقعاً قدرتمند در زمینه بهره‌وری باشد، باید انواع روش‌های ورودی را بدون نقص مدیریت کند. Cahier طوری ساخته شده است که با دستورالعمل‌های ورودی صفحه نمایش بزرگ سازگار باشد و از موارد زیر پشتیبانی می‌کند:

  • قلم: ادغام با رابط برنامه‌نویسی کاربردی Ink، عدم شناسایی اثر کف دست، ثبت نقش یادداشت، ورودی قلم در فیلدهای متنی و حالت فراگیر.
  • صفحه‌کلید: پشتیبانی از رایج‌ترین میانبرها و ترکیب‌های صفحه‌کلید (مانند ctrl+click، meta+click) و نمایش واضح فوکوس صفحه‌کلید.
  • ماوس و ترک‌پد: پشتیبانی از حالت‌های کلیک راست و شناور

پشتیبانی از تعاملات پیشرفته‌ی صفحه‌کلید، ماوس و ترک‌پد، تمرکز اصلی برای بهبودهای بیشتر است.

همین امروز شروع کنید

ما امیدواریم که Cahier به عنوان یک سکوی پرتاب برای برنامه عالی بعدی شما عمل کند. ما آن را به عنوان یک منبع جامع و متن‌باز ساخته‌ایم که نشان می‌دهد چگونه می‌توان یک رابط کاربری تطبیقی، APIهای قدرتمند مانند Ink و نقش یادداشت‌ها و یک معماری تطبیقی ​​مدرن را ترکیب کرد.

آماده شیرجه زدن هستید؟

  • کد را بررسی کنید : برای بررسی کدبیس Cahier و مشاهده اصول طراحی در عمل، به مخزن گیت‌هاب ما مراجعه کنید.
  • خودتان بسازید : از Cahier به عنوان پایه‌ای برای یادداشت‌برداری، نشانه‌گذاری اسناد یا برنامه‌های خلاقانه خود استفاده کنید.
  • مشارکت : ما از مشارکت‌های شما استقبال می‌کنیم! به ما کمک کنید تا Cahier را به منبعی حتی بهتر برای جامعه توسعه‌دهندگان اندروید تبدیل کنیم.

راهنماهای رسمی توسعه‌دهندگان را بررسی کنید و همین امروز ساخت اپلیکیشن بهره‌وری و خلاقیت نسل بعدی خود را شروع کنید. بی‌صبرانه منتظریم ببینیم چه چیزی خلق می‌کنید!

    نوشته شده توسط:

    ادامه مطلب

    ،

    چگونه‌ها

    معرفی Cahier: یک نمونه جدید اندروید گیت‌هاب برای بهره‌وری و خلاقیت در صفحه نمایش بزرگ

    مطالعه ۱۱ دقیقه‌ای
    Chris Assigbe
    مهندس روابط توسعه‌دهنده

    رابط برنامه‌نویسی کاربردی Ink اکنون در مرحله بتا است و آماده ادغام در برنامه شما می‌باشد. این نقطه عطف با بازخورد ارزشمند توسعه‌دهندگان امکان‌پذیر شده است و منجر به بهبود مستمر در عملکرد، پایداری و کیفیت بصری API شده است.

    برنامه‌های گوگل مانند Google Docs ، Pixel Studio ، Google Photos ، Chrome PDF ، Youtube Effect Maker و ویژگی‌های منحصر به فرد در اندروید مانند Circle to Search، همگی از جدیدترین APIها استفاده می‌کنند.

    برای بزرگداشت این نقطه عطف، مفتخریم که از عرضه Cahier ، یک نمونه برنامه یادداشت‌برداری جامع که برای دستگاه‌های اندرویدی در همه اندازه‌ها، به‌ویژه تبلت‌ها و تلفن‌های تاشو، بهینه شده است، خبر دهیم.

    کایه چیست؟

    Cahier (به معنی دفترچه یادداشت در زبان فرانسوی) یک برنامه نمونه است که برای نشان دادن چگونگی ساخت برنامه‌ای طراحی شده است که به کاربران امکان می‌دهد افکار خود را با ترکیب متن، نقاشی و تصاویر ثبت و سازماندهی کنند.

    این نمونه می‌تواند به عنوان مرجعی برای افزایش بهره‌وری و خلاقیت کاربر در صفحه نمایش‌های بزرگ عمل کند. این نمونه، بهترین شیوه‌ها را برای ساخت چنین تجربیاتی، تسریع درک توسعه‌دهندگان و پذیرش APIها و تکنیک‌های قدرتمند مرتبط، به نمایش می‌گذارد. این پست شما را با ویژگی‌های اصلی Cahier، APIهای کلیدی و تصمیمات معماری که این نمونه را به مرجعی عالی برای برنامه‌های شما تبدیل می‌کند، آشنا می‌کند.

    ویژگی‌های کلیدی نشان داده شده در نمونه عبارتند از:

    • ایجاد یادداشت‌های چندمنظوره: نحوه پیاده‌سازی یک سیستم ایجاد محتوای انعطاف‌پذیر را نشان می‌دهد که از قالب‌های مختلف در یک یادداشت واحد، از جمله متن، نقشه‌های آزاد و پیوست‌های تصویر، پشتیبانی می‌کند.
    • ابزارهای خلاقانه‌ی جوهرسازی : با استفاده از Ink API، تجربه‌ی طراحی با کارایی بالا و تأخیر کم را پیاده‌سازی می‌کند. این نمونه، مثالی عملی از ادغام قلم‌موهای مختلف، انتخابگر رنگ، قابلیت لغو/بازگرداندن کار و ابزار پاک‌کن را ارائه می‌دهد.
    • یکپارچه‌سازی روان محتوا با کشیدن و رها کردن : نحوه مدیریت محتوای ورودی و خروجی را با استفاده از کشیدن و رها کردن نشان می‌دهد. این شامل پذیرش تصاویر رها شده از برنامه‌های دیگر و فعال کردن امکان بیرون کشیدن محتوا از برنامه شما برای اشتراک‌گذاری یکپارچه می‌شود.
    • سازماندهی یادداشت‌ها : یادداشت‌ها را برای دسترسی سریع به عنوان موارد دلخواه علامت‌گذاری کنید. برای مرتب ماندن، نمای آنها را فیلتر کنید.
    • معماری آفلاین: با معماری آفلاین با استفاده از Room ساخته شده است و تضمین می‌کند که تمام داده‌ها به صورت محلی ذخیره می‌شوند و برنامه بدون اتصال به اینترنت کاملاً کاربردی باقی می‌ماند.
    • پشتیبانی قدرتمند از چند پنجره و چند نمونه : نحوه پشتیبانی از چند نمونه را نشان می‌دهد و به برنامه شما اجازه می‌دهد در چندین پنجره اجرا شود تا کاربران بتوانند روی یادداشت‌های مختلف در کنار هم کار کنند و بهره‌وری و خلاقیت را در صفحه نمایش‌های بزرگ افزایش دهند.
    • رابط کاربری تطبیق‌پذیر برای همه صفحه نمایش‌ها : رابط کاربری با استفاده از ListDetailPaneScaffold و NavigationSuiteScaffold به طور یکپارچه با اندازه‌ها و جهت‌های مختلف صفحه نمایش سازگار می‌شود تا یک تجربه کاربری بهینه در تلفن‌ها، تبلت‌ها و دستگاه‌های تاشو ارائه دهد.
    • یکپارچه‌سازی عمیق سیستم : راهنمایی در مورد چگونگی تبدیل برنامه خود به برنامه یادداشت‌برداری پیش‌فرض در اندروید ۱۴ و بالاتر با پاسخ به اهداف یادداشت‌های سراسری سیستم، که امکان ضبط سریع محتوا از نقاط ورودی مختلف سیستم را فراهم می‌کند، ارائه می‌دهد.

    ساخته شده برای بهره‌وری و خلاقیت در صفحه نمایش‌های بزرگ

    برای عرضه اولیه، ما تمرکز اطلاعیه را بر روی چند ویژگی اصلی قرار داده‌ایم که Cahie r را به یک منبع یادگیری کلیدی برای موارد استفاده از بهره‌وری و خلاقیت تبدیل می‌کند.

    پایه و اساس سازگاری

    Cahier از پایه طوری ساخته شده که قابلیت تطبیق‌پذیری داشته باشد. این نمونه از کتابخانه‌های material3-adaptive، به ویژه ListDetailPaneScaffold و NavigationSuiteScaffold، برای تطبیق یکپارچه طرح‌بندی برنامه با اندازه‌ها و جهت‌های مختلف صفحه نمایش استفاده می‌کند. این یک عنصر حیاتی برای یک برنامه اندروید مدرن است و Cahier نمونه‌ای واضح از نحوه پیاده‌سازی مؤثر آن ارائه می‌دهد.

    رابط کاربری تطبیقی ​​Cahier که با کتابخانه تطبیقی ​​Material 3 ساخته شده است..gif

    رابط کاربری تطبیقی ​​Cahier که با کتابخانه تطبیقی ​​Material 3 ساخته شده است

    نمایش APIها و یکپارچه‌سازی‌های کلیدی

    این نمونه بر نمایش APIهای قدرتمند بهره‌وری متمرکز است که می‌توانید در برنامه‌های خود از آنها استفاده کنید، از جمله:

    نگاهی دقیق‌تر به APIهای کلیدی

    بیایید عمیق‌تر به دو مورد از APIهای اساسی که Cahier برای ارائه یک تجربه یادداشت‌برداری درجه یک در خود ادغام کرده است، بپردازیم.

    ایجاد تجربه‌های طبیعی در زمینه‌ی جوهرسازی با Ink API

    ورودی قلم، دستگاه‌های صفحه نمایش بزرگ را به نوت‌بوک‌ها و دفترچه‌های طراحی دیجیتال تبدیل می‌کند. برای کمک به شما در ساخت تجربیات روان و طبیعی در زمینه‌ی طراحی با قلم، ما Ink API را به سنگ بنای نمونه تبدیل کرده‌ایم. Ink API ایجاد، رندر و دستکاری خطوط قلم زیبا را با بهترین کیفیت و کمترین تأخیر در کلاس خود آسان می‌کند.

    Ink API یک معماری ماژولار ارائه می‌دهد، بنابراین می‌توانید آن را با نیازهای خاص برنامه خود تطبیق دهید. ماژول‌های API شامل موارد زیر هستند:

    • ماژول‌های نوشتن ( نوشتن - نمایش‌ها ): ورودی جوهر را به صورت آنی مدیریت می‌کنند تا خطوط روان با کمترین تأخیری که یک دستگاه می‌تواند ارائه دهد، ایجاد شود.
      • در DrawingSurface ، Cahier از InProgressStrokes که به تازگی معرفی شده است، برای مدیریت ورودی‌های لمسی یا قلم نوری به صورت بلادرنگ استفاده می‌کند. این ماژول مسئول ثبت رویدادهای اشاره‌گر و رندر کردن ضربات جوهر خیس با کمترین تأخیر ممکن است.
    • ماژول Strokes : ورودی جوهر و نمایش بصری آن را نشان می‌دهد. وقتی کاربر رسم یک خط را تمام می‌کند، تابع onStrokesFinished یک شیء Stroke نهایی/خشک شده را به برنامه ارائه می‌دهد. این شیء تغییرناپذیر، که نشان دهنده‌ی خط جوهر تکمیل شده است، سپس در DrawingCanvasViewModel مدیریت می‌شود.
    • ماژول رندرینگ: به طور کارآمد خطوط جوهر را نمایش می‌دهد و به آنها اجازه می‌دهد تا با Jetpack Compose یا نماهای اندروید ترکیب شوند.
      • برای نمایش خطوط موجود و خطوط تازه خشک‌شده، Cahier از CanvasStrokeRenderer در DrawingSurface برای طراحی فعال و از DrawingDetailPanePreview برای نمایش پیش‌نمایش استاتیک یادداشت استفاده می‌کند. این ماژول به طور مؤثر اشیاء Stroke را روی یک Canvas ترسیم می‌کند.
    • ماژول‌های قلم‌مو ( Compose - views ): روشی اعلانی برای تعریف سبک بصری خطوط ارائه می‌دهند. به‌روزرسانی‌های اخیر (از زمان انتشار alpha03) شامل یک قلم‌مو با خط چین جدید است که به ویژه برای ویژگی‌هایی مانند انتخاب با لاسو مفید است. DrawingCanvasViewModel حالت قلم‌مو فعلی را نگه می‌دارد. یک جعبه ابزار در DrawingCanvas به کاربران امکان می‌دهد خانواده‌های قلم‌موهای مختلف (مانند StockBrushes.pressurePen() یا StockBrushes.highlighter() ) را انتخاب کرده و رنگ‌ها را تغییر دهند. ViewModel شیء قلم‌مو را به‌روزرسانی می‌کند، که سپس توسط InProgressStrokes قابل ترکیب برای خطوط جدید استفاده می‌شود.
    • ماژول‌های هندسه ( ایجاد - نمایش ): از دستکاری و تجزیه و تحلیل خطوط برای قابلیت‌هایی مانند پاک کردن و انتخاب پشتیبانی می‌کنند.
      • ابزار پاک‌کن در جعبه ابزار و عملکرد آن در DrawingCanvasViewModel به ماژول geometry متکی است. وقتی پاک‌کن فعال است، یک MutableParallelogram در اطراف مسیر حرکت کاربر ایجاد می‌کند. سپس پاک‌کن تقاطع‌های بین شکل و کادرهای محدودکننده‌ی خطوط موجود را بررسی می‌کند تا مشخص کند کدام خطوط را باید پاک کند، که باعث می‌شود پاک‌کن بصری و دقیق به نظر برسد.
    • ماژول ذخیره‌سازی : قابلیت‌های سریال‌سازی و حذف سریال‌سازی کارآمد برای داده‌های ink را فراهم می‌کند که منجر به صرفه‌جویی قابل توجه در اندازه دیسک و شبکه می‌شود. برای ذخیره ترسیمات، Cahier اشیاء Stroke را در پایگاه داده Room خود حفظ می‌کند. در Converters ، نمونه از تابع رمزگذاری ماژول ذخیره‌سازی برای سریال‌سازی StrokeInputBatch (داده‌های خام نقطه‌ای) در یک ByteArray استفاده می‌کند. آرایه بایت، همراه با ویژگی‌های قلم‌مو، به عنوان یک رشته JSON ذخیره می‌شود. تابع رمزگشایی برای بازسازی strokeها هنگام بارگذاری یک یادداشت استفاده می‌شود.
    اوریون.png

    فراتر از این ماژول‌های اصلی، به‌روزرسانی‌های اخیر قابلیت‌های Ink API را گسترش داده‌اند:

    • APIهای آزمایشی جدید برای اشیاء سفارشی BrushFamily به توسعه‌دهندگان این امکان را می‌دهد تا انواع قلم‌موهای خلاقانه و منحصر به فرد ایجاد کنند و امکاناتی مانند قلم‌موهای Pencil و Laser Pointer را فراهم کنند.

    کایه از قلم‌موهای سفارشی، از جمله قلم‌مو موسیقی منحصر به فرد که در زیر نمایش داده شده است، برای نشان دادن امکانات خلاقانه پیشرفته استفاده می‌کند.

    لیزر رنگین‌کمانی که با قلم‌موهای سفارشی Ink API ساخته شده است..gif

    لیزر رنگین‌کمانی که با قلم‌موهای سفارشی Ink API ساخته شده است

    یادداشت‌ها.png

    قلم‌مو موسیقی با قلم‌موهای سفارشی Ink API ساخته شده است

    • ماژول‌های بومی Jetpack Compose برای ایجاد قابلیت همکاری، ادغام قابلیت‌های inking را مستقیماً در رابط‌های کاربری Compose شما ساده می‌کنند تا یک تجربه توسعه اصطلاحاً کاربردی‌تر و کارآمدتر ایجاد شود.

    Ink API مزایای متعددی ارائه می‌دهد که آن را به انتخابی ایده‌آل برای برنامه‌های بهره‌وری و خلاقیت نسبت به پیاده‌سازی سفارشی تبدیل می‌کند:

    • سهولت استفاده: رابط برنامه‌نویسی کاربردی Ink پیچیدگی‌های گرافیک و هندسه را کنار می‌گذارد و به شما امکان می‌دهد روی ویژگی‌های اصلی Cahier تمرکز کنید.
    • عملکرد: پشتیبانی داخلی از تأخیر کم و رندر بهینه، تجربه‌ی روان و واکنش‌گرای جوهرافشانی را تضمین می‌کند.
    • انعطاف‌پذیری: طراحی ماژولار به شما امکان می‌دهد اجزای مورد نیاز را انتخاب و گزینش کنید، که این امر امکان ادغام یکپارچه‌ی Ink API را در معماری Cahier فراهم می‌کند.

    رابط برنامه‌نویسی کاربردی Ink در حال حاضر در بسیاری از برنامه‌های گوگل، از جمله برای نشانه‌گذاری در Docs و Circle to Search و همچنین برنامه‌های همکار مانند Orion Notes و PDF Scanner ، به کار گرفته شده است.

    «Ink API اولین انتخاب ما برای Circle-to-Search (CtS) بود. با استفاده از مستندات گسترده آنها، ادغام Ink API بسیار آسان بود و به ما این امکان را داد که تنها در عرض یک هفته به اولین نمونه اولیه کاربردی خود برسیم. پشتیبانی از بافت قلم موی سفارشی و انیمیشن Ink به ما این امکان را داد که به سرعت طراحی stroke را تکرار کنیم.» - جردن کومودا، مهندس نرم‌افزار - گوگل

    تبدیل شدن به برنامه یادداشت‌های پیش‌فرض با نقش یادداشت‌ها

    یادداشت‌برداری یک قابلیت اصلی است که بهره‌وری کاربر را در دستگاه‌های صفحه نمایش بزرگ افزایش می‌دهد. با ویژگی نقش یادداشت‌ها ، کاربران می‌توانند از صفحه قفل یا در حالی که سایر برنامه‌ها در حال اجرا هستند، به برنامه‌های سازگار شما دسترسی داشته باشند. این ویژگی برنامه‌های یادداشت‌برداری پیش‌فرض در سطح سیستم را شناسایی و تنظیم می‌کند و به آنها اجازه می‌دهد تا برای ضبط محتوا راه‌اندازی شوند.

    پیاده‌سازی در Cahier

    پیاده‌سازی نقش یادداشت‌ها شامل چند مرحله کلیدی است که همه در نمونه نشان داده شده‌اند:

    1. اعلان مانیفست : ابتدا، برنامه باید قابلیت خود را برای مدیریت intentهای یادداشت‌برداری اعلام کند. در AndroidManifest.xml ، Cahier یک <intent-filter> برای اکشن android.intent.action.CREATE_NOTE قرار می‌دهد. این به سیستم نشان می‌دهد که برنامه یک کاندید بالقوه برای نقش یادداشت‌ها است.
    2. بررسی وضعیت نقش : SettingsViewModel از RoleManager اندروید برای تعیین وضعیت فعلی استفاده می‌کند. SettingsViewModel بررسی می‌کند که آیا نقش notes در دستگاه موجود است ( isRoleAvailable ) و آیا Cahier در حال حاضر آن نقش را دارد ( isRoleHeld ). این وضعیت با استفاده از جریان‌های Kotlin در معرض رابط کاربری قرار می‌گیرد.
    3. Requesting the role : In the Settings.kt file, a Button is displayed to the user if the role is available but not held. When clicked, the button calls the requestNotesRole function in the ViewModel. The function creates an intent to open the default app settings screen where the user can select Cahier. The process is managed using the rememberLauncherForActivityResult API, which handles launching the intent and receiving the result.
    4. Updating the UI : After the user returns from the settings screen, the ActivityResultLauncher callback triggers a function in the ViewModel to update the role status, ensuring the UI accurately reflects whether the app is now the default.

    Learn how to integrate the notes role in your app in our create a note-taking app guide .

    helloworld.png

    Cahier launched in a floating window as the default note-taking app on a Lenovo tablet

    A major step forward: Lenovo enables notes role

    We're thrilled to announce a major step forward for large screen Android productivity: Lenovo has enabled support for Notes Role on tablets running Android 15 and higher! With this update, you can now update your note-taking apps to allow users with compatible Lenovo devices to set them as default, granting seamless access from the lock screen and unlocking system level content capture features.

    This commitment from a leading OEM demonstrates the growing importance of the notes role in delivering a truly integrated and productive user experience on Android.

    Multi-instance, multi-windowing, and desktop windowing

    Productivity on a large screen is all about managing information and workflows efficiently. That's why Cahier is built to fully embrace Android's advanced windowing capabilities, providing a flexible workspace that adapts to user needs. The app supports:

    • Multi-windowing : The fundamental ability to run alongside another app in split-screen or free-form mode. This is essential for tasks like referencing a web page while taking notes in Cahier.
    • Multi-instance : This is where true multitasking shines. Cahier allows users to open multiple, independent windows of the app simultaneously . Imagine comparing two different notes side by side or referencing a text note in one window while working on a drawing in another. Cahier demonstrates how to manage these separate instances, each with its own state, turning your app into a powerful, multifaceted tool.
    • Desktop windowing : When connected to an external display, Android desktop mode transforms a tablet or foldable into a workstation. Because Cahier is built with an adaptive UI and supports multi-instance, the app performs beautifully in this environment. Users can open, resize, and position multiple Cahier windows just like on a traditional desktop, enabling complex workflows that were previously out of reach on mobile devices.
    cahier-desktop-windowing.webp

    Cahier running in desktop window mode on Pixel Tablet

    Here's how we implemented these features in Cahier:

    To enable multi-instance, we first needed to signal to the system that the app supports being launched multiple times by adding the PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI property to MainActivity 's declaration in AndroidManifest :

    <activity
    
        android:name="com.example.cahier.MainActivity"
    
        android:exported="true"
    
        android:label="@string/app_name"
    
        android:theme="@style/Theme.MyApplication"
    
        android:showWhenLocked="true"
    
        android:turnScreenOn="true"
    
        android:resizeableActivity="true"
    
        android:launchMode="singleInstancePerTask">
    
    
        <property
    
            android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
    
            android:value="true"/>
    
        ...
    
    </activity>

    Next, we implemented the logic to launch a new instance of the app. In CahierHomeScreen.kt , when a user opts to open a note in a new window, we create a new Intent with specific flags that instruct the system on how to handle the new activity launch. The combination of FLAG_ACTIVITY_NEW_TASK , FLAG_ACTIVITY_MULTIPLE_TASK , and FLAG_ACTIVITY_LAUNCH_ADJACENT ensures the note opens in a new, separate window alongside the existing one.

    fun openNewWindow(activity: Activity?, note: Note) {
    
        val intent = Intent(activity, MainActivity::class.java)
    
        intent.putExtra(AppArgs.NOTE_TYPE_KEY, note.type)
    
        intent.putExtra(AppArgs.NOTE_ID_KEY, note.id)
    
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
    
            Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
    
    
        activity?.startActivity(intent)
    
    }

    To support multi-window mode, we needed to signal to the system that the app supports resizability by setting the Manifest's <activity> or <application> element.

    <activity
    
        android:name="com.example.cahier.MainActivity"
    
        android:resizeableActivity="true"
    
        ...>
    
    </activity>

    The UI itself being built with the Material 3 adaptive library enables it to adapt seamlessly in multi-window scenarios like Android's split screen mode.

    To enhance user experience, we added support for drag and drop. See below how we implemented this in Cahier.

    بکشید و رها کنید

    A truly productive or creative app doesn't function in isolation; it interacts seamlessly with the rest of the device's ecosystem. Drag and drop is a cornerstone of this interaction, especially on large screens where users are often working across multiple app windows. Cahier fully embraces this by implementing intuitive drag and drop functionality for both adding and sharing content.

    • Effortless Importing : Users can drag images from other applications—like a web browser, photo gallery, or file manager—and drop them directly onto a note canvas. For this, Cahier uses the dragAndDropTarget modifier to define a drop zone, check for compatible content (like image/* ), and process the incoming URI.
    • Simple sharing : Content inside Cahier is just as easy to share as content from other apps. Users can long-press an image within a text note, or long-press the entire canvas of a drawing note and image composite, and drag it out to another application.

    Technical deep dive: Dragging from the drawing canvas

    Implementing the drag gesture on the drawing canvas presents a unique challenge. In our DrawingSurface , the composables that handle live drawing input (the Ink API's InProgressStrokes ) and the Box that detects the long-press gesture to initiate a drag are sibling composables .

    By default, the Jetpack Compose pointer input system is designed so that just one sibling composable —the first one in declaration order that overlaps the touch location—receives the event. In Cahier's case, we want our drag-and-drop input handling logic to have a chance to run and potentially consume inputs before the InProgressStrokes composable uses all unconsumed input for drawing and then consumes that input. If we don't arrange things in the right order, our Box won't detect the long-press gesture to start a drag, or InProgressStrokes won't receive the input to draw.

    To solve this, we created a custom pointerInputWithSiblingFallthrough modifier, and we put our Box using that modifier before InProgressStrokes in the composable code. This utility is a thin wrapper around the standard pointerInput system but with one critical change: it overrides the sharePointerInputWithSiblings() function to return true . This tells the Compose framework to allow pointer events to pass through to sibling composables, even after being consumed.

    internal fun Modifier.pointerInputWithSiblingFallthrough(
    
        pointerInputEventHandler: PointerInputEventHandler
    
    ) = this then PointerInputSiblingFallthroughElement(pointerInputEventHandler)
    
    
    private class PointerInputSiblingFallthroughModifierNode(
    
        pointerInputEventHandler: PointerInputEventHandler
    
    ) : PointerInputModifierNode, DelegatingNode() {
    
    
        var pointerInputEventHandler: PointerInputEventHandler
    
            get() = delegateNode.pointerInputEventHandler
    
            set(value) {
    
                delegateNode.pointerInputEventHandler = value
    
            }
    
    
        val delegateNode = delegate(
    
            SuspendingPointerInputModifierNode(pointerInputEventHandler)
    
        )
    
    
        override fun onPointerEvent(
    
            pointerEvent: PointerEvent,
    
            pass: PointerEventPass,
    
            bounds: IntSize
    
        ) {
    
            delegateNode.onPointerEvent(pointerEvent, pass, bounds)
    
        }
    
    
        override fun onCancelPointerInput() {
    
            delegateNode.onCancelPointerInput()
    
        }
    
    
        override fun sharePointerInputWithSiblings() = true
    
    }
    
    
    private data class PointerInputSiblingFallthroughElement(
    
        val pointerInputEventHandler: PointerInputEventHandler
    
    ) : ModifierNodeElement<PointerInputSiblingFallthroughModifierNode>() {
    
    
        override fun create() = PointerInputSiblingFallthroughModifierNode(pointerInputEventHandler)
    
    
        override fun update(node: PointerInputSiblingFallthroughModifierNode) {
    
            node.pointerInputEventHandler = pointerInputEventHandler
    
        }
    
    
        override fun InspectorInfo.inspectableProperties() {
    
            name = "pointerInputWithSiblingFallthrough"
    
            properties["pointerInputEventHandler"] = pointerInputEventHandler
    
        }
    
    }

    Here's how it's used in DrawingSurface :

    Box(
    
        modifier = Modifier
    
            .fillMaxSize()
    
            // Our custom modifier enables this gesture to coexist with the drawing input.
    
            .pointerInputWithSiblingFallthrough {
    
                detectDragGesturesAfterLongPress(
    
                    onDragStart = { onStartDrag() },
    
                    onDrag = { _, _ -> /* consume drag events */ },
    
                    onDragEnd = { /* No action needed */ }
    
                )
    
            }
    
    ) 
    
    // The Ink API's composable for live drawing sits here as a sibling.
    
    InProgressStrokes(...)

    With this in place, the system correctly detects both the drawing strokes and the long-press drag gesture simultaneously. Once the drag is initiated, we create a shareable content:// URI with FileProvider and pass the URI to the system's drag and drop framework using view.startDragAndDrop() . This solution ensures a robust and intuitive user experience, showcasing how to overcome complex gesture conflicts in layered UIs.

    Built with modern architecture

    Beyond specific APIs, Cahier demonstrates crucial architectural patterns for building high-quality, adaptive applications.

    The presentation layer: Jetpack Compose and adaptability

    The presentation layer is built entirely with Jetpack Compose. As mentioned, Cahier adopts the material3-adaptive library for UI adaptability. State management follows a strict Unidirectional Data Flow (UDF) pattern, with ViewModel instances used as data containers that hold note information and UI state.

    The data layer: Repositories and Room

    For the data layer, Cahier uses a NoteRepository interface to abstract all data operations. This design choice cleanly allows the app to swap between a local data source (Room) and a potential future remote backend. The data flow for an action like editing a note is straightforward:

    1. The Jetpack Compose UI triggers a method in the ViewModel.
    2. The ViewModel fetches the note from NoteRepository , handles the logic, and passes the updated note back to the repository.
    3. NoteRepository saves the update to a Room database.

    Comprehensive input support

    To be a true productivity powerhouse, an app must handle a variety of input methods flawlessly. Cahier is built to be compliant with large screen input guidelines and supports:

    • Stylus: Integration with the Ink API, palm rejection, registration for the notes role, stylus input in text fields, and immersive mode.
    • Keyboard: Support for most common keyboard shortcuts and combinations (like ctrl+click, meta+click) and clear indication for keyboard focus.
    • Mouse and trackpad: Support for right-click and hover states.

    Support for advanced keyboard, mouse, and trackpad interactions is a key focus for further improvements.

    همین امروز شروع کنید

    We hope Cahier serves as a launchpad for your next great app. We built it to be a comprehensive, open source resource that demonstrates how to combine an adaptive UI, powerful APIs like Ink and notes role, and a modern, adaptive architecture.

    آماده شیرجه زدن هستید؟

    • Explore the code : Head over to our GitHub repository to explore the Cahier codebase and see the design principles in action.
    • Build your own : Use Cahier as a foundation for your own note-taking, document markup, or creative application.
    • Contribute : We welcome your contributions! Help us make Cahier an even better resource for the Android developer community.

    Check out the official developer guides and start building your next generation productivity and creativity app today. We can't wait to see what you create!

      نوشته شده توسط:

      ادامه مطلب