কপি ও পেস্ট করুন

Compose-এর মাধ্যমে চেষ্টা করুন
Jetpack Compose হল Android-এর জন্য সাজেস্ট করা UI টুলকিট। Compose-এ কীভাবে কপি ও পেস্ট করতে হয় তা জানুন।

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

ফ্রেমওয়ার্কের কিছু অংশ কন্টেন্ট প্রোভাইডার ব্যবহার করে বলে, এই ডকুমেন্ট ধরে নেয় যে আপনি Android Content Provider API সম্পর্কে কিছুটা জানেন, যা কন্টেন্ট প্রোভাইডার-এ বর্ণনা করা হয়েছে।

ক্লিপবোর্ডে কন্টেন্ট কপি করার সময় ব্যবহারকারীরা ফিডব্যাক আশা করেন, তাই কপি ও পেস্ট করার ফ্রেমওয়ার্ক ছাড়াও, Android 13 (API লেভেল 33) ও তার পরবর্তী ভার্সনে কপি করার সময় Android ব্যবহারকারীদের ডিফল্ট UI দেখায়। এই ফিচারের কারণে, ডুপ্লিকেট বিজ্ঞপ্তি পাওয়ার ঝুঁকি থাকে। আপনি এই ধরনের ব্যতিক্রমী কেস সম্পর্কে ডুপ্লিকেট বিজ্ঞপ্তি এড়িয়ে চলুন বিভাগ থেকে আরও জানতে পারবেন।

Android 13 ক্লিপবোর্ড বিজ্ঞপ্তি দেখাচ্ছে এমন অ্যানিমেশন
ছবি ১. Android 13 ও তার পরবর্তী যেকোনও ভার্সনে কন্টেন্ট ক্লিপবোর্ডে প্রবেশ করলে UI দেখানো হয়।

Android 12L (API লেভেল ৩২) এবং তার আগের ভার্সনে কপি করার সময় ব্যবহারকারীদের ম্যানুয়ালি মতামত প্রদান করুন। এই ডকুমেন্টে এর জন্য সাজেশন দেখুন।

ক্লিপবোর্ড ফ্রেমওয়ার্ক

ক্লিপবোর্ড ফ্রেমওয়ার্ক ব্যবহার করার সময়, ক্লিপ অবজেক্টে ডেটা রাখুন এবং তারপর সিস্টেম-ওয়াইড ক্লিপবোর্ডে ক্লিপ অবজেক্ট রাখুন। ক্লিপ অবজেক্ট তিনটি ফর্মের মধ্যে যেকোনও একটি নিতে পারে:

টেক্সট
একটি টেক্সট স্ট্রিং। ক্লিপ অবজেক্টে সরাসরি স্ট্রিংটি রাখুন, যা আপনি তারপরে ক্লিপবোর্ডে রাখবেন। স্ট্রিং পেস্ট করতে, ক্লিপবোর্ড থেকে ক্লিপ অবজেক্ট পান এবং আপনার অ্যাপ্লিকেশনের স্টোরেজে স্ট্রিং কপি করুন।
URI
একটি Uri অবজেক্ট যা যেকোনও URI-এর ফর্মকে উপস্থাপন করে। এটি মূলত কন্টেন্ট প্রোভাইডার থেকে জটিল ডেটা কপি করার জন্য ব্যবহার করা হয়। ডেটা কপি করতে, Uri অবজেক্টকে ক্লিপ অবজেক্টে রাখুন এবং ক্লিপ অবজেক্টকে ক্লিপবোর্ডে রাখুন। ডেটা পেস্ট করতে, ক্লিপ অবজেক্ট পান, Uri অবজেক্ট পান, এটি কোনও ডেটা সোর্সে সমাধান করুন, যেমন কোনও কন্টেন্ট প্রোভাইডার এবং সোর্স থেকে ডেটা কপি করে আপনার অ্যাপ্লিকেশনের স্টোরেজে রাখুন।
ইনটেন্ট
An Intent. এটি অ্যাপ্লিকেশন শর্টকাট কপি করার সুবিধা দেয়। ডেটা কপি করতে, একটি Intent তৈরি করুন, এটি একটি ক্লিপ অবজেক্টে রাখুন এবং ক্লিপবোর্ডে ক্লিপ অবজেক্টটি রাখুন। ডেটা পেস্ট করতে, ক্লিপ অবজেক্ট পান এবং তারপরে আপনার অ্যাপ্লিকেশনের মেমরি এরিয়াতে Intent অবজেক্ট কপি করুন।

ক্লিপবোর্ডে একবারে শুধুমাত্র একটি ক্লিপ অবজেক্ট থাকে। কোনও অ্যাপ্লিকেশন ক্লিপবোর্ডে ক্লিপ অবজেক্ট রাখলে, আগের ক্লিপ অবজেক্টটি অদৃশ্য হয়ে যায়।

