کپی و پیست کنید

روش نوشتن را امتحان کنید
Jetpack Compose ابزار رابط کاربری پیشنهادی برای اندروید است. یاد بگیرید که چگونه از کپی و چسباندن در Compose استفاده کنید.

اندروید یک چارچوب قدرتمند مبتنی بر کلیپ‌بورد برای کپی و پیست کردن ارائه می‌دهد. این چارچوب از انواع داده‌های ساده و پیچیده، از جمله رشته‌های متنی، ساختارهای داده پیچیده، داده‌های متنی و جریان دودویی و دارایی‌های برنامه پشتیبانی می‌کند. داده‌های متنی ساده مستقیماً در کلیپ‌بورد ذخیره می‌شوند، در حالی که داده‌های پیچیده به عنوان مرجعی ذخیره می‌شوند که برنامه پیست کننده با یک ارائه دهنده محتوا آن را حل می‌کند. کپی و پیست کردن هم در داخل یک برنامه و هم بین برنامه‌هایی که این چارچوب را پیاده‌سازی می‌کنند، کار می‌کند.

از آنجایی که بخشی از چارچوب از ارائه دهندگان محتوا استفاده می‌کند، این سند فرض را بر آشنایی با رابط برنامه‌نویسی کاربردی ارائه دهنده محتوای اندروید (Android Content Provider API) می‌گذارد که در بخش ارائه دهندگان محتوا (Content providers) توضیح داده شده است.

کاربران هنگام کپی کردن محتوا در کلیپ‌بورد انتظار بازخورد دارند، بنابراین علاوه بر چارچوبی که کپی و چسباندن را پشتیبانی می‌کند، اندروید در اندروید ۱۳ (سطح API ۳۳) و بالاتر، هنگام کپی کردن، یک رابط کاربری پیش‌فرض به کاربران نشان می‌دهد. به دلیل این ویژگی، خطر اعلان تکراری وجود دارد. می‌توانید در بخش «جلوگیری از اعلان‌های تکراری» درباره این مورد حاشیه‌ای بیشتر بدانید.

انیمیشنی که اعلان کلیپ‌بورد اندروید ۱۳ را نشان می‌دهد
شکل ۱. رابط کاربری نشان داده شده هنگام ورود محتوا به کلیپ‌بورد در اندروید ۱۳ و بالاتر.

هنگام کپی کردن در اندروید ۱۲L (سطح API ۳۲) و پایین‌تر، به صورت دستی به کاربران بازخورد ارائه دهید. به توصیه‌های این سند مراجعه کنید.

چارچوب کلیپ‌بورد

وقتی از چارچوب کلیپ‌بورد استفاده می‌کنید، داده‌ها را در یک شیء کلیپ قرار می‌دهید و سپس شیء کلیپ را در کلیپ‌بورد سراسری سیستم قرار می‌دهید. شیء کلیپ می‌تواند یکی از سه شکل زیر را داشته باشد:

متن
یک رشته متنی. رشته را مستقیماً در شیء کلیپ قرار دهید، که سپس آن را در کلیپ‌بورد قرار می‌دهید. برای چسباندن رشته، شیء کلیپ را از کلیپ‌بورد بگیرید و رشته را در حافظه برنامه خود کپی کنید.
یو آر آی
یک شیء Uri که هر نوع URI را نشان می‌دهد. این در درجه اول برای کپی کردن داده‌های پیچیده از یک ارائه دهنده محتوا است. برای کپی کردن داده‌ها، یک شیء Uri را در یک شیء کلیپ قرار دهید و شیء کلیپ را در کلیپ بورد قرار دهید. برای چسباندن داده‌ها، شیء کلیپ را دریافت کنید، شیء Uri را دریافت کنید، آن را به یک منبع داده، مانند یک ارائه دهنده محتوا، تبدیل کنید و داده‌ها را از منبع به فضای ذخیره‌سازی برنامه خود کپی کنید.
قصد
یک Intent . این از کپی کردن میانبرهای برنامه پشتیبانی می‌کند. برای کپی کردن داده‌ها، یک Intent ایجاد کنید، آن را در یک شیء کلیپ قرار دهید و شیء کلیپ را در کلیپ‌بورد قرار دهید. برای چسباندن داده‌ها، شیء کلیپ را دریافت کرده و سپس شیء Intent را در قسمت حافظه برنامه خود کپی کنید.

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

اگر می‌خواهید به کاربران اجازه دهید داده‌ها را در برنامه شما پیست کنند، لازم نیست همه انواع داده‌ها را مدیریت کنید. می‌توانید قبل از اینکه به کاربران گزینه پیست کردن را بدهید، داده‌های موجود در کلیپ‌بورد را بررسی کنید. شیء کلیپ علاوه بر داشتن یک فرم داده خاص، حاوی فراداده‌هایی نیز هست که به شما می‌گوید چه نوع MIMEهایی در دسترس هستند. این فراداده‌ها به شما کمک می‌کنند تصمیم بگیرید که آیا برنامه شما می‌تواند کار مفیدی با داده‌های کلیپ‌بورد انجام دهد یا خیر. به عنوان مثال، اگر برنامه‌ای دارید که در درجه اول متن را مدیریت می‌کند، ممکن است بخواهید اشیاء کلیپی را که حاوی URI یا intent هستند، نادیده بگیرید.

همچنین ممکن است بخواهید به کاربران اجازه دهید متن را صرف نظر از نوع داده در کلیپ‌بورد، پیست کنند. برای انجام این کار، داده‌های کلیپ‌بورد را به یک نمایش متنی وارد کنید و سپس این متن را پیست کنید. این مورد در بخش «اجبار به تبدیل کلیپ‌بورد به متن» توضیح داده شده است.

کلاس‌های کلیپ‌بورد

این بخش کلاس‌های مورد استفاده توسط چارچوب کلیپ‌بورد را شرح می‌دهد.

مدیر کلیپ‌بورد

حافظه موقت سیستم اندروید توسط کلاس سراسری ClipboardManager نمایش داده می‌شود. این کلاس را مستقیماً نمونه‌سازی نکنید. در عوض، با فراخوانی getSystemService(CLIPBOARD_SERVICE) به آن ارجاع دهید.

