একবার আপনি কন্টেন্ট URI ব্যবহার করে ফাইল শেয়ার করার জন্য আপনার অ্যাপ সেট আপ করলে, আপনি সেই ফাইলগুলির জন্য অন্যান্য অ্যাপের অনুরোধে সাড়া দিতে পারেন। এই অনুরোধগুলিতে সাড়া দেওয়ার একটি উপায় হল সার্ভার অ্যাপ থেকে একটি ফাইল নির্বাচন ইন্টারফেস প্রদান করা যা অন্য অ্যাপ্লিকেশনগুলিকে আহ্বান করতে পারে। এই পদ্ধতিটি একটি ক্লায়েন্ট অ্যাপ্লিকেশনকে ব্যবহারকারীদের সার্ভার অ্যাপ থেকে একটি ফাইল নির্বাচন করতে দেয় এবং তারপরে নির্বাচিত ফাইলের বিষয়বস্তু URI পেতে দেয়।
এই পাঠটি আপনাকে দেখায় কিভাবে আপনার অ্যাপে একটি ফাইল নির্বাচন Activity
তৈরি করতে হয় যা ফাইলের অনুরোধে সাড়া দেয়।
ফাইল অনুরোধ গ্রহণ
ক্লায়েন্ট অ্যাপ্লিকেশানগুলি থেকে ফাইলগুলির জন্য অনুরোধগুলি পেতে এবং একটি বিষয়বস্তু URI দিয়ে প্রতিক্রিয়া জানাতে, আপনার অ্যাপটিকে একটি ফাইল নির্বাচন Activity
প্রদান করা উচিত। ক্লায়েন্ট অ্যাপ্লিকেশানগুলি ACTION_PICK
অ্যাকশন ধারণকারী একটি Intent
সহ startActivityForResult()
কল করে এই Activity
শুরু করে। যখন ক্লায়েন্ট অ্যাপ startActivityForResult()
কল করে, আপনার অ্যাপ ব্যবহারকারীর নির্বাচিত ফাইলের জন্য একটি বিষয়বস্তু URI আকারে ক্লায়েন্ট অ্যাপে একটি ফলাফল ফেরত দিতে পারে।
একটি ক্লায়েন্ট অ্যাপে একটি ফাইলের জন্য একটি অনুরোধ কীভাবে বাস্তবায়ন করতে হয় তা শিখতে, একটি ভাগ করা ফাইলের অনুরোধ করার পাঠটি দেখুন।
একটি ফাইল নির্বাচন কার্যকলাপ তৈরি করুন
ফাইল নির্বাচন Activity
সেট আপ করতে, আপনার ম্যানিফেস্টে Activity
নির্দিষ্ট করে শুরু করুন, একটি অভিপ্রায় ফিল্টার সহ যা অ্যাকশন ACTION_PICK
এবং CATEGORY_DEFAULT
এবং CATEGORY_OPENABLE
বিভাগগুলির সাথে মেলে। এছাড়াও আপনার অ্যাপ অন্যান্য অ্যাপে যে ফাইলগুলি পরিবেশন করে তার জন্য MIME ধরনের ফিল্টার যোগ করুন। নিম্নলিখিত স্নিপেট আপনাকে দেখায় কিভাবে নতুন Activity
এবং অভিপ্রায় ফিল্টার নির্দিষ্ট করতে হয়:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> ... <application> ... <activity android:name=".FileSelectActivity" android:label="@File Selector" > <intent-filter> <action android:name="android.intent.action.PICK"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.OPENABLE"/> <data android:mimeType="text/plain"/> <data android:mimeType="image/*"/> </intent-filter> </activity>
কোডে ফাইল নির্বাচন কার্যকলাপ সংজ্ঞায়িত করুন
এরপরে, একটি Activity
সাবক্লাস সংজ্ঞায়িত করুন যা আপনার অ্যাপের files/images/
ডিরেক্টরি থেকে উপলব্ধ ফাইলগুলিকে অভ্যন্তরীণ স্টোরেজে প্রদর্শন করে এবং ব্যবহারকারীকে পছন্দসই ফাইল বাছাই করতে দেয়। নিম্নলিখিত স্নিপেটটি প্রদর্শন করে কিভাবে এই Activity
সংজ্ঞায়িত করতে হয় এবং ব্যবহারকারীর নির্বাচনের প্রতিক্রিয়া জানাতে হয়:
কোটলিন
class MainActivity : Activity() { // The path to the root of this app's internal storage private lateinit var privateRootDir: File // The path to the "images" subdirectory private lateinit var imagesDir: File // Array of files in the images subdirectory private lateinit var imageFiles: Array<File> // Array of filenames corresponding to imageFiles private lateinit var imageFilenames: Array<String> // Initialize the Activity override fun onCreate(savedInstanceState: Bundle?) { ... // Set up an Intent to send back to apps that request a file resultIntent = Intent("com.example.myapp.ACTION_RETURN_FILE") // Get the files/ subdirectory of internal storage privateRootDir = filesDir // Get the files/images subdirectory; imagesDir = File(privateRootDir, "images") // Get the files in the images subdirectory imageFiles = imagesDir.listFiles() // Set the Activity's result to null to begin with setResult(Activity.RESULT_CANCELED, null) /* * Display the file names in the ListView fileListView. * Back the ListView with the array imageFilenames, which * you can create by iterating through imageFiles and * calling File.getAbsolutePath() for each File */ ... } ... }
জাভা
public class MainActivity extends Activity { // The path to the root of this app's internal storage private File privateRootDir; // The path to the "images" subdirectory private File imagesDir; // Array of files in the images subdirectory File[] imageFiles; // Array of filenames corresponding to imageFiles String[] imageFilenames; // Initialize the Activity @Override protected void onCreate(Bundle savedInstanceState) { ... // Set up an Intent to send back to apps that request a file resultIntent = new Intent("com.example.myapp.ACTION_RETURN_FILE"); // Get the files/ subdirectory of internal storage privateRootDir = getFilesDir(); // Get the files/images subdirectory; imagesDir = new File(privateRootDir, "images"); // Get the files in the images subdirectory imageFiles = imagesDir.listFiles(); // Set the Activity's result to null to begin with setResult(Activity.RESULT_CANCELED, null); /* * Display the file names in the ListView fileListView. * Back the ListView with the array imageFilenames, which * you can create by iterating through imageFiles and * calling File.getAbsolutePath() for each File */ ... } ... }
একটি ফাইল নির্বাচন প্রতিক্রিয়া
একবার একজন ব্যবহারকারী একটি ভাগ করা ফাইল নির্বাচন করলে, আপনার অ্যাপ্লিকেশনটিকে অবশ্যই নির্ধারণ করতে হবে কোন ফাইলটি নির্বাচন করা হয়েছে এবং তারপর ফাইলটির জন্য একটি সামগ্রী URI তৈরি করতে হবে৷ যেহেতু Activity
একটি ListView
এ উপলব্ধ ফাইলগুলির তালিকা প্রদর্শন করে, যখন ব্যবহারকারী একটি ফাইলের নাম ক্লিক করে সিস্টেমটি কল করে onItemClick()
পদ্ধতিটি, যেখানে আপনি নির্বাচিত ফাইলটি পেতে পারেন।
একটি ফাইলের ইউআরআই এক অ্যাপ থেকে অন্য অ্যাপে পাঠানোর উদ্দেশ্য ব্যবহার করার সময়, অন্য অ্যাপ পড়তে পারে এমন একটি URI পেতে আপনাকে অবশ্যই সতর্ক থাকতে হবে। Android 6.0 (API লেভেল 23) এবং পরে চলমান ডিভাইসগুলিতে এটি করার জন্য বিশেষ যত্নের প্রয়োজন কারণ Android-এর সেই সংস্করণে অনুমতি মডেলের পরিবর্তন, বিশেষ করে READ_EXTERNAL_STORAGE
এর একটি বিপজ্জনক অনুমতি হয়ে উঠছে, যা গ্রহণকারী অ্যাপের অভাব হতে পারে৷
এই বিবেচনাগুলি মাথায় রেখে, আমরা সুপারিশ করি যে আপনি Uri.fromFile()
ব্যবহার করা এড়িয়ে চলুন, যা বেশ কয়েকটি ত্রুটি উপস্থাপন করে। এই পদ্ধতি:
- প্রোফাইল জুড়ে ফাইল শেয়ার করার অনুমতি দেয় না।
- Android 4.4 (API স্তর 19) বা তার নিচের সংস্করণে চলমান ডিভাইসগুলিতে আপনার অ্যাপের
WRITE_EXTERNAL_STORAGE
অনুমতি থাকা প্রয়োজন৷ - প্রাপ্ত অ্যাপগুলির
READ_EXTERNAL_STORAGE
অনুমতি থাকা প্রয়োজন, যা গুরুত্বপূর্ণ শেয়ার লক্ষ্যে ব্যর্থ হবে, যেমন Gmail, যেগুলির কাছে সেই অনুমতি নেই৷
Uri.fromFile()
ব্যবহার করার পরিবর্তে, আপনি নির্দিষ্ট URI-তে অন্যান্য অ্যাপকে অ্যাক্সেস দেওয়ার জন্য URI অনুমতি ব্যবহার করতে পারেন। URI অনুমতি Uri.fromFile()
দ্বারা উত্পন্ন file://
URI-তে কাজ না করলেও, তারা বিষয়বস্তু প্রদানকারীদের সাথে যুক্ত URI-তে কাজ করে। FileProvider
API আপনাকে এই ধরনের URI তৈরি করতে সাহায্য করতে পারে। এই পদ্ধতিটি এমন ফাইলগুলির সাথেও কাজ করে যেগুলি বাহ্যিক স্টোরেজে নয়, কিন্তু অভিপ্রায় পাঠানোর অ্যাপের স্থানীয় সঞ্চয়স্থানে।
onItemClick()
এ, নির্বাচিত ফাইলের ফাইলের নামের জন্য একটি File
অবজেক্ট পান এবং এটিকে একটি আর্গুমেন্ট হিসাবে getUriForFile()
এ পাস করুন, সেই অথরিটির সাথে যা আপনি FileProvider
এর জন্য <provider>
উপাদানে নির্দিষ্ট করেছেন। ফলস্বরূপ কন্টেন্ট ইউআরআই-এ কর্তৃপক্ষ, ফাইলের ডিরেক্টরির সাথে সম্পর্কিত একটি পাথ সেগমেন্ট (এক্সএমএল মেটা-ডেটাতে উল্লেখ করা হয়েছে) এবং ফাইলের নাম এর এক্সটেনশন সহ রয়েছে। কিভাবে FileProvider
এক্সএমএল মেটা-ডেটার উপর ভিত্তি করে ডাইরেক্টরিগুলিকে পাথ সেগমেন্টে ম্যাপ করে সে অংশে বর্ণনা করা হয়েছে শেয়ারযোগ্য ডিরেক্টরি নির্দিষ্ট করুন ।
নিম্নলিখিত স্নিপেট আপনাকে দেখায় কিভাবে নির্বাচিত ফাইলটি সনাক্ত করতে হয় এবং এটির জন্য একটি সামগ্রী URI পেতে হয়:
কোটলিন
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> /* * Get a File for the selected file name. * Assume that the file names are in the * imageFilename array. */ val requestFile = File(imageFilenames[position]) /* * Most file-related method calls need to be in * try-catch blocks. */ // Use the FileProvider to get a content URI val fileUri: Uri? = try { FileProvider.getUriForFile( this@MainActivity, "com.example.myapp.fileprovider", requestFile) } catch (e: IllegalArgumentException) { Log.e("File Selector", "The selected file can't be shared: $requestFile") null } ... } ... }
জাভা
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override /* * When a filename in the ListView is clicked, get its * content URI and send it to the requesting app */ public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) { /* * Get a File for the selected file name. * Assume that the file names are in the * imageFilename array. */ File requestFile = new File(imageFilename[position]); /* * Most file-related method calls need to be in * try-catch blocks. */ // Use the FileProvider to get a content URI try { fileUri = FileProvider.getUriForFile( MainActivity.this, "com.example.myapp.fileprovider", requestFile); } catch (IllegalArgumentException e) { Log.e("File Selector", "The selected file can't be shared: " + requestFile.toString()); } ... } }); ... }
মনে রাখবেন যে আপনি শুধুমাত্র সেই ফাইলগুলির জন্য সামগ্রী URI তৈরি করতে পারেন যা আপনার নির্দিষ্ট করা একটি মেটা-ডেটা ফাইলে থাকে যেখানে <paths>
উপাদান রয়েছে, যেমন ভাগ করা যায় এমন ডিরেক্টরি উল্লেখ করুন। যদি আপনি একটি File
জন্য getUriForFile()
কল করেন যেটি আপনি নির্দিষ্ট করেননি, তাহলে আপনি একটি IllegalArgumentException
পাবেন।
ফাইলের জন্য অনুমতি দিন
আপনি যে ফাইলটি অন্য অ্যাপের সাথে শেয়ার করতে চান তার জন্য এখন আপনার কাছে একটি কন্টেন্ট URI আছে, আপনাকে ক্লায়েন্ট অ্যাপটিকে ফাইলটি অ্যাক্সেস করার অনুমতি দিতে হবে। অ্যাক্সেসের অনুমতি দিতে, একটি Intent
সামগ্রী URI যোগ করে এবং তারপরে Intent
অনুমতি ফ্ল্যাগ সেট করে ক্লায়েন্ট অ্যাপকে অনুমতি দিন। আপনি যে অনুমতিগুলি প্রদান করেন তা অস্থায়ী এবং স্বয়ংক্রিয়ভাবে মেয়াদ শেষ হয়ে যায় যখন গ্রহনকারী অ্যাপের টাস্ক স্ট্যাক শেষ হয়।
নিম্নলিখিত কোড স্নিপেট আপনাকে দেখায় কিভাবে ফাইলের জন্য পড়ার অনুমতি সেট করতে হয়:
কোটলিন
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> ... if (fileUri != null) { // Grant temporary read permission to the content URI resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) ... } ... } ... }
জাভা
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks in the ListView fileListView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) { ... if (fileUri != null) { // Grant temporary read permission to the content URI resultIntent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION); } ... } ... }); ... }
সতর্কতা: অস্থায়ী অ্যাক্সেসের অনুমতি ব্যবহার করে আপনার ফাইলগুলিতে নিরাপদে অ্যাক্সেস দেওয়ার একমাত্র উপায় হল setFlags()
কল করা। একটি ফাইলের বিষয়বস্তুর URI-এর জন্য Context.grantUriPermission()
পদ্ধতিতে কল করা এড়িয়ে চলুন, যেহেতু এই পদ্ধতিটি অ্যাক্সেস মঞ্জুর করে যা আপনি শুধুমাত্র Context.revokeUriPermission()
কল করে প্রত্যাহার করতে পারেন।
Uri.fromFile()
ব্যবহার করবেন না। এটি গ্রহনকারী অ্যাপগুলিকে READ_EXTERNAL_STORAGE
অনুমতি নিতে বাধ্য করে, আপনি যদি ব্যবহারকারীদের মধ্যে শেয়ার করার চেষ্টা করছেন এবং Android এর 4.4 (API স্তর 19) এর চেয়ে কম সংস্করণে, তাহলে আপনার অ্যাপের WRITE_EXTERNAL_STORAGE
থাকা প্রয়োজন হলে তা মোটেও কাজ করবে না। এবং সত্যিই গুরুত্বপূর্ণ শেয়ার টার্গেট, যেমন Gmail অ্যাপে READ_EXTERNAL_STORAGE
নেই, যার ফলে এই কলটি ব্যর্থ হয়েছে৷ পরিবর্তে, আপনি নির্দিষ্ট URI-তে অন্যান্য অ্যাপকে অ্যাক্সেস দেওয়ার জন্য URI অনুমতিগুলি ব্যবহার করতে পারেন। URI অনুমতিগুলি Uri.fromFile()
দ্বারা তৈরি করা ফাইল:// URI-তে কাজ না করলেও, তারা বিষয়বস্তু প্রদানকারীদের সাথে যুক্ত Uris-এ কাজ করে। শুধুমাত্র এই জন্য আপনার নিজের বাস্তবায়ন করার পরিবর্তে, আপনি ফাইল শেয়ারিং -এ ব্যাখ্যা করা হিসাবে FileProvider
ব্যবহার করতে পারেন এবং করা উচিত।
অনুরোধকারী অ্যাপের সাথে ফাইলটি শেয়ার করুন
যে অ্যাপটি অনুরোধ করেছে তার সাথে ফাইলটি শেয়ার করতে, কন্টেন্ট ইউআরআই সহ Intent
পাস করুন এবং setResult()
এ অনুমতি দিন। যখন আপনি এইমাত্র সংজ্ঞায়িত Activity
শেষ করেন, তখন সিস্টেমটি ক্লায়েন্ট অ্যাপে বিষয়বস্তু URI সহ Intent
পাঠায়। নিম্নলিখিত কোড স্নিপেট আপনাকে দেখায় কিভাবে এটি করতে হয়:
কোটলিন
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> ... if (fileUri != null) { ... // Put the Uri and MIME type in the result Intent resultIntent.setDataAndType(fileUri, contentResolver.getType(fileUri)) // Set the result setResult(Activity.RESULT_OK, resultIntent) } else { resultIntent.setDataAndType(null, "") setResult(RESULT_CANCELED, resultIntent) } } }
জাভা
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) { ... if (fileUri != null) { ... // Put the Uri and MIME type in the result Intent resultIntent.setDataAndType( fileUri, getContentResolver().getType(fileUri)); // Set the result MainActivity.this.setResult(Activity.RESULT_OK, resultIntent); } else { resultIntent.setDataAndType(null, ""); MainActivity.this.setResult(RESULT_CANCELED, resultIntent); } } });
ব্যবহারকারীরা একটি ফাইল বেছে নেওয়ার পরে অবিলম্বে ক্লায়েন্ট অ্যাপে ফিরে যাওয়ার একটি উপায় প্রদান করুন৷ এটি করার একটি উপায় হল একটি চেকমার্ক বা সম্পন্ন বোতাম প্রদান করা। বোতামের android:onClick
বৈশিষ্ট্য ব্যবহার করে বোতামের সাথে একটি পদ্ধতি সংযুক্ত করুন। পদ্ধতিতে, কল finish()
। যেমন:
কোটলিন
fun onDoneClick(v: View) { // Associate a method with the Done button finish() }
জাভা
public void onDoneClick(View v) { // Associate a method with the Done button finish(); }
অতিরিক্ত সম্পর্কিত তথ্যের জন্য, পড়ুন:
- কন্টেন্ট ইউআরআই ডিজাইন করা
- বিষয়বস্তু প্রদানকারীর অনুমতি বাস্তবায়ন করা
- অনুমতি
- ইন্টেন্টস এবং ইনটেন্ট ফিল্টার