আপনি যদি ব্যবহারকারীদের আপনার অ্যাপ্লিকেশনে ডেটা পেস্ট করতে দিতে চান, তাহলে আপনাকে সব ধরনের ডেটা ম্যানেজ করতে হবে না। ব্যবহারকারীদের পেস্ট করার বিকল্প দেওয়ার আগে আপনি ক্লিপবোর্ডের ডেটা পরীক্ষা করতে পারবেন। একটি নির্দিষ্ট ডেটা ফর্ম থাকার পাশাপাশি, ক্লিপ অবজেক্টে মেটাডেটাও থাকে যা আপনাকে বলে যে কোন MIME প্রকার উপলভ্য। এই মেটাডেটা আপনাকে সিদ্ধান্ত নিতে সাহায্য করে যে আপনার অ্যাপ্লিকেশন ক্লিপবোর্ড ডেটার সাথে উপযোগী কিছু করতে পারে কিনা। যেমন, আপনার কাছে এমন কোনও অ্যাপ্লিকেশন থাকলে যা মূলত টেক্সট হ্যান্ডেল করে, আপনি URI বা ইনটেন্ট আছে এমন ক্লিপ অবজেক্ট উপেক্ষা করতে চাইতে পারেন।

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

ক্লিপবোর্ড ক্লাস

এই বিভাগে ক্লিপবোর্ড ফ্রেমওয়ার্কের মাধ্যমে ব্যবহার করা ক্লাস সম্পর্কে বর্ণনা করা হয়েছে।

ClipboardManager

Android সিস্টেম ক্লিপবোর্ড গ্লোবাল ClipboardManager ক্লাস দ্বারা উপস্থাপিত হয়। এই ক্লাস সরাসরি ইনস্ট্যান্টিয়েট করবেন না। পরিবর্তে, getSystemService(CLIPBOARD_SERVICE) ইনভোক করে এটির রেফারেন্স পান।

ClipData, ClipData.Item, এবং ClipDescription

ক্লিপবোর্ডে ডেটা যোগ করতে, একটি ClipData অবজেক্ট তৈরি করুন যাতে ডেটার বিবরণ এবং ডেটা থাকে। ক্লিপবোর্ডে একবারে একটি ClipData রাখা যায়। ClipData-এ একটি ClipDescription অবজেক্ট এবং এক বা একাধিক ClipData.Item অবজেক্ট থাকে।

ClipDescription অবজেক্টে ক্লিপের মেটাডেটা থাকে। বিশেষ করে, এতে ক্লিপের ডেটার জন্য উপলভ্য MIME-এর প্রকারের অ্যারে থাকে। এছাড়াও, Android 12 (API লেভেল 31) এবং তার পরবর্তী ভার্সনে, অবজেক্টে স্টাইলাইজ করা টেক্সট আছে কিনা এবং অবজেক্টে টেক্সটের ধরন সম্পর্কে তথ্য মেটাডেটায় অন্তর্ভুক্ত থাকে। আপনি ক্লিপবোর্ডে কোনও ক্লিপ রাখলে, এই তথ্য পেস্ট করার অ্যাপ্লিকেশনে উপলভ্য হয়, যা ক্লিপ ডেটা ম্যানেজ করতে পারবে কিনা তা পরীক্ষা করতে পারে।

ClipData.Item অবজেক্টে টেক্সট, URI বা ইনটেন্ট ডেটা থাকে:

টেক্সট
একটি CharSequence.
URI
একটি Uri. এতে সাধারণত কন্টেন্ট প্রোভাইডার URI থাকে, যদিও যেকোনও URI অনুমোদিত। যে অ্যাপ্লিকেশন ডেটা প্রদান করে সেটি ক্লিপবোর্ডে URI রাখে। যেসব অ্যাপ্লিকেশন ডেটা পেস্ট করতে চায়, সেগুলি ক্লিপবোর্ড থেকে URI পায় এবং কন্টেন্ট প্রোভাইডার বা অন্যান্য ডেটা সোর্স অ্যাক্সেস করতে এবং ডেটা ফিরিয়ে আনতে এটি ব্যবহার করে।
ইনটেন্ট
একটি Intent। এই ডেটার ধরন আপনাকে ক্লিপবোর্ডে অ্যাপ্লিকেশন শর্টকাট কপি করতে দেয়। ব্যবহারকারীরা পরে ব্যবহারের জন্য তাদের অ্যাপ্লিকেশনে শর্টকাট পেস্ট করতে পারবেন।

আপনি একটি ক্লিপে একাধিক ClipData.Item অবজেক্ট যোগ করতে পারবেন। এর ফলে ব্যবহারকারীরা একাধিক আইটেম বেছে নিয়ে সেগুলি কপি করে একটি ক্লিপ হিসেবে পেস্ট করতে পারবেন। যেমন, আপনার কাছে যদি এমন একটি তালিকা উইজেট থাকে যা ব্যবহারকারীকে একসাথে একাধিক আইটেম বেছে নিতে দেয়, তাহলে আপনি একবারে ক্লিপবোর্ডে সব আইটেম কপি করতে পারবেন। এটি করতে, তালিকার প্রতিটি আইটেমের জন্য আলাদা ClipData.Item তৈরি করুন এবং তারপরে ClipData অবজেক্টে ClipData.Item অবজেক্ট যোগ করুন।

ClipData কনভেনিয়েন্স পদ্ধতি