ClipData، ClipData.Item و ClipDescription

برای افزودن داده به کلیپ‌بورد، یک شیء ClipData ایجاد کنید که شامل توضیحی از داده‌ها و خود داده‌ها باشد. کلیپ‌بورد در هر زمان یک ClipData در خود جای می‌دهد. یک ClipData شامل یک شیء ClipDescription و یک یا چند شیء ClipData.Item است.

یک شیء ClipDescription شامل فراداده‌هایی در مورد کلیپ است. به طور خاص، شامل آرایه‌ای از انواع MIME موجود برای داده‌های کلیپ است. علاوه بر این، در اندروید ۱۲ (سطح API ۳۱) و بالاتر، فراداده شامل اطلاعاتی در مورد اینکه آیا شیء حاوی متن استایل‌بندی شده است یا خیر و در مورد نوع متن موجود در شیء است. وقتی یک کلیپ را در کلیپ‌بورد قرار می‌دهید، این اطلاعات برای برنامه‌های چسباندن در دسترس است که می‌توانند بررسی کنند که آیا می‌توانند داده‌های کلیپ را مدیریت کنند یا خیر.

یک شیء ClipData.Item شامل متن، URI یا داده‌های intent است:

متن
یک CharSequence )
یو آر آی
یک Uri . این معمولاً شامل یک URI ارائه دهنده محتوا است، اگرچه هر URI مجاز است. برنامه‌ای که داده‌ها را ارائه می‌دهد، URI را در کلیپ‌بورد قرار می‌دهد. برنامه‌هایی که می‌خواهند داده‌ها را جای‌گذاری کنند، URI را از کلیپ‌بورد دریافت کرده و از آن برای دسترسی به ارائه دهنده محتوا یا منبع داده دیگر و بازیابی داده‌ها استفاده می‌کنند.
قصد
یک Intent . این نوع داده به شما امکان می‌دهد میانبر یک برنامه را در کلیپ‌بورد کپی کنید. سپس کاربران می‌توانند میانبر را برای استفاده بعدی در برنامه‌های خود جایگذاری کنند.

شما می‌توانید بیش از یک شیء ClipData.Item را به یک کلیپ اضافه کنید. این به کاربران اجازه می‌دهد چندین انتخاب را به عنوان یک کلیپ واحد کپی و پیست کنند. برای مثال، اگر یک ویجت لیست دارید که به کاربر اجازه می‌دهد بیش از یک مورد را همزمان انتخاب کند، می‌توانید همه موارد را به طور همزمان در کلیپ‌بورد کپی کنید. برای انجام این کار، برای هر مورد لیست یک ClipData.Item جداگانه ایجاد کنید و سپس اشیاء ClipData.Item به شیء ClipData اضافه کنید.

روش‌های راحتی ClipData

کلاس ClipData متدهای استاتیک و راحتی را برای ایجاد یک شیء ClipData با یک شیء ClipData.Item و یک شیء ساده ClipDescription ارائه می‌دهد:

newPlainText(label, text)
یک شیء ClipData برمی‌گرداند که شیء ClipData.Item آن حاوی یک رشته متنی است. برچسب شیء ClipDescription روی label تنظیم شده است. نوع MIME واحد در ClipDescription MIMETYPE_TEXT_PLAIN است.

برای ایجاد یک کلیپ از یک رشته متنی، از newPlainText() استفاده کنید.

newUri(resolver, label, URI)
یک شیء ClipData را برمی‌گرداند که شیء ClipData.Item آن حاوی یک URI است. برچسب شیء ClipDescription روی label تنظیم شده است. اگر URI یک content URI باشد - یعنی اگر Uri.getScheme() content: برگرداند - این متد از شیء ContentResolver ارائه شده در resolver برای بازیابی انواع MIME موجود از ارائه دهنده محتوا استفاده می‌کند. سپس آنها را در ClipDescription ذخیره می‌کند. برای URI که content: URI نیست، متد نوع MIME را روی MIMETYPE_TEXT_URILIST تنظیم می‌کند.

از newUri() برای ایجاد یک کلیپ از یک URI - به ویژه یک content: URI - استفاده کنید.

newIntent(label, intent)
یک شیء ClipData برمی‌گرداند که شیء ClipData.Item آن شامل یک Intent است. برچسب شیء ClipDescription روی label تنظیم شده است. نوع MIME روی MIMETYPE_TEXT_INTENT تنظیم شده است.

برای ایجاد یک کلیپ از یک شیء Intent از newIntent() استفاده کنید.

تبدیل داده‌های کلیپ‌بورد به متن

حتی اگر برنامه شما فقط متن را مدیریت می‌کند، می‌توانید داده‌های غیرمتنی را با تبدیل آنها با استفاده از متد ClipData.Item.coerceToText() از کلیپ‌بورد کپی کنید.

این متد داده‌های موجود در ClipData.Item را به متن تبدیل کرده و یک CharSequence برمی‌گرداند. مقداری که ClipData.Item.coerceToText() برمی‌گرداند، بر اساس نوع داده در ClipData.Item است:

متن
اگر ClipData.Item از نوع متن باشد - یعنی اگر getText() تهی نباشد - coerceToText() متن را برمی‌گرداند.
یو آر آی
اگر ClipData.Item یک URI باشد - یعنی اگر getUri() تهی نباشد - coerceToText() سعی می‌کند از آن به عنوان یک URI محتوا استفاده کند.
  • اگر URI یک URI محتوایی باشد و ارائه‌دهنده بتواند یک جریان متنی را برگرداند، coerceToText() یک جریان متنی را برمی‌گرداند.
  • اگر URI یک URI محتوایی باشد اما ارائه‌دهنده، جریان متنی ارائه ندهد، coerceToText() نمایشی از URI را برمی‌گرداند. این نمایش همان نمایشی است که توسط Uri.toString() برگردانده می‌شود.
  • اگر URI یک URI محتوایی نباشد، coerceToText() نمایشی از URI را برمی‌گرداند. این نمایش همان نمایشی است که توسط Uri.toString() برگردانده می‌شود.
