কম্পিউটার টিউটোরিয়াল

Android-এ SD কার্ডে ফোল্ডার/ফাইলে লিখার অনুমতি সেট করার পদ্ধতি

Android-এ এক্সটার্নাল স্টোরেজ (SD কার্ড) পড়া/লিখার অনুমতি এবং বাস্তবায়ন

এই উদাহরণটি দেখায় কিভাবে Android-এ এক্সটার্নাল স্টোরেজ (SD কার্ড) তে ফাইল তৈরি, লিখা এবং পড়া যায়। Android 10 (API 29) থেকে Scoped Storage চালু হওয়ায় এক্সটার্নাল স্টোরেজ এক্সেসের নিয়ম উল্লেখযোগ্যভাবে পরিবর্তন হয়েছে।

📌 গুরুত্বপূর্ণ: Android 10+ (API 29+) এ WRITE_EXTERNAL_STORAGE পারমিশন থাকলেও সাধারণ ফোল্ডার অ্যাক্সেস সীমাবদ্ধ। অ্যাপ-স্পেসিফিক ডিরেক্টরি (getExternalFilesDir()) ব্যবহার করুন যা কোনো পারমিশন ছাড়াই অ্যাক্সেসযোগ্য। পাবলিক ফোল্ডার (Downloads, Documents ইত্যাদি) অ্যাক্সেসের জন্য MediaStore বা Storage Access Framework (ACTION_OPEN_DOCUMENT_TREE) ব্যবহার করুন।

প্রয়োজনীয় ধাপসমূহ

ধাপ ১: নতুন প্রজেক্ট তৈরি করুন

Android Studio-তে File → New Project এ যান এবং প্রজেক্ট তৈরি করুন।

ধাপ ২: লেআউট ফাইল (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="12dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="এক্সটার্নাল স্টোরেজে পড়া এবং লিখা"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textAlignment="center" />

    <EditText
        android:id="@+id/myInputText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:ems="10"
        android:gravity="top|start"
        android:lines="5"
        android:minLines="3"
        android:hint="এখানে টেক্সট লিখুন..." />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:orientation="horizontal"
        android:weightSum="1.0">

        <Button
            android:id="@+id/saveExternalStorage"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="0.5"
            android:text="সংরক্ষণ" />

        <Button
            android:id="@+id/getExternalStorage"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="0.5"
            android:text="পড়ুন" />

    </LinearLayout>

    <TextView
        android:id="@+id/response"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:padding="5dp"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceMedium" />

</LinearLayout>

ধাপ ৩: MainActivity.java বাস্তবায়ন করুন

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class MainActivity extends AppCompatActivity {

    EditText editText;
    TextView textView;
    Button saveButton, readButton;
    File myExternalFile;
    String myData = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = findViewById(R.id.myInputText);
        textView = findViewById(R.id.response);
        saveButton = findViewById(R.id.saveExternalStorage);
        readButton = findViewById(R.id.getExternalStorage);

        // সংরক্ষণ বাটন ক্লিক লিসেনার
        saveButton.setOnClickListener(v -> {
            try {
                FileOutputStream fos = new FileOutputStream(myExternalFile);
                fos.write(editText.getText().toString().getBytes());
                fos.close();
                editText.setText("");
                textView.setText("SampleFile.txt এক্সটার্নাল স্টোরেজে সংরক্ষিত হয়েছে...");
            } catch (IOException e) {
                e.printStackTrace();
                textView.setText("ত্রুটি: " + e.getMessage());
            }
        });

        // পড়ার বাটন ক্লিক লিসেনার
        readButton.setOnClickListener(v -> {
            try {
                FileInputStream fis = new FileInputStream(myExternalFile);
                DataInputStream in = new DataInputStream(fis);
                BufferedReader br = new BufferedReader(new InputStreamReader(in));
                String strLine;
                myData = "";
                while ((strLine = br.readLine()) != null) {
                    myData += strLine + "\n";
                }
                in.close();
                editText.setText(myData);
                textView.setText("SampleFile.txt ডেটা পড়া হয়েছে...");
            } catch (IOException e) {
                e.printStackTrace();
                textView.setText("ত্রুটি: " + e.getMessage());
            }
        });

        // স্টোরেজ অবস্থা চেক করুন
        if (!isExternalStorageAvailable() || isExternalStorageReadOnly()) {
            saveButton.setEnabled(false);
            textView.setText("এক্সটার্নাল স্টোরেজ উপলব্ধ নয় বা রিড-অনলি মোডে");
        } else {
            // অ্যাপ-স্পেসিফিক ডিরেক্টরি ব্যবহার করুন (Scoped Storage-র জন্য নিরাপদ)
            String fileName = "SampleFile.txt";
            String filePath = "MyFolder";
            myExternalFile = new File(getExternalFilesDir(filePath), fileName);
        }
    }

    // এক্সটার্নাল স্টোরেজ রিড-অনলি কিনা চেক করুন
    private static boolean isExternalStorageReadOnly() {
        String extStorageState = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState);
    }

    // এক্সটার্নাল স্টোরেজ উপলব্ধ কিনা চেক করুন
    private static boolean isExternalStorageAvailable() {
        String extStorageState = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(extStorageState);
    }
}