ClipData ক্লাস, একটি ClipData.Item অবজেক্ট ও একটি সাধারণ ClipDescription অবজেক্ট সহ ClipData অবজেক্ট তৈরি করার জন্য স্ট্যাটিক কনভেনিয়েন্স পদ্ধতি প্রদান করে:

newPlainText(label, text)
একটি ClipData অবজেক্ট রিটার্ন করে যার একটি ClipData.Item অবজেক্টে একটি টেক্সট স্ট্রিং থাকে। ClipDescription অবজেক্টের লেবেল label-এ সেট করা হয়েছে। ClipDescription-এ সিঙ্গেল MIME-এর ধরন হল MIMETYPE_TEXT_PLAIN।

টেক্সট স্ট্রিং থেকে ক্লিপ তৈরি করতে newPlainText() ব্যবহার করুন।

newUri(resolver, label, URI)
একটি ClipData অবজেক্ট রিটার্ন করে যার একটি ClipData.Item অবজেক্টে URI থাকে। ClipDescription অবজেক্টের লেবেল label-এ সেট করা হয়েছে। URI যদি কন্টেন্ট URI হয়—অর্থাৎ, যদি Uri.getScheme() content: রিটার্ন করে—তাহলে পদ্ধতিটি ContentResolver অবজেক্ট ব্যবহার করে যা resolver-এ প্রদান করা হয়েছে যাতে কন্টেন্ট প্রোভাইডার থেকে উপলভ্য MIME-এর প্রকার উদ্ধার করা যায়। তারপর এটি ClipDescription-এ সেভ করে। URI-এর জন্য যা content: URI নয়, পদ্ধতিটি MIME-এর প্রকারকে MIMETYPE_TEXT_URILIST-এ সেট করে।

URI থেকে ক্লিপ তৈরি করতে newUri() ব্যবহার করুন—বিশেষ করে 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() টেক্সট ফেরত পাঠায়।
URI
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.toUri(URI_INTENT_SCHEME)-এর ফেরত দেওয়া রিপ্রেজেন্টেশনের মতোই।

ক্লিপবোর্ড ফ্রেমওয়ার্কের সারসংক্ষেপ চিত্র ২-এ দেখানো হয়েছে। ডেটা কপি করতে, কোনও অ্যাপ্লিকেশন ClipboardManager গ্লোবাল ক্লিপবোর্ডে ClipData অবজেক্ট রাখে। ClipData-এ এক বা একাধিক ClipData.Item অবজেক্ট এবং একটি ClipDescription অবজেক্ট আছে। ডেটা পেস্ট করতে, কোনও অ্যাপ্লিকেশন ClipData পায়, ClipDescription থেকে এর MIME-এর প্রকার পায় এবং ClipData.Item থেকে অথবা ClipData.Item-এর মাধ্যমে উল্লেখ করা কন্টেন্ট প্রোভাইডার থেকে ডেটা পায়।

কপি ও পেস্ট ফ্রেমওয়ার্কের ব্লক ডায়াগ্রাম দেখানো ছবি
ছবি ২. Android ক্লিপবোর্ড ফ্রেমওয়ার্ক।

ক্লিপবোর্ডে কপি করুন

ক্লিপবোর্ডে ডেটা কপি করতে, গ্লোবাল ClipboardManager অবজেক্টের হ্যান্ডেল পান, ClipData অবজেক্ট তৈরি করুন এবং এতে একটি ClipDescription ও এক বা একাধিক ClipData.Item অবজেক্ট যোগ করুন। তারপর, সম্পূর্ণ করা ClipData অবজেক্টটি ClipboardManager অবজেক্টে যোগ করুন। নিম্নলিখিত পদ্ধতিতে এটি আরও বিস্তারিতভাবে বর্ণনা করা হয়েছে:

  1. আপনি কন্টেন্ট URI ব্যবহার করে ডেটা কপি করলে, কন্টেন্ট প্রোভাইডার সেট-আপ করুন।
  2. সিস্টেম ক্লিপবোর্ড পান:

    Kotlin

    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 অবজেক্টে ডেটা কপি করুন:

    • টেক্সটের জন্য

      Kotlin

      // 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-তে আইডেন্টিফায়ার এনকোড করা বিভাগে আলোচনা করা হয়েছে।

      Kotlin

      // 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 তৈরি করে এবং তারপরে এটি ক্লিপ অবজেক্টে রাখে:

      Kotlin

      // 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. ক্লিপবোর্ডে নতুন ক্লিপ অবজেক্ট রাখুন:

    Kotlin

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

    জাভা

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

ক্লিপবোর্ডে কপি করার সময় মতামত দিন

কোনও অ্যাপ ক্লিপবোর্ডে কন্টেন্ট কপি করলে, ব্যবহারকারীরা ভিজ্যুয়াল ফিডব্যাক আশা করেন। Android 13 এবং তার পরবর্তী ভার্সনে ব্যবহারকারীদের জন্য এটি অটোমেটিক করা হয়, তবে এর আগের ভার্সনে এটি ম্যানুয়ালি প্রয়োগ করতে হবে।