قصد
اگر ClipData.Item یک Intent باشد - یعنی اگر getIntent() تهی نباشد coerceToText() آن را به یک URI از نوع Intent تبدیل کرده و برمی‌گرداند. نمایش آن مشابه نمایش Intent.toUri(URI_INTENT_SCHEME) است.

چارچوب کلیپ‌بورد در شکل ۲ خلاصه شده است. برای کپی کردن داده‌ها، یک برنامه یک شیء ClipData در کلیپ‌بورد سراسری ClipboardManager قرار می‌دهد. ClipData شامل یک یا چند شیء ClipData.Item و یک شیء ClipDescription است. برای چسباندن داده‌ها، یک برنامه ClipData دریافت می‌کند، نوع MIME آن را از ClipDescription می‌گیرد و داده‌ها را از ClipData.Item یا از ارائه‌دهنده محتوای ارجاع داده شده توسط ClipData.Item دریافت می‌کند.

تصویری که نمودار بلوکی چارچوب کپی و چسباندن را نشان می‌دهد
شکل 2. چارچوب کلیپ‌بورد اندروید.

کپی کردن در کلیپ بورد

برای کپی کردن داده‌ها به کلیپ‌بورد، یک هندل به شیء سراسری ClipboardManager بدهید، یک شیء ClipData ایجاد کنید و یک ClipDescription و یک یا چند شیء ClipData.Item به آن اضافه کنید. سپس، شیء ClipData تکمیل‌شده را به شیء ClipboardManager اضافه کنید. این کار در روش زیر بیشتر توضیح داده شده است:

  1. اگر داده‌ها را با استفاده از یک URL محتوا کپی می‌کنید، یک ارائه‌دهنده محتوا تنظیم کنید.
  2. کلیپ‌بورد سیستم را دریافت کنید:

    کاتلین

    when(menuItem.itemId) {
        ...
        R.id.menu_copy -> { // if the user selects copy
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        }
    }

    جاوا

    ...
    // If the user selects copy.
    case R.id.menu_copy:
    
    // Gets a handle to the clipboard service.
    ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
  3. داده‌ها را در یک شیء ClipData جدید کپی کنید:

    • برای متن

      کاتلین

      // Creates a new text clip to put on the clipboard.
      val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")

      جاوا

      // Creates a new text clip to put on the clipboard.
      ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
    • برای یک URI

      این قطعه کد با رمزگذاری یک شناسه رکورد روی URI محتوا برای ارائه دهنده، یک URI می‌سازد. این تکنیک با جزئیات بیشتر در بخش رمزگذاری شناسه روی URI پوشش داده شده است.

      کاتلین

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      const val CONTACTS = "content://com.example.contacts"
      
      // Declares a path string for URIs, used to copy data.
      const val COPY_PATH = "/copy"
      
      // Declares the Uri to paste to the clipboard.
      val copyUri: Uri = Uri.parse("$CONTACTS$COPY_PATH/$lastName")
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)

      جاوا

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      private static final String CONTACTS = "content://com.example.contacts";
      
      // Declares a path string for URIs, used to copy data.
      private static final String COPY_PATH = "/copy";
      
      // Declares the Uri to paste to the clipboard.
      Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
    • برای یک نیت

      این قطعه کد یک Intent برای یک برنامه می‌سازد و سپس آن را در شیء clip قرار می‌دهد:

      کاتلین

      // Creates the Intent.
      val appIntent = Intent(this, com.example.demo.myapplication::class.java)
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      val clip: ClipData = ClipData.newIntent("Intent", appIntent)

      جاوا

      // Creates the Intent.
      Intent appIntent = new Intent(this, com.example.demo.myapplication.class);
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      ClipData clip = ClipData.newIntent("Intent", appIntent);
  4. شیء کلیپ جدید را روی کلیپ بورد قرار دهید:

    کاتلین

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip)

    جاوا

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip);

هنگام کپی کردن در کلیپ بورد، بازخورد ارائه دهید

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

از اندروید ۱۳ به بعد، سیستم هنگام اضافه شدن محتوا به کلیپ‌بورد، یک تأیید بصری استاندارد نمایش می‌دهد. این تأیید جدید موارد زیر را انجام می‌دهد:

  • تأیید می‌کند که محتوا با موفقیت کپی شده است.
  • پیش‌نمایشی از محتوای کپی‌شده ارائه می‌دهد.

انیمیشنی که اعلان کلیپ‌بورد اندروید ۱۳ را نشان می‌دهد
شکل ۳. رابط کاربری نشان داده شده هنگام ورود محتوا به کلیپ‌بورد در اندروید ۱۳ و بالاتر.

در اندروید ۱۲L (سطح API 32) و پایین‌تر، کاربران ممکن است مطمئن نباشند که آیا محتوا را با موفقیت کپی کرده‌اند یا اینکه چه چیزی را کپی کرده‌اند. این ویژگی اعلان‌های مختلف نشان داده شده توسط برنامه‌ها را پس از کپی کردن استاندارد می‌کند و به کاربران کنترل بیشتری بر کلیپ‌بورد می‌دهد.

جلوگیری از ارسال اعلان‌های تکراری

در اندروید ۱۲L (سطح API 32) و پایین‌تر، توصیه می‌کنیم پس از کپی کردن، با استفاده از یک ویجت مانند Toast یا Snackbar ، با نمایش بازخورد بصری درون برنامه‌ای، به کاربران در مورد کپی موفقیت‌آمیز هشدار دهید.

برای جلوگیری از نمایش تکراری اطلاعات، اکیداً توصیه می‌کنیم برای اندروید ۱۳ و بالاتر، پیام‌های تست یا اسنک‌بار نمایش داده شده پس از یک کپی درون‌برنامه‌ای را حذف کنید.

اسنک‌بار را بعد از یک کپی درون‌برنامه‌ای منتشر کنید.
شکل ۴. اگر در اندروید ۱۳ یک اسنک‌بار تأیید کپی نمایش دهید، کاربر پیام‌های تکراری می‌بیند.
بعد از یک کپی درون برنامه‌ای، یک پیام تست ارسال کنید.
شکل ۵. اگر در اندروید ۱۳ یک پیام تایید کپی نمایش دهید، کاربر پیام‌های تکراری می‌بیند.