ধাপ ৪: AndroidManifest.xml-এ পারমিশন যোগ করুন

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">

    <!-- Android 9 (API 28) এবং তার নিচের জন্য -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />

    <!-- Android 10+ (API 29+) জন্যScoped Storage-এ অ্যাপ-স্পেসিফিক ডিরেক্টরির জন্য কোনো পারমিশন লাগে না -->

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:requestLegacyExternalStorage="true"> <!-- Android 10-এ লিগ্যাসি স্টোরেজ সক্ষম করতে (অস্থায়ী) -->

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

Android ভার্সন অনুযায়ী স্টোরেজ নীতি

Android ভার্সন API লেভেল স্টোরেজ মডেল পারমিশন প্রয়োজন
Android 9 এবং নিচে <= 28 Legacy Storage WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE
Android 10 (Q) 29 Scoped Storage (ঐচ্ছিক) অ্যাপ-স্পেসিফিকের জন্য কোনো পারমিশন নয়; লিগ্যাসি 위해 android:requestLegacyExternalStorage="true"
Android 11 (R) + >= 30 Scoped Storage (বাধ্যতামূলক) অ্যাপ-স্পেসিফিকের জন্য কোনো পারমিশন নয়; পাবলিক ফাইল জন্য MediaStore / SAF

আধুনিক সেরা অনুশীলন (Best Practices)

১. অ্যাপ-স্পেসিফিক ফাইল (কোনো পারমিশন লাগে না)

// ফাইল লিখা
File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "myfile.txt");

// ফাইল পড়া
File file = new File(getExternalFilesDir(null), "myfile.txt");

২. MediaStore API (পাবলিক মিডিয়া ফাইল - ছবি, ভিডিও, অডিও)

ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "image.jpg");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyApp");

Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream os = getContentResolver().openOutputStream(uri)) {
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
}

৩. Storage Access Framework (ডকুমেন্ট/অন্যান্য ফাইল)

// ফাইল তৈরি করার জন্য
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TITLE, "notes.txt");
startActivityForResult(intent, CREATE_FILE_REQUEST);

অ্যাপ্লিকেশন চালানোর পদ্ধতি

আপনার Android ডিভাইস কম্পিউটারের সাথে সংযুক্ত করুন। Android Studio-তে Run আইকনে ক্লিক করুন এবং ডিভাইসে আউটপুট দেখুন।

Android-এ SD কার্ডে ফোল্ডার/ফাইলে লিখার অনুমতি সেট করার পদ্ধতি

Android-এ SD কার্ডে ফোল্ডার/ফাইলে লিখার অনুমতি সেট করার পদ্ধতি

  1. অ্যান্ড্রয়েডে TextWatcher ক্লাস ব্যবহারের পদ্ধতি

  2. অ্যান্ড্রয়েডে প্রোগ্রাম্যাটিক্যালি ফোন নম্বর পাওয়ার পদ্ধতি

  3. অ্যান্ড্রয়েডে Assets ফোল্ডারে ফাইল লেখার পদ্ধতি

  4. অ্যান্ড্রয়েডে TextView-এ স্প্যান ব্যবহার করে টেক্সটের রঙ পরিবর্তন করার পদ্ধতি