Android 13 থেকে শুরু করে, ক্লিপবোর্ডে কন্টেন্ট যোগ করা হলে সিস্টেম একটি স্ট্যান্ডার্ড ভিজ্যুয়াল কনফার্মেশন দেখায়। নতুন কনফার্মেশন নিম্নলিখিত কাজ করে:

  • কন্টেন্ট কপি করা হয়েছে কিনা তা কনফার্ম করে।
  • কপি করা কন্টেন্টের প্রিভিউ দেখায়।

Android 13 ক্লিপবোর্ড বিজ্ঞপ্তি দেখাচ্ছে এমন অ্যানিমেশন
ছবি ৩. Android 13 ও তার পরবর্তী যেকোনও ভার্সনে কন্টেন্ট ক্লিপবোর্ডে প্রবেশ করলে UI দেখানো হয়।

Android 12L (API লেভেল ৩২) এবং তার আগের ভার্সনে, ব্যবহারকারীরা কন্টেন্ট কপি করেছেন কিনা বা কী কপি করেছেন তা নিয়ে নিশ্চিত নাও হতে পারেন। এই ফিচার কপি করার পরে অ্যাপের মাধ্যমে দেখানো বিভিন্ন বিজ্ঞপ্তি স্ট্যান্ডার্ডাইজ করে এবং ক্লিপবোর্ডের উপর ব্যবহারকারীদের আরও নিয়ন্ত্রণ অফার করে।

ডুপ্লিকেট বিজ্ঞপ্তি এড়িয়ে চলুন

Android 12L (API লেভেল 32) এবং তার আগের ভার্সনে, ব্যবহারকারী সফলভাবে কপি করলে, আমরা ভিজ্যুয়াল, অ্যাপ-মধ্যস্থ ফিডব্যাক ইস্যু করে, কপি করার পরে Toast বা Snackbar-এর মতো উইজেট ব্যবহার করে ব্যবহারকারীকে সতর্ক করার সাজেশন দিই।

তথ্যের ডুপ্লিকেট ডিসপ্লে এড়াতে, আমরা Android 13 এবং তার পরবর্তী ভার্সনের জন্য অ্যাপ-মধ্যস্থ কপি করার পরে দেখানো টোস্ট বা স্ন্যাকবার সরানোর জন্য জোরালো সাজেশন দিই।

অ্যাপ-মধ্যস্থ কপি করার পরে পোস্ট স্ন্যাকবার।
ছবি ৪. Android 13-এ কপি কনফার্মেশন স্ন্যাকবার দেখালে, ব্যবহারকারী ডুপ্লিকেট মেসেজ দেখতে পান।
অ্যাপ-মধ্যস্থ কপি করার পরে টোস্ট পোস্ট করুন।
ছবি ৫. Android 13-এ কপি কনফার্মেশন টোস্ট দেখালে, ব্যবহারকারী ডুপ্লিকেট মেসেজ দেখতে পান।

এটি কীভাবে প্রয়োগ করতে হয় তার উদাহরণ এখানে দেওয়া হল:

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() কল করার আগে আপনাকে অবশ্যই ClipData-এ ClipDescription-এ একটি ফ্ল্যাগ যোগ করতে হবে। এই ফ্ল্যাগ যোগ করলে Android 13 এবং তার পরবর্তী ভার্সনে কপি করা কন্টেন্টের ভিজ্যুয়াল কনফার্মেশনে সংবেদনশীল কন্টেন্ট দেখানো আটকানো যায়।

সংবেদনশীল কন্টেন্ট ফ্ল্যাগ না করে কপি করা টেক্সটের প্রিভিউ
ছবি ৬. সংবেদনশীল কন্টেন্ট ফ্ল্যাগ ছাড়াই কপি করা টেক্সটের প্রিভিউ।
কপি করা টেক্সট প্রিভিউ সংবেদনশীল কন্টেন্ট ফ্ল্যাগ করছে।
ছবি ৭. সংবেদনশীল কন্টেন্ট ফ্ল্যাগ সহ কপি করা টেক্সটের প্রিভিউ।

সংবেদনশীল কন্টেন্ট ফ্ল্যাগ করতে, 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. getSystemService(CLIPBOARD_SERVICE) ব্যবহার করে গ্লোবাল ClipboardManager অবজেক্ট পান। এছাড়াও, পেস্ট করা টেক্সট ধারণ করার জন্য একটি গ্লোবাল ভেরিয়েবেল ঘোষণা করুন:

    Kotlin

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

    জাভা

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
  2. বর্তমান অ্যাক্টিভিটিতে "পেস্ট" বিকল্প চালু বা বন্ধ করতে হবে কিনা তা নির্ধারণ করুন। ক্লিপবোর্ডে ক্লিপ আছে কিনা এবং ক্লিপের মাধ্যমে দেখানো ডেটার ধরন আপনি ম্যানেজ করতে পারবেন কিনা তা যাচাই করুন:

    Kotlin

    // 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 আছে কিনা তা আপনি এখনও জানেন না। নিম্নলিখিত কোড স্নিপেট এটি পরীক্ষা করে, তবে এটি শুধুমাত্র প্লেন টেক্সট ম্যানেজ করার কোড দেখায়:

    Kotlin

    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 অবজেক্টে কন্টেন্ট URI থাকলে এবং আপনি যদি নির্ধারণ করেন যে আপনি এর MIME প্রকারগুলির মধ্যে একটি ম্যানেজ করতে পারবেন, তাহলে একটি ContentResolver তৈরি করুন এবং ডেটা ফিরিয়ে আনতে উপযুক্ত কন্টেন্ট প্রোভাইডার পদ্ধতি কল করুন।