در اینجا مثالی از نحوه پیاده‌سازی این مورد آورده شده است:

fun textCopyThenPost(textCopied:String) {
    val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
    // When setting the clipboard text.
    clipboardManager.setPrimaryClip(ClipData.newPlainText   ("", textCopied))
    // Only show a toast for Android 12 and lower.
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2)
        Toast.makeText(context, Copied, Toast.LENGTH_SHORT).show()
}

محتوای حساس را به کلیپ بورد اضافه کنید

اگر برنامه شما به کاربران اجازه می‌دهد محتوای حساس مانند رمزهای عبور یا اطلاعات کارت اعتباری را در کلیپ‌بورد کپی کنند، باید قبل از فراخوانی ClipboardManager.setPrimaryClip() یک پرچم به ClipDescription در ClipData اضافه کنید. افزودن این پرچم از نمایش محتوای حساس در تأیید بصری محتوای کپی شده در اندروید ۱۳ و بالاتر جلوگیری می‌کند.

پیش‌نمایش متن کپی‌شده بدون علامت‌گذاری محتوای حساس
شکل ۶. پیش‌نمایش متن کپی‌شده بدون پرچم محتوای حساس.
پیش‌نمایش متن کپی‌شده، محتوای حساس را علامت‌گذاری می‌کند.
شکل ۷. پیش‌نمایش متن کپی‌شده با پرچم محتوای حساس.

برای علامت‌گذاری محتوای حساس، یک مقدار بولی به ClipDescription اضافه کنید. همه برنامه‌ها باید این کار را انجام دهند، صرف نظر از سطح API مورد نظر.

// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
    }
}

// If your app is compiled with a lower SDK.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean("android.content.extra.IS_SENSITIVE", true)
    }
}

چسباندن از کلیپ‌بورد

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

متن ساده را جایگذاری کنید

برای چسباندن متن ساده، کلیپ‌بورد سراسری را دریافت کنید و تأیید کنید که می‌تواند متن ساده را برگرداند. سپس شیء کلیپ را دریافت کرده و متن آن را با استفاده از getText() به حافظه خود کپی کنید، همانطور که در روش زیر توضیح داده شده است:

  1. شیء سراسری ClipboardManager را با استفاده از getSystemService(CLIPBOARD_SERVICE) دریافت کنید. همچنین، یک متغیر سراسری برای نگهداری متن پیست شده تعریف کنید:

    کاتلین

    var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    var pasteData: String = ""

    جاوا

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
  2. مشخص کنید که آیا نیاز به فعال یا غیرفعال کردن گزینه "چسباندن" در فعالیت فعلی دارید. تأیید کنید که کلیپ‌بورد حاوی یک کلیپ است و می‌توانید نوع داده‌های نمایش داده شده توسط کلیپ را مدیریت کنید:

    کاتلین

    // Gets the ID of the "paste" menu item.
    val pasteItem: MenuItem = menu.findItem(R.id.menu_paste)
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    pasteItem.isEnabled = when {
        !clipboard.hasPrimaryClip() -> {
            false
        }
        !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> {
            // Disables the paste menu item, since the clipboard has data but it
            // isn't plain text.
            false
        }
        else -> {
            // Enables the paste menu item, since the clipboard contains plain text.
            true
        }
    }

    جاوا

    // Gets the ID of the "paste" menu item.
    MenuItem pasteItem = menu.findItem(R.id.menu_paste);
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    if (!(clipboard.hasPrimaryClip())) {
    
        pasteItem.setEnabled(false);
    
    } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) {
    
        // Disables the paste menu item, since the clipboard has data but
        // it isn't plain text.
        pasteItem.setEnabled(false);
    } else {
    
        // Enables the paste menu item, since the clipboard contains plain text.
        pasteItem.setEnabled(true);
    }
  3. داده‌ها را از کلیپ‌بورد کپی کنید. این نقطه در کد فقط در صورتی قابل دسترسی است که گزینه منوی "چسباندن" فعال باشد، بنابراین می‌توانید فرض کنید که کلیپ‌بورد حاوی متن ساده است. شما هنوز نمی‌دانید که آیا حاوی یک رشته متنی است یا یک URI که به متن ساده اشاره می‌کند. قطعه کد زیر این را آزمایش می‌کند، اما فقط کد مربوط به مدیریت متن ساده را نشان می‌دهد:

    کاتلین

    when (menuItem.itemId) {
        ...
        R.id.menu_paste -> {    // Responds to the user selecting "paste".
            // Examines the item on the clipboard. If getText() doesn't return null,
            // the clip item contains the text. Assumes that this application can only
            // handle one item at a time.
            val item = clipboard.primaryClip.getItemAt(0)
    
            // Gets the clipboard as text.
            pasteData = item.text
    
            return if (pasteData != null) {
                // If the string contains data, then the paste operation is done.
                true
            } else {
                // The clipboard doesn't contain text. If it contains a URI,
                // attempts to get data from it.
                val pasteUri: Uri? = item.uri
    
                if (pasteUri != null) {
                    // If the URI contains something, try to get text from it.
    
                    // Calls a routine to resolve the URI and get data from it.
                    // This routine isn't presented here.
                    pasteData = resolveUri(pasteUri)
                    true
                } else {
    
                    // Something is wrong. The MIME type was plain text, but the
                    // clipboard doesn't contain text or a Uri. Report an error.
                    Log.e(TAG,"Clipboard contains an invalid data type")
                    false
                }
            }
        }
    }

    جاوا

    // Responds to the user selecting "paste".
    case R.id.menu_paste:
    
    // Examines the item on the clipboard. If getText() does not return null,
    // the clip item contains the text. Assumes that this application can only
    // handle one item at a time.
     ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
    
    // Gets the clipboard as text.
    pasteData = item.getText();
    
    // If the string contains data, then the paste operation is done.
    if (pasteData != null) {
        return true;
    
    // The clipboard doesn't contain text. If it contains a URI, attempts to get
    // data from it.
    } else {
        Uri pasteUri = item.getUri();
    
        // If the URI contains something, try to get text from it.
        if (pasteUri != null) {
    
            // Calls a routine to resolve the URI and get data from it.
            // This routine isn't presented here.
            pasteData = resolveUri(Uri);
            return true;
        } else {
    
            // Something is wrong. The MIME type is plain text, but the
            // clipboard doesn't contain text or a Uri. Report an error.
            Log.e(TAG, "Clipboard contains an invalid data type");
            return false;
        }
    }

جایگذاری داده‌ها از یک URI محتوا

اگر شیء ClipData.Item حاوی یک URL محتوا باشد و شما تشخیص دهید که می‌توانید یکی از انواع MIME آن را مدیریت کنید، یک ContentResolver ایجاد کنید و متد ارائه‌دهنده محتوای مناسب را برای بازیابی داده‌ها فراخوانی کنید.

روش زیر نحوه دریافت داده‌ها از یک ارائه‌دهنده محتوا را بر اساس یک URI محتوا در کلیپ‌بورد شرح می‌دهد. این روش بررسی می‌کند که آیا نوع MIME مورد استفاده برنامه از طرف ارائه‌دهنده در دسترس است یا خیر.

  1. یک متغیر سراسری تعریف کنید که شامل نوع MIME باشد:

    کاتلین

    // Declares a MIME type constant to match against the MIME types offered
    // by the provider.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"

    جاوا

    // Declares a MIME type constant to match against the MIME types offered by
    // the provider.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
  2. کلیپ‌بورد سراسری را دریافت کنید. همچنین یک content resolver دریافت کنید تا بتوانید به ارائه‌دهنده محتوا دسترسی داشته باشید:

    کاتلین

    // Gets a handle to the Clipboard Manager.
    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
    // Gets a content resolver instance.
    val cr = contentResolver

    جاوا

    // Gets a handle to the Clipboard Manager.
    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    
    // Gets a content resolver instance.
    ContentResolver cr = getContentResolver();
  3. کلیپ اصلی را از کلیپ‌بورد دریافت کنید و محتویات آن را به عنوان یک URI دریافت کنید:

    کاتلین

    // Gets the clipboard data from the clipboard.
    val clip: ClipData? = clipboard.primaryClip
    
    clip?.run {
    
        // Gets the first item from the clipboard data.
        val item: ClipData.Item = getItemAt(0)
    
        // Tries to get the item's contents as a URI.
        val pasteUri: Uri? = item.uri

    جاوا

    // Gets the clipboard data from the clipboard.
    ClipData clip = clipboard.getPrimaryClip();
    
    if (clip != null) {
    
        // Gets the first item from the clipboard data.
        ClipData.Item item = clip.getItemAt(0);
    
        // Tries to get the item's contents as a URI.
        Uri pasteUri = item.getUri();
  4. با فراخوانی getType(Uri) بررسی کنید که آیا URI یک URI محتوایی است یا خیر. اگر Uri به یک ارائه دهنده محتوای معتبر اشاره نکند، این متد null را برمی‌گرداند.

    کاتلین

        // If the clipboard contains a URI reference...
        pasteUri?.let {
    
            // ...is this a content URI?
            val uriMimeType: String? = cr.getType(it)

    جاوا

        // If the clipboard contains a URI reference...
        if (pasteUri != null) {
    
            // ...is this a content URI?
            String uriMimeType = cr.getType(pasteUri);
  5. بررسی کنید که آیا ارائه دهنده محتوا از نوع MIME که برنامه آن را درک می‌کند پشتیبانی می‌کند یا خیر. اگر پشتیبانی می‌کند، برای دریافت داده‌ها ContentResolver.query() را فراخوانی کنید. مقدار بازگشتی یک Cursor است.

    کاتلین

            // If the return value isn't null, the Uri is a content Uri.
            uriMimeType?.takeIf {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                it == MIME_TYPE_CONTACT
            }?.apply {
    
                // Get the data from the content provider.
                cr.query(pasteUri, null, null, null, null)?.use { pasteCursor ->
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                    }
    
                    // Kotlin `use` automatically closes the Cursor.
                }
            }
        }
    }

    جاوا

            // If the return value isn't null, the Uri is a content Uri.
            if (uriMimeType != null) {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                if (uriMimeType.equals(MIME_TYPE_CONTACT)) {
    
                    // Get the data from the content provider.
                    Cursor pasteCursor = cr.query(uri, null, null, null, null);
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor != null) {
                        if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                        }
                    }
    
                    // Close the Cursor.
                    pasteCursor.close();
                 }
             }
         }
    }

چسباندن یک Intent

برای چسباندن یک intent، ابتدا کلیپ‌بورد سراسری را دریافت کنید. شیء ClipData.Item را بررسی کنید تا ببینید آیا حاوی Intent است یا خیر. سپس getIntent() برای کپی کردن intent به حافظه خود فراخوانی کنید. قطعه کد زیر این موضوع را نشان می‌دهد:

کاتلین

// Gets a handle to the Clipboard Manager.
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager

// Checks whether the clip item contains an Intent by testing whether
// getIntent() returns null.
val pasteIntent: Intent? = clipboard.primaryClip?.getItemAt(0)?.intent

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

جاوا

// Gets a handle to the Clipboard Manager.
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);

// Checks whether the clip item contains an Intent, by testing whether
// getIntent() returns null.
Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent();

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

اعلان سیستم هنگام دسترسی برنامه شما به داده‌های کلیپ‌بورد نشان داده می‌شود

در اندروید ۱۲ (سطح API 31) و بالاتر، سیستم معمولاً هنگام فراخوانی تابع getPrimaryClip() توسط برنامه، یک پیام toast نمایش می‌دهد. متن داخل پیام شامل فرمت زیر است:

APP pasted from your clipboard

وقتی برنامه شما یکی از موارد زیر را انجام می‌دهد، سیستم پیام تست را نشان نمی‌دهد:

  • از طریق برنامه خودتان ClipData دسترسی پیدا می‌کند.
  • به طور مکرر از یک برنامه خاص به ClipData دسترسی پیدا می‌کند. Toast فقط زمانی ظاهر می‌شود که برنامه شما برای اولین بار به داده‌های آن برنامه دسترسی پیدا کند.
  • فراداده‌های مربوط به شیء کلیپ را بازیابی می‌کند، مثلاً با فراخوانی getPrimaryClipDescription() به جای getPrimaryClip() .