ক্লিপবোর্ডে কন্টেন্ট URI-এর উপর ভিত্তি করে কন্টেন্ট প্রোভাইডারের থেকে কীভাবে ডেটা পাওয়া যায় তা নিম্নলিখিত পদ্ধতিতে বর্ণনা করা হয়েছে। এটি চেক করে দেখে যে অ্যাপ্লিকেশন ব্যবহার করতে পারে এমন MIME-এর ধরন প্রদানকারীর কাছে উপলভ্য আছে কিনা।

  1. MIME-এর প্রকার ধারণ করার জন্য একটি গ্লোবাল ভেরিয়েবেল ঘোষণা করুন:

    Kotlin

    // 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. গ্লোবাল ক্লিপবোর্ড পান। এছাড়াও, কন্টেন্ট রিজলভার পান যাতে আপনি কন্টেন্ট প্রোভাইডার অ্যাক্সেস করতে পারেন:

    Kotlin

    // 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 হিসেবে এর কন্টেন্ট পান:

    Kotlin

    // 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. URI-টি কন্টেন্ট URI কিনা তা getType(Uri)-এ কল করে টেস্ট করুন। Uri সঠিক কন্টেন্ট প্রোভাইডারকে পয়েন্ট না করলে, এই পদ্ধতি নাল রিটার্ন করে।

    Kotlin

        // 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।

    Kotlin

            // 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();
                 }
             }
         }
    }

ইনটেন্ট পেস্ট করুন

কোনও ইনটেন্ট পেস্ট করতে, প্রথমে গ্লোবাল ক্লিপবোর্ড পান। ClipData.Item অবজেক্টটি পরীক্ষা করে দেখুন এতে Intent আছে কিনা। তারপর আপনার নিজের স্টোরেজে ইনটেন্ট কপি করতে getIntent() কল করুন। নিম্নলিখিত স্নিপেট এটি দেখায়:

Kotlin

// 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.
}

আপনার অ্যাপ ক্লিপবোর্ড ডেটা অ্যাক্সেস করলে সিস্টেম বিজ্ঞপ্তি দেখানো হয়

Android 12 (API লেভেল 31) এবং তার পরবর্তী ভার্সনে, আপনার অ্যাপ getPrimaryClip() কল করলে, সিস্টেম সাধারণত একটি টোস্ট মেসেজ দেখায়। মেসেজের মধ্যে থাকা টেক্সটে নিম্নলিখিত ফর্ম্যাট রয়েছে:

APP pasted from your clipboard

আপনার অ্যাপ নিম্নলিখিত কোনও একটি কাজ করলে, সিস্টেম টোস্ট মেসেজ দেখায় না:

  • আপনার নিজের অ্যাপ থেকে ClipData অ্যাক্সেস করে।
  • নির্দিষ্ট অ্যাপ থেকে বারবার ClipData অ্যাক্সেস করে। আপনার অ্যাপ যখন প্রথমবার সেই অ্যাপ থেকে ডেটা অ্যাক্সেস করে, তখনই শুধু টোস্ট মেসেজ দেখানো হয়।
  • ক্লিপ অবজেক্টের জন্য মেটাডেটা ফিরিয়ে আনে, যেমন getPrimaryClip()-এর পরিবর্তে getPrimaryClipDescription() কল করার মাধ্যমে।

জটিল ডেটা কপি করতে কন্টেন্ট প্রোভাইডার ব্যবহার করুন

কন্টেন্ট প্রোভাইডার ডেটাবেস রেকর্ড বা ফাইল স্ট্রিমের মতো জটিল ডেটা কপি করতে সাহায্য করে। ডেটা কপি করতে, ক্লিপবোর্ডে কন্টেন্ট URI রাখুন। পেস্ট করা অ্যাপ্লিকেশনগুলি তখন ক্লিপবোর্ড থেকে এই URI পায় এবং ডাটাবেস ডেটা বা ফাইল স্ট্রিম বর্ণনাকারী পুনরুদ্ধার করতে এটি ব্যবহার করে।

যেহেতু পেস্ট করা অ্যাপ্লিকেশনে শুধুমাত্র আপনার ডেটার জন্য কন্টেন্ট URI আছে, তাই এটি জানতে হবে যে কোন ডেটা পুনরুদ্ধার করতে হবে। আপনি URI-তে ডেটার জন্য একটি শনাক্তকারী এনকোড করে এই তথ্য প্রদান করতে পারেন অথবা আপনি একটি অনন্য URI প্রদান করতে পারেন যা আপনার কপি করতে চাওয়া ডেটা প্রদান করে। আপনি কোন পদ্ধতি বেছে নেবেন তা আপনার ডেটার সংগঠনের উপর নির্ভর করে।

নিচের বিভাগগুলি থেকে URI সেট-আপ করা, জটিল ডেটা প্রদান করা এবং ফাইল স্ট্রিম প্রদান করার ব্যাপারে জানতে পারবেন। বিবরণগুলি ধরে নেয় যে আপনি কন্টেন্ট প্রোভাইডার ডিজাইনের সাধারণ নীতিগুলির সাথে পরিচিত।

URI-তে একটি শনাক্তকারী এনকোড করুন

URI সহ ক্লিপবোর্ডে ডেটা কপি করার একটি কার্যকর কৌশল হল URI-তে ডেটার জন্য একটি শনাক্তকারী এনকোড করা। আপনার কন্টেন্ট প্রোভাইডার URI থেকে শনাক্তকারী পেতে এবং ডেটা ফিরিয়ে আনতে এটি ব্যবহার করতে পারবে। পেস্ট করা অ্যাপ্লিকেশনকে জানতে হবে না যে শনাক্তকারীটি আছে। এটি শুধু ক্লিপবোর্ড থেকে আপনার "রেফারেন্স"—URI এবং শনাক্তকারী—পায়, এটি আপনার কন্টেন্ট প্রোভাইডারকে দেয় এবং ডেটা ফেরত পায়।

আপনি সাধারণত কন্টেন্ট URI-এর শেষে একটি আইডেন্টিফায়ার যোগ করে সেটি এনকোড করেন। যেমন, ধরুন আপনি নিজের পরিষেবা প্রদানকারীর URI নিম্নলিখিত স্ট্রিং হিসেবে সংজ্ঞায়িত করেছেন:

"content://com.example.contacts"

আপনি এই URI-তে কোনও নাম এনকোড করতে চাইলে, নিম্নলিখিত কোড স্নিপেট ব্যবহার করুন:

Kotlin

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()
পেস্ট করার অ্যাপ্লিকেশন ধরে নেয় যে আপনি ক্লিপবোর্ডে যে URI রেখেছেন তার সাথে এই পদ্ধতি ব্যবহার করে তারা আপনার ডেটা পেতে পারে। কপি করার সুবিধা দিতে, এই পদ্ধতিতে URI শনাক্ত করুন যাতে বিশেষ "কপি" পাথ থাকে। আপনার অ্যাপ্লিকেশন তখন ক্লিপবোর্ডে রাখার জন্য একটি "কপি" URI তৈরি করতে পারে, যার মধ্যে কপি করার পাথ এবং আপনি যে রেকর্ডটি কপি করতে চান তার একটি পয়েন্টার থাকে।
getType()
আপনি যে ডেটা কপি করতে চান তার জন্য এই পদ্ধতিকে অবশ্যই MIME-এর প্রকার রিটার্ন করতে হবে। নতুন ClipData অবজেক্টে MIME-এর প্রকার যোগ করতে newUri() মেথড getType()-কে কল করে।

জটিল ডেটার জন্য MIME-এর ধরন কন্টেন্ট প্রোভাইডার-এ বর্ণনা করা হয়েছে।

আপনাকে অন্য কোনও কন্টেন্ট প্রোভাইডার পদ্ধতি ব্যবহার করতে হবে না, যেমন insert() বা update()। পেস্ট করার অ্যাপ্লিকেশনকে শুধু আপনার কাজ করে এমন MIME-এর প্রকার পেতে হবে এবং আপনার পরিষেবা প্রদানকারীর থেকে ডেটা কপি করতে হবে। আপনার কাছে আগে থেকেই এইসব পদ্ধতি থাকলে, সেগুলি কপি করার অপারেশনে কোনও বাধা সৃষ্টি করবে না।

জটিল ডেটা কপি করার জন্য কীভাবে আপনার অ্যাপ্লিকেশন সেট-আপ করবেন তা নিম্নলিখিত স্নিপেট থেকে জানতে পারবেন:

  1. আপনার অ্যাপ্লিকেশনের গ্লোবাল কনস্ট্যান্টে, একটি বেস URI স্ট্রিং এবং একটি পাথ ঘোষণা করুন যা ডেটা কপি করার জন্য আপনি যে URI স্ট্রিং ব্যবহার করছেন তা শনাক্ত করে। এছাড়াও, কপি করা ডেটার জন্য MIME-এর ধরন ঘোষণা করুন।

    Kotlin

    // 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 রাখুন।

    Kotlin

    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-এর সাথে মেলে।

    Kotlin

    // 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 প্যাটার্ন ম্যানেজ করতে পারে, তবে শুধুমাত্র ক্লিপবোর্ড কপি করার অপারেশনের প্যাটার্ন দেখায়।

    Kotlin

    // 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. কপি করা ডেটার জন্য উপযুক্ত MIME-এর প্রকার ফিরিয়ে দিতে getType() পদ্ধতি সেট-আপ করুন:

    Kotlin

    // 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 পাবেন এবং ডেটা পেতে ও পেস্ট করতে এটি ব্যবহার করবেন।

ডেটা স্ট্রিম কপি করুন