استفاده از ارائه دهندگان محتوا برای کپی کردن داده‌های پیچیده

ارائه دهندگان محتوا از کپی کردن داده‌های پیچیده مانند رکوردهای پایگاه داده یا جریان‌های فایل پشتیبانی می‌کنند. برای کپی کردن داده‌ها، یک URI محتوا را در کلیپ‌بورد قرار دهید. برنامه‌های چسباندن (paste) سپس این URI را از کلیپ‌بورد دریافت کرده و از آن برای بازیابی داده‌های پایگاه داده یا توصیف‌گرهای جریان فایل استفاده می‌کنند.

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

بخش‌های بعدی نحوه تنظیم URIها، ارائه داده‌های پیچیده و ارائه جریان‌های فایل را شرح می‌دهند. این توضیحات فرض می‌کنند که شما با اصول کلی طراحی ارائه‌دهنده محتوا آشنا هستید.

کدگذاری یک شناسه روی URI

یک تکنیک مفید برای کپی کردن داده‌ها به کلیپ‌بورد با استفاده از یک URI، کدگذاری یک شناسه برای داده‌های موجود در خود URI است. سپس ارائه‌دهنده محتوای شما می‌تواند شناسه را از URI دریافت کرده و از آن برای بازیابی داده‌ها استفاده کند. برنامه چسباندن نیازی به دانستن وجود شناسه ندارد. فقط باید "مرجع" شما - URI به علاوه شناسه - را از کلیپ‌بورد دریافت کند، ارائه‌دهنده محتوای شما را به آن بدهد و داده‌ها را پس بگیرد.

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

"content://com.example.contacts"

اگر می‌خواهید نامی را روی این URI رمزگذاری کنید، از قطعه کد زیر استفاده کنید:

کاتلین

val uriString = "content://com.example.contacts/Smith"

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
val copyUri = Uri.parse(uriString)

جاوا

String uriString = "content://com.example.contacts" + "/" + "Smith";

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
Uri copyUri = Uri.parse(uriString);

اگر از قبل از یک ارائه دهنده محتوا استفاده می‌کنید، ممکن است بخواهید یک مسیر URI جدید اضافه کنید که نشان دهد URI برای کپی کردن است. برای مثال، فرض کنید از قبل مسیرهای URI زیر را دارید:

"content://com.example.contacts/people"
"content://com.example.contacts/people/detail"
"content://com.example.contacts/people/images"

می‌توانید مسیر دیگری برای کپی کردن URIها اضافه کنید:

"content://com.example.contacts/copying"

سپس می‌توانید با تطبیق الگو، یک URI «کپی» را تشخیص داده و آن را با کدی که مخصوص کپی و چسباندن است، مدیریت کنید.

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

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

کپی کردن ساختارهای داده

یک ارائه‌دهنده محتوا برای کپی و چسباندن داده‌های پیچیده به عنوان یک زیرکلاس از کامپوننت ContentProvider تنظیم کنید. URI که در کلیپ‌بورد قرار می‌دهید را طوری کدگذاری کنید که دقیقاً به رکوردی که می‌خواهید ارائه دهید اشاره کند. علاوه بر این، وضعیت موجود برنامه خود را در نظر بگیرید:

  • اگر از قبل یک ارائه‌دهنده محتوا دارید، می‌توانید به قابلیت‌های آن اضافه کنید. ممکن است فقط لازم باشد متد query() آن را برای مدیریت URIهایی که از برنامه‌هایی که می‌خواهند داده‌ها را جای‌گذاری کنند، دریافت می‌کنند، تغییر دهید. احتمالاً می‌خواهید این متد را برای مدیریت الگوی "کپی" URI تغییر دهید.
  • اگر برنامه شما یک پایگاه داده داخلی دارد، ممکن است بخواهید این پایگاه داده را به یک ارائه دهنده محتوا منتقل کنید تا کپی کردن از آن تسهیل شود.
  • اگر از پایگاه داده استفاده نمی‌کنید، می‌توانید یک ارائه‌دهنده محتوای ساده پیاده‌سازی کنید که تنها هدف آن ارائه داده‌ها به برنامه‌هایی است که از کلیپ‌بورد پیست می‌کنند.

در ارائه دهنده محتوا، حداقل متدهای زیر را لغو کنید:

query()
برنامه‌های چسباندن (paste) فرض می‌کنند که می‌توانند داده‌های شما را با استفاده از این روش و با استفاده از URI که شما در کلیپ‌بورد قرار می‌دهید، دریافت کنند. برای پشتیبانی از کپی کردن، از این روش برای شناسایی URIهایی که حاوی یک مسیر "کپی" خاص هستند، استفاده کنید. سپس برنامه شما می‌تواند یک URI "کپی" ایجاد کند تا در کلیپ‌بورد قرار دهد، که شامل مسیر کپی و یک اشاره‌گر به رکورد دقیقی است که می‌خواهید کپی کنید.
getType()
این متد باید انواع MIME را برای داده‌هایی که قصد کپی کردن آنها را دارید، برگرداند. متد newUri() getType() را فراخوانی می‌کند تا انواع MIME را در شیء جدید ClipData قرار دهد.

انواع MIME برای داده‌های پیچیده در ارائه دهندگان محتوا شرح داده شده‌اند.

شما نیازی به داشتن هیچ یک از متدهای دیگر ارائه دهنده محتوا، مانند insert() یا update() ندارید. یک برنامه چسباندن (paste) فقط باید انواع MIME پشتیبانی شده شما را دریافت کرده و داده‌ها را از ارائه دهنده شما کپی کند. اگر از قبل این متدها را دارید، آنها در عملیات کپی اختلالی ایجاد نمی‌کنند.