আপনি স্ট্রিম হিসেবে প্রচুর পরিমাণে টেক্সট ও বাইনারি ডেটা কপি ও পেস্ট করতে পারবেন। ডেটা নিম্নলিখিত ফর্ম্যাটে থাকতে পারে:

  • আসল ডিভাইসে স্টোর করা ফাইল
  • সকেট থেকে স্ট্রিম
  • পরিষেবা প্রদানকারীর অন্তর্নিহিত ডেটাবেস সিস্টেমে প্রচুর পরিমাণে ডেটা স্টোর করা হয়

ডেটা স্ট্রিমের জন্য কন্টেন্ট প্রোভাইডার, Cursor অবজেক্টের পরিবর্তে AssetFileDescriptor-এর মতো ফাইল ডেসক্রিপটর অবজেক্টের মাধ্যমে এর ডেটা অ্যাক্সেস করার সুবিধা প্রদান করে। পেস্ট করা অ্যাপ্লিকেশন এই ফাইল ডেসক্রিপটর ব্যবহার করে ডেটা স্ট্রিম পড়ে।

কোনও পরিষেবা প্রদানকারীর সাথে ডেটা স্ট্রিম কপি করার জন্য আপনার অ্যাপ্লিকেশন সেট-আপ করতে, এইসব ধাপ অনুসরণ করুন:

  1. ক্লিপবোর্ডে যে ডেটা স্ট্রিম রাখছেন তার জন্য কন্টেন্ট URI সেট-আপ করুন। এটি করার জন্য নিম্নলিখিত বিকল্পগুলি অন্তর্ভুক্ত রয়েছে:
    • URI-তে ডেটা স্ট্রিমের জন্য একটি শনাক্তকারী এনকোড করুন, যেমনটি URI-তে একটি শনাক্তকারী এনকোড করুন বিভাগে বর্ণনা করা হয়েছে এবং তারপরে আপনার প্রদানকারীর মধ্যে একটি টেবিল বজায় রাখুন যাতে শনাক্তকারী এবং সংশ্লিষ্ট স্ট্রিমের নাম থাকে।
    • URI-তে সরাসরি স্ট্রিমের নাম এনকোড করুন।
    • এমন একটি অনন্য URI ব্যবহার করুন যা সবসময় পরিষেবা প্রদানকারীর থেকে বর্তমান স্ট্রিম ফিরিয়ে দেয়। আপনি এই বিকল্প ব্যবহার করলে, URI ব্যবহার করে ক্লিপবোর্ডে স্ট্রিম কপি করার সময়, অন্য কোনও স্ট্রিম দেখানোর জন্য আপনার প্রদানকারীকে আপডেট করতে ভুলবেন না।
  2. আপনি যে ধরনের ডেটা স্ট্রিম অফার করার পরিকল্পনা করছেন তার প্রতিটি ধরনের জন্য একটি MIME-এর প্রকার প্রদান করুন। পেস্ট করা অ্যাপ্লিকেশন ক্লিপবোর্ডে ডেটা পেস্ট করতে পারবে কিনা তা নির্ধারণ করতে এই তথ্যের প্রয়োজন।
  3. ContentProvider পদ্ধতির মধ্যে একটি প্রয়োগ করুন যা স্ট্রিমের জন্য ফাইল ডেসক্রিপটর ফেরত দেয়। আপনি কন্টেন্ট URI-তে শনাক্তকারী এনকোড করলে, কোন স্ট্রিম খুলতে হবে তা নির্ধারণ করতে এই পদ্ধতি ব্যবহার করুন।
  4. ক্লিপবোর্ডে ডেটা স্ট্রিম কপি করতে, কন্টেন্ট URI তৈরি করুন এবং এটি ক্লিপবোর্ডে রাখুন।

ডেটা স্ট্রিম পেস্ট করতে, কোনও অ্যাপ্লিকেশন ক্লিপবোর্ড থেকে ক্লিপ পায়, URI পায় এবং স্ট্রিম খোলে এমন ContentResolver ফাইল ডেসক্রিপটর পদ্ধতিতে কল করার জন্য এটি ব্যবহার করে। ContentResolver মেথড সংশ্লিষ্ট ContentProvider মেথডকে কল করে, এটিকে কন্টেন্ট URI পাস করে। আপনার পরিষেবা প্রদানকারী ফাইল ডেসক্রিপটরকে ContentResolver পদ্ধতিতে ফিরিয়ে দেয়। তারপর পেস্ট করা অ্যাপ্লিকেশনটির দায়িত্ব হল স্ট্রিম থেকে ডেটা পড়া।

নিচের তালিকায় কন্টেন্ট প্রোভাইডারের জন্য সবচেয়ে গুরুত্বপূর্ণ ফাইল ডেসক্রিপটর পদ্ধতি দেখানো হয়েছে। এগুলির প্রতিটির একটি করে সংশ্লিষ্ট ContentResolver পদ্ধতি আছে, যার স্ট্রিং "Descriptor" পদ্ধতির নামের সাথে যোগ করা আছে। যেমন, ContentResolver-এর অ্যানালগ openAssetFile() হল openAssetFileDescriptor()।

openTypedAssetFile()

এই পদ্ধতি অ্যাসেট ফাইল ডেসক্রিপটর ফিরিয়ে দেয়, তবে শুধুমাত্র যদি প্রদত্ত MIME-এর প্রকার প্রদানকারীর দ্বারা সমর্থিত হয়। কলার—যে অ্যাপ্লিকেশন পেস্ট করছে—সেটি MIME-এর প্রকারের প্যাটার্ন প্রদান করে। ক্লিপবোর্ডে URI কপি করে এমন অ্যাপ্লিকেশনের কন্টেন্ট প্রোভাইডার AssetFileDescriptor ফাইল হ্যান্ডেল রিটার্ন করে যদি এটি সেই MIME-এর প্রকার প্রদান করতে পারে এবং না পারলে ব্যতিক্রম থ্রো করে।

এই পদ্ধতি ফাইলের সাবসেকশন ম্যানেজ করে। কন্টেন্ট প্রোভাইডার ক্লিপবোর্ডে কপি করেছে এমন অ্যাসেট পড়ার জন্য আপনি এটি ব্যবহার করতে পারবেন।

openAssetFile()
এই পদ্ধতিটি openTypedAssetFile()-এর আরও সাধারণ ফর্ম। এটি অনুমোদিত MIME-এর প্রকারের জন্য ফিল্টার করে না, তবে এটি ফাইলের সাবসেকশন পড়তে পারে।
openFile()
এটি openAssetFile()-এর আরও সাধারণ ফর্ম। এটি ফাইলের সাবসেকশন পড়তে পারে না।

আপনি চাইলে ফাইল ডেসক্রিপটর পদ্ধতির সাথে openPipeHelper() পদ্ধতি ব্যবহার করতে পারেন। এটি পেস্ট করা অ্যাপ্লিকেশনটিকে একটি পাইপ ব্যবহার করে ব্যাকগ্রাউন্ড থ্রেডে স্ট্রিম ডেটা পড়তে দেয়। এই পদ্ধতি ব্যবহার করতে, ContentProvider.PipeDataWriter ইন্টারফেস প্রয়োগ করুন।

কার্যকর কপি ও পেস্ট ফাংশনালিটি ডিজাইন করা

আপনার অ্যাপ্লিকেশনের জন্য কার্যকর কপি ও পেস্ট কার্যকারিতা ডিজাইন করতে, এইসব পয়েন্ট মনে রাখবেন:

  • যেকোনও সময়, ক্লিপবোর্ডে শুধুমাত্র একটি ক্লিপ থাকে। সিস্টেমের যেকোনও অ্যাপ্লিকেশন দ্বারা নতুন কপি অপারেশন আগের ক্লিপ ওভাররাইট করে। ব্যবহারকারী আপনার অ্যাপ্লিকেশন থেকে নেভিগেট করে অন্য কোথাও যেতে পারেন এবং ফিরে আসার আগে কপি করতে পারেন, তাই আপনি ধরে নিতে পারেন না যে ক্লিপবোর্ডে সেই ক্লিপটিই আছে যা ব্যবহারকারী আগে আপনার অ্যাপ্লিকেশনে কপি করেছিলেন।
  • ক্লিপ প্রতি একাধিক ClipData.Item অবজেক্টের উদ্দেশ্য হল একটি সিলেকশনের বিভিন্ন রেফারেন্সের পরিবর্তে একাধিক সিলেকশন কপি ও পেস্ট করার সুবিধা প্রদান করা। আপনি সাধারণত চান যে ক্লিপের সব ClipData.Item অবজেক্টের ফর্ম যেন একই হয়। অর্থাৎ, এগুলি অবশ্যই সাধারণ টেক্সট, কন্টেন্ট URI বা Intent হতে হবে এবং মিশ্রিত হলে চলবে না।
  • আপনি ডেটা প্রদান করার সময়, বিভিন্ন MIME উপস্থাপনা অফার করতে পারেন। আপনি যে MIME-এর প্রকার সাপোর্ট করেন তা ClipDescription-এ যোগ করুন এবং তারপরে আপনার কন্টেন্ট প্রোভাইডারে MIME-এর প্রকার প্রয়োগ করুন।
  • ক্লিপবোর্ড থেকে ডেটা পাওয়ার সময়, উপলভ্য MIME-এর ধরন চেক করা এবং তারপরে কোনটি ব্যবহার করা হবে তা নির্ধারণ করার দায়িত্ব আপনার অ্যাপ্লিকেশনের। এমনকি ক্লিপবোর্ডে কোনও ক্লিপ থাকলেও এবং ব্যবহারকারী পেস্ট করার অনুরোধ করলেও, আপনার অ্যাপ্লিকেশনকে পেস্ট করতে হবে না। MIME-এর ধরন মানানসই হলে পেস্ট করুন। আপনি coerceToText() ব্যবহার করে ক্লিপবোর্ডে থাকা ডেটা টেক্সটে পরিবর্তন করতে পারেন। আপনার অ্যাপ্লিকেশন যদি উপলভ্য MIME-এর প্রকারের মধ্যে একাধিক প্রকারকে সমর্থন করে, তাহলে আপনি ব্যবহারকারীকে বেছে নিতে দিতে পারেন যে কোনটি ব্যবহার করতে হবে।