قطعه کدهای زیر نحوه تنظیم برنامه شما برای کپی کردن داده‌های پیچیده را نشان می‌دهند:

  1. در ثابت‌های سراسری برنامه‌تان، یک رشته URI پایه و مسیری که رشته‌های URI مورد استفاده برای کپی کردن داده‌ها را مشخص می‌کند، تعریف کنید. همچنین یک نوع MIME برای داده‌های کپی شده تعریف کنید.

    کاتلین

    // Declares the base URI string.
    private const val CONTACTS = "content://com.example.contacts"
    
    // Declares a path string for URIs that you use to copy data.
    private const val COPY_PATH = "/copy"
    
    // Declares a MIME type for the copied data.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"

    جاوا

    // Declares the base URI string.
    private static final String CONTACTS = "content://com.example.contacts";
    
    // Declares a path string for URIs that you use to copy data.
    private static final String COPY_PATH = "/copy";
    
    // Declares a MIME type for the copied data.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
  2. در فعالیتی که کاربران داده‌ها را از آن کپی می‌کنند، کدی را برای کپی کردن داده‌ها در کلیپ‌بورد تنظیم کنید. در پاسخ به درخواست کپی، آدرس اینترنتی (URI) را در کلیپ‌بورد قرار دهید.

    کاتلین

    class MyCopyActivity : Activity() {
        ...
    when(item.itemId) {
        R.id.menu_copy -> { // The user has selected a name and is requesting a copy.
            // Appends the last name to the base URI.
            // The name is stored in "lastName".
            uriString = "$CONTACTS$COPY_PATH/$lastName"
    
            // Parses the string into a URI.
            val copyUri: Uri? = Uri.parse(uriString)
    
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
            val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
    
            // Sets the clipboard's primary clip.
            clipboard.setPrimaryClip(clip)
        }
    }

    جاوا

    public class MyCopyActivity extends Activity {
        ...
    // The user has selected a name and is requesting a copy.
    case R.id.menu_copy:
    
        // Appends the last name to the base URI.
        // The name is stored in "lastName".
        uriString = CONTACTS + COPY_PATH + "/" + lastName;
    
        // Parses the string into a URI.
        Uri copyUri = Uri.parse(uriString);
    
        // Gets a handle to the clipboard service.
        ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
        ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
    
        // Sets the clipboard's primary clip.
        clipboard.setPrimaryClip(clip);
  3. در محدوده سراسری ارائه‌دهنده محتوای خود، یک تطبیق‌دهنده URI ایجاد کنید و یک الگوی URI اضافه کنید که با URIهایی که در کلیپ‌بورد قرار می‌دهید، مطابقت داشته باشد.

    کاتلین

    // A Uri Match object that simplifies matching content URIs to patterns.
    private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
    
        // Adds a matcher for the content URI. It matches.
        // "content://com.example.contacts/copy/*"
        addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT)
    }
    
    // An integer to use in switching based on the incoming URI pattern.
    private const val GET_SINGLE_CONTACT = 0
    ...
    class MyCopyProvider : ContentProvider() {
        ...
    }

    جاوا

    public class MyCopyProvider extends ContentProvider {
        ...
    // A Uri Match object that simplifies matching content URIs to patterns.
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
    // An integer to use in switching based on the incoming URI pattern.
    private static final int GET_SINGLE_CONTACT = 0;
    ...
    // Adds a matcher for the content URI. It matches
    // "content://com.example.contacts/copy/*"
    sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
  4. متد query() را تنظیم کنید. این متد می‌تواند الگوهای URI مختلفی را مدیریت کند، بسته به نحوه کدنویسی شما، اما فقط الگوی مربوط به عملیات کپی کردن کلیپ‌بورد نمایش داده می‌شود.

    کاتلین

    // Sets up your provider's query() method.
    override fun query(
            uri: Uri,
            projection: Array<out String>?,
            selection: String?,
            selectionArgs: Array<out String>?,
            sortOrder: String?
    ): Cursor? {
        ...
        // When based on the incoming content URI:
        when(sUriMatcher.match(uri)) {
    
            GET_SINGLE_CONTACT -> {
    
                // Queries and returns the contact for the requested name. Decodes
                // the incoming URI, queries the data model based on the last name,
                // and returns the result as a Cursor.
            }
        }
        ...
    }

    جاوا

    // Sets up your provider's query() method.
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
        String sortOrder) {
        ...
        // Switch based on the incoming content URI.
        switch (sUriMatcher.match(uri)) {
    
        case GET_SINGLE_CONTACT:
    
            // Queries and returns the contact for the requested name. Decodes the
            // incoming URI, queries the data model based on the last name, and
            // returns the result as a Cursor.
        ...
    }
  5. متد getType() را طوری تنظیم کنید که یک نوع MIME مناسب برای داده‌های کپی شده برگرداند:

    کاتلین

    // Sets up your provider's getType() method.
    override fun getType(uri: Uri): String? {
        ...
        return when(sUriMatcher.match(uri)) {
            GET_SINGLE_CONTACT -> MIME_TYPE_CONTACT
            ...
        }
    }

    جاوا

    // Sets up your provider's getType() method.
    public String getType(Uri uri) {
        ...
        switch (sUriMatcher.match(uri)) {
        case GET_SINGLE_CONTACT:
            return (MIME_TYPE_CONTACT);
        ...
        }
    }

بخش «چسباندن داده‌ها از یک URI محتوا» نحوه دریافت یک URI محتوا از کلیپ‌بورد و استفاده از آن برای دریافت و چسباندن داده‌ها را شرح می‌دهد.

کپی کردن جریان‌های داده

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

  • فایل‌های ذخیره شده روی دستگاه واقعی
  • جریان‌ها از سوکت‌ها
  • حجم زیادی از داده‌ها در سیستم پایگاه داده اصلی ارائه دهنده ذخیره می‌شوند

یک ارائه دهنده محتوا برای جریان‌های داده، دسترسی به داده‌های خود را با یک شیء توصیف‌گر فایل، مانند AssetFileDescriptor ، به جای یک شیء Cursor فراهم می‌کند. برنامه چسباندن، جریان داده را با استفاده از این توصیف‌گر فایل می‌خواند.

برای تنظیم برنامه خود برای کپی کردن جریان داده با یک ارائه دهنده، این مراحل را دنبال کنید:

  1. یک URI محتوا برای جریان داده‌ای که در کلیپ‌بورد قرار می‌دهید تنظیم کنید. گزینه‌های انجام این کار شامل موارد زیر است:
    • همانطور که در بخش « رمزگذاری شناسه روی URI» توضیح داده شده است، یک شناسه برای جریان داده روی URI رمزگذاری کنید و سپس جدولی را در ارائه‌دهنده خود نگهداری کنید که شامل شناسه‌ها و نام جریان مربوطه باشد.
    • نام جریان را مستقیماً روی URI کدگذاری کنید.
    • از یک URI منحصر به فرد استفاده کنید که همیشه جریان فعلی را از ارائه دهنده برمی‌گرداند. اگر از این گزینه استفاده می‌کنید، به یاد داشته باشید که ارائه دهنده خود را به‌روزرسانی کنید تا هر زمان که جریان را با استفاده از URI در کلیپ بورد کپی می‌کنید، به جریان متفاوتی اشاره کند.
  2. برای هر نوع جریان داده‌ای که قصد ارائه آن را دارید، یک نوع MIME ارائه دهید. برنامه‌های چسباندن (paste) برای تعیین اینکه آیا می‌توانند داده‌ها را در کلیپ‌بورد جای‌گذاری کنند یا خیر، به این اطلاعات نیاز دارند.
  3. یکی از متدهای ContentProvider را پیاده‌سازی کنید که یک توصیف‌گر فایل برای یک جریان برمی‌گرداند. اگر شناسه‌ها را روی URI محتوا کدگذاری می‌کنید، از این متد برای تعیین اینکه کدام جریان باید باز شود استفاده کنید.
  4. برای کپی کردن جریان داده به کلیپ بورد، URI محتوا را بسازید و آن را در کلیپ بورد قرار دهید.

برای چسباندن یک جریان داده، یک برنامه کلیپ را از کلیپ‌بورد دریافت می‌کند، URI را دریافت می‌کند و از آن در فراخوانی یک متد توصیف‌گر فایل ContentResolver که جریان را باز می‌کند، استفاده می‌کند. متد ContentResolver متد ContentProvider مربوطه را فراخوانی می‌کند و URI محتوا را به آن ارسال می‌کند. ارائه‌دهنده شما توصیف‌گر فایل را به متد ContentResolver برمی‌گرداند. سپس برنامه چسباندن، مسئولیت خواندن داده‌ها از جریان را بر عهده دارد.

لیست زیر مهمترین متدهای توصیفگر فایل برای یک ارائه دهنده محتوا را نشان می‌دهد. هر یک از این متدها یک متد ContentResolver متناظر با رشته "Descriptor" دارند که به نام متد اضافه شده است. برای مثال، معادل ContentResolver برای openAssetFile() openAssetFileDescriptor() است.

openTypedAssetFile()

این متد یک توصیف‌گر فایل دارایی را برمی‌گرداند، اما تنها در صورتی که نوع MIME ارائه شده توسط ارائه‌دهنده پشتیبانی شود. فراخواننده - برنامه‌ای که عمل چسباندن را انجام می‌دهد - یک الگوی نوع MIME ارائه می‌دهد. ارائه‌دهنده محتوای برنامه‌ای که یک URI را در کلیپ‌بورد کپی می‌کند، در صورتی که بتواند آن نوع MIME را ارائه دهد، یک هندل فایل AssetFileDescriptor را برمی‌گرداند و در غیر این صورت یک استثنا ایجاد می‌کند.

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

openAssetFile()
این متد شکل عمومی‌تری از openTypedAssetFile() است. این متد انواع MIME مجاز را فیلتر نمی‌کند، اما می‌تواند زیرمجموعه‌های فایل‌ها را بخواند.
openFile()
این یک شکل کلی‌تر از openAssetFile() است. این تابع نمی‌تواند زیربخش‌های فایل‌ها را بخواند.

شما می‌توانید به صورت اختیاری از متد openPipeHelper() به همراه متد توصیف‌گر فایل خود استفاده کنید. این به برنامه‌ی چسباندن (paste) اجازه می‌دهد تا داده‌های جریان را در یک نخ پس‌زمینه با استفاده از یک pipe بخواند. برای استفاده از این متد، رابط ContentProvider.PipeDataWriter را پیاده‌سازی کنید.

طراحی قابلیت کپی و پیست موثر

برای طراحی قابلیت کپی و پیست موثر برای برنامه خود، این نکات را به خاطر داشته باشید:

  • در هر زمان، فقط یک کلیپ در کلیپ‌بورد وجود دارد. یک عملیات کپی جدید توسط هر برنامه‌ای در سیستم، کلیپ قبلی را رونویسی می‌کند. از آنجایی که کاربر ممکن است از برنامه شما خارج شود و قبل از بازگشت، کپی کند، نمی‌توانید فرض کنید که کلیپ‌بورد حاوی کلیپی است که کاربر قبلاً در برنامه شما کپی کرده است.
  • هدف از چندین شیء ClipData.Item در هر کلیپ، پشتیبانی از کپی و چسباندن چندین انتخاب به جای اشکال مختلف ارجاع به یک انتخاب واحد است. شما معمولاً می‌خواهید همه اشیاء ClipData.Item در یک کلیپ فرم یکسانی داشته باشند. یعنی، همه آنها باید متن ساده، URI محتوا یا Intent باشند و ترکیبی از آنها نباشد.
  • وقتی داده‌ها را ارائه می‌دهید، می‌توانید نمایش‌های MIME مختلفی ارائه دهید. انواع MIME مورد پشتیبانی خود را به ClipDescription اضافه کنید و سپس انواع MIME را در ارائه‌دهنده محتوای خود پیاده‌سازی کنید.
  • وقتی داده‌ها را از کلیپ‌بورد دریافت می‌کنید، برنامه شما مسئول بررسی انواع MIME موجود و سپس تصمیم‌گیری در مورد استفاده از یکی از آنها، در صورت وجود، است. حتی اگر کلیپی در کلیپ‌بورد وجود داشته باشد و کاربر درخواست چسباندن (paste) کند، برنامه شما ملزم به انجام چسباندن نیست. در صورت سازگاری نوع MIME، چسباندن را انجام دهید. می‌توانید داده‌های موجود در کلیپ‌بورد را با استفاده از coerceToText() به متن تبدیل کنید. اگر برنامه شما از بیش از یکی از انواع MIME موجود پشتیبانی می‌کند، می‌توانید به کاربر اجازه دهید که از کدام یک استفاده کند.