অ্যাক্টিভিটি এম্বেডিং

অ্যাক্টিভিটি এম্বেডিং, দুটি অ্যাক্টিভিটি বা একই অ্যাক্টিভিটির দুটি ইনস্ট্যান্সের মধ্যে কোনও অ্যাপ্লিকেশনের টাস্ক উইন্ডোকে ভাগ করে বড় স্ক্রিন ডিভাইসে অ্যাপকে অপ্টিমাইজ করে।

ছবি ১. পাশাপাশি অ্যাক্টিভিটি সহ সেটিংস অ্যাপ।

আপনার অ্যাপে একাধিক অ্যাক্টিভিটি থাকলে, অ্যাক্টিভিটি এম্বেডিং আপনাকে ট্যাবলেট, ফোল্ডেবল ও ChromeOS ডিভাইসে উন্নত ব্যবহারকারীর অভিজ্ঞতা প্রদান করতে সাহায্য করে।

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

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

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

Android 12L (API লেভেল 32) ও এর পরবর্তী যেকোনও ভার্সনে চলে এমন বেশিরভাগ বড় স্ক্রিনের ডিভাইসে অ্যাক্টিভিটি এম্বেডিং কাজ করে।

টাস্ক উইন্ডো স্প্লিট করা

অ্যাক্টিভিটি এম্বেডিং অ্যাপ টাস্ক উইন্ডোকে দুটি কন্টেনারে ভাগ করে: প্রাথমিক ও সেকেন্ডারি। কন্টেনারগুলি মূল অ্যাক্টিভিটি থেকে বা কন্টেনারে আগে থেকেই থাকা অন্যান্য অ্যাক্টিভিটি থেকে লঞ্চ করা অ্যাক্টিভিটি ধরে রাখে।

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

অ্যাক্টিভিটি এম্বেডিং আপনাকে বিভিন্ন উপায়ে অ্যাক্টিভিটি দেখাতে সাহায্য করে। আপনার অ্যাপ, দুটি অ্যাক্টিভিটি একই সাথে পাশাপাশি বা একটির উপরে আরেকটি লঞ্চ করে টাস্ক উইন্ডো স্প্লিট করতে পারে:

ছবি ২. দুটি অ্যাক্টিভিটি পাশাপাশি এবং একটির উপরে আরেকটি।

সম্পূর্ণ টাস্ক উইন্ডো জুড়ে থাকা কোনও অ্যাক্টিভিটি, পাশাপাশি নতুন অ্যাক্টিভিটি লঞ্চ করে স্প্লিট তৈরি করতে পারে:

ছবি ৩. অ্যাক্টিভিটি A, অ্যাক্টিভিটি B-কে পাশে শুরু করে।

আগে থেকেই স্প্লিট করা এবং টাস্ক উইন্ডো শেয়ার করা অ্যাক্টিভিটিগুলি নিম্নলিখিত উপায়ে অন্যান্য অ্যাক্টিভিটি লঞ্চ করতে পারে:

  • অন্য অ্যাক্টিভিটির উপরে পাশে:

    ছবি ৪. অ্যাক্টিভিটি A, অ্যাক্টিভিটি B-এর উপরে অ্যাক্টিভিটি C-কে সাইডে শুরু করে।
  • একদিকে সরিয়ে এবং স্প্লিটকে পাশাপাশি শিফট করে, আগের প্রাথমিক অ্যাক্টিভিটি আড়াল করে:

    ছবি ৫. অ্যাক্টিভিটি B, অ্যাক্টিভিটি C-কে পাশে শুরু করে এবং স্প্লিটকে পাশে সরায়।
  • সবচেয়ে উপরে থাকা অ্যাক্টিভিটির জায়গায় কোনও অ্যাক্টিভিটি লঞ্চ করুন; অর্থাৎ, একই অ্যাক্টিভিটি স্ট্যাকে:

    ছবি ৬. অ্যাক্টিভিটি 'খ' কোনও অতিরিক্ত ইনটেন্ট ফ্ল্যাগ ছাড়াই অ্যাক্টিভিটি 'গ' শুরু করে।
  • একই টাস্কে একটি অ্যাক্টিভিটি ফুল উইন্ডো লঞ্চ করুন:

    ছবি ৭. অ্যাক্টিভিটি A বা অ্যাক্টিভিটি B, অ্যাক্টিভিটি C শুরু করে যা টাস্ক উইন্ডো পূর্ণ করে।

ব্যাক নেভিগেশন

অ্যাক্টিভিটিগুলির মধ্যে পারস্পরিক নির্ভরতা বা কীভাবে ব্যবহারকারীরা ব্যাক ইভেন্ট ট্রিগার করেন তার উপর নির্ভর করে স্প্লিট টাস্ক উইন্ডো স্টেটে বিভিন্ন ধরনের অ্যাপ্লিকেশনের ব্যাক নেভিগেশন সংক্রান্ত আলাদা আলাদা নিয়ম থাকতে পারে, যেমন:

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

বোতাম নেভিগেশন ব্যবহার করার সময়, শেষ ফোকাস করা অ্যাক্টিভিটিতে ব্যাক ইভেন্ট পাঠানো হয়।

জেসচার-ভিত্তিক নেভিগেশনের জন্য:

  • Android 14 (API লেভেল 34) ও এর আগের যেকোনও ভার্সন — ব্যাক ইভেন্টটি সেই অ্যাক্টিভিটিতে পাঠানো হয় যেখানে জেসচারটি করা হয়েছে। ব্যবহারকারী স্ক্রিনের বাঁদিক থেকে সোয়াইপ করলে, স্প্লিট উইন্ডোর বাঁদিকের প্যানেলে থাকা অ্যাক্টিভিটিতে 'ব্যাক' ইভেন্ট পাঠানো হয়। ব্যবহারকারী স্ক্রিনের ডানদিক থেকে সোয়াইপ করলে, ডানদিকের প্যানেলে অ্যাক্টিভিটিতে 'ফিরে যান' ইভেন্ট পাঠানো হয়।

  • Android 15 (API লেভেল 35) ও তার পরের যেকোনও ভার্সন

    • একই অ্যাপ থেকে একাধিক অ্যাক্টিভিটি ডিল করার সময়, সোয়াইপের দিকনির্দেশ নির্বিশেষে জেসচার সবচেয়ে উপরের অ্যাক্টিভিটি সম্পূর্ণ করে, যা আরও বেশি ইউনিফায়েড অভিজ্ঞতা প্রদান করে।

    • আলাদা আলাদা অ্যাপ থেকে দুটি অ্যাক্টিভিটি (ওভারলে) জড়িত আছে এমন পরিস্থিতিতে, ব্যাক ইভেন্টটি ফোকাসে থাকা শেষ অ্যাক্টিভিটিতে ডাইরেক্ট করা হয়, যা বোতাম নেভিগেশনের বিহেভিয়ারের সাথে সামঞ্জস্যপূর্ণ।

মাল্টি-প্যানেল লেআউট

Jetpack WindowManager আপনাকে Android 12L (API লেভেল 32) বা তার পরের যেকোনও ভার্সন সহ বড় স্ক্রিনের ডিভাইসে এবং আগের প্ল্যাটফর্ম ভার্সন সহ কিছু ডিভাইসে অ্যাক্টিভিটি এম্বেডিং মাল্টি-প্যানেল লেআউট তৈরি করতে সাহায্য করে। ফ্র্যাগমেন্ট বা ভিউ-ভিত্তিক লেআউটের পরিবর্তে একাধিক অ্যাক্টিভিটির উপর ভিত্তি করে তৈরি বর্তমান অ্যাপ, যেমন SlidingPaneLayout সোর্স কোড রিফ্যাক্টর না করেই উন্নত বড় স্ক্রিন ব্যবহারকারীর অভিজ্ঞতা প্রদান করতে পারে।

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

ছবি ৮. মাল্টি-প্যানে লেআউটে দুটি অ্যাক্টিভিটি একসাথে শুরু হয়েছে।

অ্যাট্রিবিউট ভাগ করা

আপনি স্প্লিট কন্টেনারগুলির মধ্যে টাস্ক উইন্ডো কীভাবে আনুপাতিকভাবে ভাগ করা হয় এবং কন্টেনারগুলি কীভাবে একে অপরের সাথে সম্পর্কিত তা নির্দিষ্ট করতে পারেন।

XML কনফিগারেশন ফাইলে সংজ্ঞায়িত নিয়মের জন্য, নিম্নলিখিত অ্যাট্রিবিউট সেট করুন:

  • splitRatio: কন্টেনারের অনুপাত সেট করে। মানটি হল একটি ফ্লোটিং পয়েন্ট ওপেন ইন্টারভ্যালে (০.০, ১.০) থাকা সংখ্যা।
  • splitLayoutDirection: স্প্লিট কন্টেনারগুলি কীভাবে একে অপরের তুলনায় লেআউট করা হয় তা নির্দিষ্ট করে। ভ্যালুর মধ্যে এগুলি পড়ে:
    • ltr: বাঁদিক থেকে ডানদিকে
    • rtl: ডানদিক থেকে বাঁদিকে
    • locale: লোকেল সেটিং থেকে ltr বা rtl নির্ধারণ করা হয়

উদাহরণ দেখতে XML কনফিগারেশন বিভাগ দেখুন।

WindowManager API ব্যবহার করে তৈরি করা নিয়মের জন্য, SplitAttributes.Builder সহ একটি SplitAttributes অবজেক্ট তৈরি করুন এবং নিম্নলিখিত বিল্ডার মেথড কল করুন:

  • setSplitType(): স্প্লিট কন্টেনারের অনুপাত সেট করে। বৈধ আর্গুমেন্ট, যার মধ্যে SplitAttributes.SplitType.ratio() পদ্ধতিও পড়ে, সেই সম্পর্কে জানতে SplitAttributes.SplitType দেখুন।
  • setLayoutDirection(): কন্টেনারের লেআউট সেট করে। সম্ভাব্য ভ্যালু জানতে SplitAttributes.LayoutDirection দেখুন।

উদাহরণ দেখতে WindowManager API বিভাগ দেখুন।

ছবি ৯. দুটি অ্যাক্টিভিটি স্প্লিট বাঁদিক থেকে ডানদিকে লেআউট করা হয়েছে, তবে স্প্লিট রেশিও আলাদা।

স্প্লিট ওরিয়েন্টেশন

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

আপনি SplitController SplitAttributes ক্যালকুলেটরের সাহায্যে স্প্লিট করার ওরিয়েন্টেশন নির্দিষ্ট করতে পারবেন। অ্যাক্টিভ SplitRule-এর জন্য ক্যালকুলেটর SplitAttributes গণনা করে।

বিভিন্ন ডিভাইস স্টেটের জন্য বিভিন্ন দিকে প্যারেন্ট কন্টেনার স্প্লিট করতে ক্যালকুলেটর ব্যবহার করুন, যেমন:

Kotlin

if (WindowSdkExtensions.getInstance().extensionVersion >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator { params ->
        val parentConfiguration = params.parentConfiguration
        val builder = SplitAttributes.Builder()
        return@setSplitAttributesCalculator if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build()
        } else if (parentConfiguration.screenHeightDp >= 600) {
            // Horizontal split for tall displays.
            builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP)
                .build()
        } else {
            // Fallback to expand the secondary container.
            builder
                .setSplitType(SPLIT_TYPE_EXPAND)
                .build()
        }
    }
}

জাভা

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator(params -> {
        Configuration parentConfiguration = params.getParentConfiguration();
        SplitAttributes.Builder builder = new SplitAttributes.Builder();
        if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            return builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build();
        } else if (parentConfiguration.screenHeightDp >= 600) {
            // Horizontal split for tall displays.
            return builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP)
                .build();
        } else {
            // Fallback to expand the secondary container.
            return builder
                .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
                .build();
        }
    });
}

ফোল্ড করা যায় এমন ডিভাইসে, ডিভাইসটি ল্যান্ডস্কেপ মোডে থাকলে আপনি স্ক্রিনটি উল্লম্বভাবে স্প্লিট করতে পারবেন, ডিভাইসটি পোর্ট্রেট মোডে থাকলে একটি অ্যাক্টিভিটি ডিসপ্লে করতে পারবেন এবং ডিভাইসটি টেবিলটপ পোজে থাকলে স্ক্রিনটি অনুভূমিকভাবে স্প্লিট করতে পারবেন:

Kotlin

if (WindowSdkExtensions.getInstance().extensionVersion >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator { params ->
        val tag = params.splitRuleTag
        val parentWindowMetrics = params.parentWindowMetrics
        val parentConfiguration = params.parentConfiguration
        val foldingFeatures =
            params.parentWindowLayoutInfo.displayFeatures.filterIsInstance<FoldingFeature>()
        val feature = if (foldingFeatures.size == 1) foldingFeatures[0] else null
        val builder = SplitAttributes.Builder()
        builder.setSplitType(SPLIT_TYPE_HINGE)
        return@setSplitAttributesCalculator if (feature?.isSeparating == true) {
            // Horizontal split for tabletop posture.
            builder
                .setSplitType(SPLIT_TYPE_HINGE)
                .setLayoutDirection(
                    if (feature.orientation == FoldingFeature.Orientation.HORIZONTAL) {
                        SplitAttributes.LayoutDirection.BOTTOM_TO_TOP
                    } else {
                        SplitAttributes.LayoutDirection.LOCALE
                    }
                )
                .build()
        } else if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build()
        } else {
            // No split for tall displays.
            builder
                .setSplitType(SPLIT_TYPE_EXPAND)
                .build()
        }
    }
}

জাভা

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator(params -> {
        String tag = params.getSplitRuleTag();
        WindowMetrics parentWindowMetrics = params.getParentWindowMetrics();
        Configuration parentConfiguration = params.getParentConfiguration();
        List<FoldingFeature> foldingFeatures =
            params.getParentWindowLayoutInfo().getDisplayFeatures().stream().filter(
                    item -> item instanceof FoldingFeature)
                .map(item -> (FoldingFeature) item)
                .collect(Collectors.toList());
        FoldingFeature feature = foldingFeatures.size() == 1 ? foldingFeatures.get(0) : null;
        SplitAttributes.Builder builder = new SplitAttributes.Builder();
        builder.setSplitType(SplitType.SPLIT_TYPE_HINGE);
        if (feature != null && feature.isSeparating()) {
            // Horizontal slit for tabletop posture.
            return builder
                .setSplitType(SplitType.SPLIT_TYPE_HINGE)
                .setLayoutDirection(
                    feature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL
                        ? SplitAttributes.LayoutDirection.BOTTOM_TO_TOP
                        : SplitAttributes.LayoutDirection.LOCALE)
                .build();
        }
        else if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            return builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build();
        } else {
            // No split for tall displays.
            return builder
                .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
                .build();
        }
    });
}

প্লেসহোল্ডার

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

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

ছবি ১০. ফোল্ড করা যায় এমন ডিভাইস ফোল্ড ও আনফোল্ড করা হচ্ছে। প্লেসহোল্ডার অ্যাক্টিভিটি শেষ হয়ে গেছে এবং ডিসপ্লে সাইজ পরিবর্তন হওয়ার সাথে সাথে আবার তৈরি করা হয়েছে।

তবে, SplitPlaceholder.Builder-এর SplitPlaceholderRule বা setSticky() পদ্ধতির stickyPlaceholder অ্যাট্রিবিউট ডিফল্ট আচরণকে ওভাররাইড করতে পারে। অ্যাট্রিবিউট বা মেথড true ভ্যালু নির্দিষ্ট করলে, ডিসপ্লেকে দুটি প্যানেল থেকে একটি প্যানেল ডিসপ্লেতে রিসাইজ করা হলে, সিস্টেম টাস্ক উইন্ডোতে প্লেসহোল্ডারকে সবচেয়ে উপরের অ্যাক্টিভিটি হিসেবে দেখায় (উদাহরণ হিসেবে স্প্লিট কনফিগারেশন দেখুন)।

ছবি ১১. ফোল্ড করা যায় এমন ডিভাইস ফোল্ড ও আনফোল্ড করা হচ্ছে। Placeholder অ্যাক্টিভিটি স্টিকি।

উইন্ডোর সাইজ পরিবর্তন

ডিভাইস কনফিগারেশনে পরিবর্তন হলে, টাস্ক উইন্ডোর প্রস্থ কমে যায়, ফলে মাল্টি-পেন লেআউটের জন্য সেটি যথেষ্ট বড় থাকে না (যেমন, বড় স্ক্রিনের ফোল্ড করা যায় এমন ডিভাইস ট্যাবলেট সাইজ থেকে ফোন সাইজে ফোল্ড করা হলে অথবা মাল্টি-উইন্ডো মোডে অ্যাপ উইন্ডোর সাইজ পরিবর্তন করা হলে), টাস্ক উইন্ডোর সেকেন্ডারি প্যানে থাকা প্লেসহোল্ডার নয় এমন অ্যাক্টিভিটি প্রাইমারি প্যানে থাকা অ্যাক্টিভিটির উপরে স্ট্যাক করা হয়।

প্লেসহোল্ডার অ্যাক্টিভিটি তখনই দেখানো হয় যখন স্প্লিটের জন্য যথেষ্ট ডিসপ্লে উইডথ থাকে। ছোট স্ক্রিনে, প্লেসহোল্ডার অটোমেটিক বাতিল হয়ে যায়। ডিসপ্লে এরিয়া আবার যথেষ্ট বড় হয়ে গেলে, প্লেসহোল্ডারটি আবার তৈরি করা হয়। (প্লেসহোল্ডার বিভাগ দেখুন।)

অ্যাক্টিভিটি স্ট্যাকিং সম্ভব কারণ WindowManager সেকেন্ডারি প্যানেলে অ্যাক্টিভিটিগুলিকে প্রাইমারি প্যানেলে থাকা অ্যাক্টিভিটিগুলির উপরে z-অর্ডার করে।

সেকেন্ডারি প্যানেলে একাধিক অ্যাক্টিভিটি

অ্যাক্টিভিটি B, অ্যাক্টিভিটি C-কে কোনও অতিরিক্ত ইনটেন্ট ফ্ল্যাগ ছাড়াই চালু করে:

অ্যাক্টিভিটি স্প্লিট, যার মধ্যে A, B ও C অ্যাক্টিভিটি রয়েছে এবং C, B-এর
          উপরে স্ট্যাক করা আছে।

এর ফলে একই টাস্কে অ্যাক্টিভিটির নিম্নলিখিত z-অর্ডার তৈরি হয়:

সেকেন্ডারি অ্যাক্টিভিটি স্ট্যাক, যেখানে B-এর উপরে C অ্যাক্টিভিটি স্ট্যাক করা আছে।
          সেকেন্ডারি স্ট্যাক, অ্যাক্টিভিটি A থাকা প্রাইমারি অ্যাক্টিভিটি স্ট্যাকের উপরে স্ট্যাক করা হয়।

তাই, ছোট টাস্ক উইন্ডোতে, অ্যাপ্লিকেশনটি একটি অ্যাক্টিভিটিতে ছোট হয়ে যায় এবং স্ট্যাকের উপরে C থাকে:

ছোট উইন্ডোতে শুধুমাত্র অ্যাক্টিভিটি 'গ' দেখানো হচ্ছে।

ছোট উইন্ডোতে ফিরে নেভিগেট করলে, একে অপরের উপরে স্ট্যাক করা অ্যাক্টিভিটিগুলির মাধ্যমে নেভিগেট করা হয়।

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

স্ট্যাক করা স্প্লিট

অ্যাক্টিভিটি B, অ্যাক্টিভিটি C-কে পাশে শুরু করে এবং স্প্লিটকে পাশে সরায়:

টাস্ক উইন্ডোতে অ্যাক্টিভিটি 'ক' ও 'খ' দেখানো হচ্ছে, তারপর অ্যাক্টিভিটি 'খ' ও 'গ' দেখানো হচ্ছে।

একই টাস্কে অ্যাক্টিভিটির নিম্নলিখিত z-অর্ডার হল ফলাফল:

একটি স্ট্যাকে অ্যাক্টিভিটি A, B ও C। অ্যাক্টিভিটিগুলি
          নিচে থেকে উপরে এই ক্রমে সাজানো হয়েছে: C, B, A.

ছোট টাস্ক উইন্ডোতে, অ্যাপ্লিকেশনটি C on top সহ একটি অ্যাক্টিভিটিতে ছোট হয়ে যায়:

ছোট উইন্ডোতে শুধুমাত্র অ্যাক্টিভিটি 'গ' দেখানো হচ্ছে।

ফিক্সড-পোর্ট্রেট ওরিয়েন্টেশন

android:screenOrientation ম্যানিফেস্ট সেটিং, অ্যাপকে পোর্ট্রেট বা ল্যান্ডস্কেপ ওরিয়েন্টেশনে অ্যাক্টিভিটি সীমাবদ্ধ করতে দেয়। ট্যাবলেট ও ফোল্ড করা যায় এমন ডিভাইসের মতো বড় স্ক্রিনযুক্ত ডিভাইসে ব্যবহারকারীর অভিজ্ঞতা উন্নত করতে, ডিভাইস প্রস্তুতকারী (OEM) স্ক্রিন ওরিয়েন্টেশন সংক্রান্ত অনুরোধ উপেক্ষা করতে এবং ল্যান্ডস্কেপ ডিসপ্লেতে পোর্ট্রেট ওরিয়েন্টেশন বা পোর্ট্রেট ডিসপ্লেতে ল্যান্ডস্কেপ ওরিয়েন্টেশনে অ্যাপ লেটারবক্স করতে পারে।

ছবি ১২. লেটারবক্সড অ্যাক্টিভিটি: ল্যান্ডস্কেপ ডিভাইসে ফিক্সড-পোর্ট্রেট (বাঁদিকে), পোর্ট্রেট ডিভাইসে ফিক্সড-ল্যান্ডস্কেপ (ডানদিকে)।

একইভাবে, অ্যাক্টিভিটি এম্বেডিং চালু করা থাকলে, OEM-রা ডিভাইস কাস্টমাইজ করে বড় স্ক্রিনে ল্যান্ডস্কেপ ওরিয়েন্টেশনে ফিক্সড-পোর্ট্রেট অ্যাক্টিভিটি লেটারবক্স করতে পারে (প্রস্থ ≥ ৬০০dp)। ফিক্সড-পোর্ট্রেট অ্যাক্টিভিটি দ্বিতীয় অ্যাক্টিভিটি লঞ্চ করলে, ডিভাইসটি দু'টি অ্যাক্টিভিটি পাশাপাশি টু-পেন ডিসপ্লেতে দেখাতে পারে।

ছবি ১৩. ফিক্সড-পোর্ট্রেট অ্যাক্টিভিটি A, অ্যাক্টিভিটি B-কে পাশে শুরু করে।

আপনার অ্যাপ যে অ্যাক্টিভিটি এম্বেডিং (স্প্লিট কনফিগারেশন বিভাগ দেখুন) সাপোর্ট করে তা ডিভাইসকে জানাতে, আপনার অ্যাপ ম্যানিফেস্ট ফাইলে সবসময় android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED প্রপার্টি যোগ করুন। তারপরে OEM-কাস্টমাইজ করা ডিভাইস নির্ধারণ করতে পারে যে ফিক্সড-পোর্ট্রেট অ্যাক্টিভিটি লেটারবক্স করতে হবে কিনা।

স্প্লিট কনফিগারেশন

স্প্লিট নিয়ম অ্যাক্টিভিটি স্প্লিট কনফিগার করে। আপনি XML কনফিগারেশন ফাইলে বা Jetpack WindowManager API কল করার মাধ্যমে স্প্লিট করার নিয়ম ডিফাইন করেন।

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

নিম্নলিখিত কাজগুলি করুন:

  1. আপনার অ্যাপের মডিউল-লেভেলে build.gradle লেটেস্ট WindowManager লাইব্রেরি নির্ভরতা যোগ করুন, যেমন:

    implementation 'androidx.window:window:1.1.0-beta02'

    WindowManager লাইব্রেরি অ্যাক্টিভিটি এম্বেডিংয়ের জন্য প্রয়োজনীয় সব কম্পোনেন্ট প্রদান করে।

  2. সিস্টেমকে জানান যে আপনার অ্যাপ অ্যাক্টিভিটি এম্বেডিং প্রয়োগ করেছে।

    অ্যাপ ম্যানিফেস্ট ফাইলের <application> এলিমেন্টে android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED প্রপার্টি যোগ করুন এবং ভ্যালু true হিসেবে সেট করুন, যেমন:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        <application>
            <property
                android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
                android:value="true" />
        </application>
    </manifest>
    

    WindowManager রিলিজ 1.1.0-alpha06 ও এর পরের ভার্সনে, ম্যানিফেস্টে প্রপার্টি যোগ করে 'ট্রু' হিসেবে সেট না করা পর্যন্ত, অ্যাক্টিভিটি এম্বেডিং স্প্লিট বন্ধ করা থাকে।

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

XML কনফিগারেশন

অ্যাক্টিভিটি এম্বেডিংয়ের XML-ভিত্তিক প্রয়োগ তৈরি করতে, নিম্নলিখিত ধাপগুলি সম্পূর্ণ করুন:

  1. নিম্নলিখিত কাজগুলি করে এমন একটি XML রিসোর্স ফাইল তৈরি করুন:

    • স্প্লিট শেয়ার করা অ্যাক্টিভিটি নির্ধারণ করে
    • স্প্লিট করার বিকল্প কনফিগার করে
    • কন্টেন্ট উপলভ্য না থাকলে, স্প্লিটের সেকেন্ডারি কন্টেনারের জন্য প্লেসহোল্ডার তৈরি করে
    • স্প্লিটের অংশ হওয়া উচিত নয় এমন অ্যাক্টিভিটি নির্দিষ্ট করে

    যেমন:

    <!-- main_split_config.xml -->
    
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Define a split for the named activities. -->
        <SplitPairRule
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:finishPrimaryWithSecondary="never"
            window:finishSecondaryWithPrimary="always"
            window:clearTop="false">
            <SplitPairFilter
                window:primaryActivityName=".ListActivity"
                window:secondaryActivityName=".DetailActivity"/>
        </SplitPairRule>
    
        <!-- Specify a placeholder for the secondary container when content is
             not available. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".PlaceholderActivity"
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:stickyPlaceholder="false">
            <ActivityFilter
                window:activityName=".ListActivity"/>
        </SplitPlaceholderRule>
    
        <!-- Define activities that should never be part of a split. Note: Takes
             precedence over other split rules for the activity named in the
             rule. -->
        <ActivityRule
            window:alwaysExpand="true">
            <ActivityFilter
                window:activityName=".ExpandedActivity"/>
        </ActivityRule>
    
    </resources>
    
  2. ইনিশিয়ালাইজার তৈরি করুন।

    WindowManager RuleController কম্পোনেন্ট XML কনফিগারেশন ফাইল পার্স করে এবং সিস্টেমের জন্য নিয়ম উপলভ্য করে তোলে। Jetpack Startup লাইব্রেরি Initializer অ্যাপ চালু হওয়ার সময় XML ফাইলকে RuleController উপলভ্য করে তোলে, যাতে কোনও অ্যাক্টিভিটি শুরু হওয়ার সময় নিয়ম কার্যকর থাকে।

    ইনিশিয়ালাইজার তৈরি করতে, নিম্নলিখিত কাজগুলি করুন:

    1. আপনার মডিউল-লেভেল build.gradle ফাইলে লেটেস্ট Jetpack Startup লাইব্রেরি ডিপেন্ডেন্সি যোগ করুন, যেমন:

      implementation 'androidx.startup:startup-runtime:1.1.1'

    2. Initializer ইন্টারফেস প্রয়োগ করে এমন একটি ক্লাস তৈরি করুন।

      XML কনফিগারেশন ফাইলের (main_split_config.xml) ID, RuleController.parseRules() মেথডে পাস করার মাধ্যমে, ইনিশিয়ালাইজার RuleController-এর জন্য স্প্লিট করার নিয়ম উপলভ্য করে তোলে।

      Kotlin

      class SplitInitializer : Initializer<RuleController> {
      
          override fun create(context: Context): RuleController {
              return RuleController.getInstance(context).apply {
                  setRules(RuleController.parseRules(context, R.xml.main_split_config))
              }
          }
      
          override fun dependencies(): List<Class<out Initializer<*>>> {
              return emptyList()
          }
      }

      জাভা

      public class SplitInitializer implements Initializer<RuleController> {
      
          @NonNull
          @Override
          public RuleController create(@NonNull Context context) {
              RuleController ruleController = RuleController.getInstance(context);
              ruleController.setRules(
                  RuleController.parseRules(context, R.xml.main_split_config)
              );
               return ruleController;
           }
      
           @NonNull
           @Override
           public List<Class<? extends Initializer<?>>> dependencies() {
               return Collections.emptyList();
           }
      }

  3. নিয়মের সংজ্ঞা দেওয়ার জন্য কন্টেন্ট প্রদানকারী তৈরি করুন।

    আপনার অ্যাপ মেনিফেস্ট ফাইলে androidx.startup.InitializationProvider যোগ করুন <provider> হিসেবে। আপনার RuleController ইনিশিয়ালাইজার SplitInitializer প্রয়োগ করার রেফারেন্স যোগ করুন:

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- Make SplitInitializer discoverable by InitializationProvider. -->
        <meta-data android:name="${applicationId}.SplitInitializer"
            android:value="androidx.startup" />
    </provider>
    

    অ্যাপের onCreate() মেথড কল করার আগে InitializationProvider ডিসকভার ও ইনিশিয়ালাইজ SplitInitializer করে। এর ফলে, অ্যাপের মূল অ্যাক্টিভিটি শুরু হলে স্প্লিট নিয়ম কার্যকর হয়।

WindowManager API

আপনি অল্প কয়েকটি API কলের মাধ্যমে প্রোগ্র্যামাটিক উপায়ে অ্যাক্টিভিটি এম্বেডিং প্রয়োগ করতে পারবেন। যাতে কোনও অ্যাক্টিভিটি লঞ্চ করার আগে নিয়ম কার্যকর থাকে তা নিশ্চিত করতে, onCreate() Application-এর সাবক্লাসের মধ্যে কল করুন।

-এ রুল যোগ করুন।

প্রোগ্র্যামাটিক উপায়ে অ্যাক্টিভিটি স্প্লিট তৈরি করতে, নিম্নলিখিত কাজগুলি করুন:

  1. একটি স্প্লিট নিয়ম তৈরি করুন:

    1. একটি SplitPairFilter তৈরি করুন যা স্প্লিট শেয়ার করা অ্যাক্টিভিটি শনাক্ত করে:

      Kotlin

      val splitPairFilter = SplitPairFilter(
          ComponentName(this, ListActivity::class.java),
          ComponentName(this, DetailActivity::class.java),
          null
      )

      জাভা

      SplitPairFilter splitPairFilter = new SplitPairFilter(
         new ComponentName(this, ListActivity.class),
         new ComponentName(this, DetailActivity.class),
         null
      );

    2. ফিল্টার সেটে ফিল্টার যোগ করুন:

      Kotlin

      val filterSet = setOf(splitPairFilter)

      জাভা

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
      ```

    3. স্প্লিটের জন্য লেআউট অ্যাট্রিবিউট তৈরি করুন:

      Kotlin

      val splitAttributes: SplitAttributes = SplitAttributes.Builder()
          .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
          .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
          .build()

      জাভা

      SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();

      SplitAttributes.Builder লেআউট অ্যাট্রিবিউট সহ একটি অবজেক্ট তৈরি করে:

      • setSplitType(): উপলভ্য ডিসপ্লে এরিয়া কীভাবে প্রতিটি অ্যাক্টিভিটি কন্টেনারে অ্যাসাইন করা হয় তা নির্ধারণ করে। অনুপাত স্প্লিট করার ধরনটি উপলভ্য ডিসপ্লে এরিয়ার সেই অংশকে নির্দিষ্ট করে যা প্রাইমারি কন্টেনারে বরাদ্দ করা হয়েছে; সেকেন্ডারি কন্টেনারটি উপলভ্য ডিসপ্লে এরিয়ার অবশিষ্ট অংশ দখল করে।
      • setLayoutDirection(): অ্যাক্টিভিটি কন্টেনারগুলি একে অপরের সাথে কীভাবে সাজানো হয় তা নির্দিষ্ট করে, প্রথমে প্রাথমিক কন্টেনার।
    4. SplitPairRule তৈরি করুন:

      Kotlin

      val splitPairRule = SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build()

      জাভা

      SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build();

      SplitPairRule.Builder নিয়ম তৈরি ও কনফিগার করে:

      • filterSet: স্প্লিট পেয়ার ফিল্টার থাকে যা স্প্লিট শেয়ার করে এমন অ্যাক্টিভিটি শনাক্ত করে নিয়ম প্রয়োগ করার সময় নির্ধারণ করে।
      • setDefaultSplitAttributes(): নিয়মের উপর লেআউট অ্যাট্রিবিউট প্রয়োগ করে।
      • setMinWidthDp(): স্প্লিট করার সুবিধা চালু করতে, ন্যূনতম ডিসপ্লে প্রস্থ (ডেন্সিটি‑ইনডিপেন্ডেন্ট পিক্সেল, dp-এ) সেট করে।
      • setMinSmallestWidthDp(): ডিভাইসের ওরিয়েন্টেশন যাই হোক না কেন, স্প্লিট করার সুবিধা চালু করতে হলে, দুটি ডিসপ্লে ডাইমেনশনের মধ্যে ছোটটির ন্যূনতম ভ্যালু (ডিপি-তে) কত হতে হবে তা সেট করে।
      • setMaxAspectRatioInPortrait(): পোর্ট্রেট ওরিয়েন্টেশনে সর্বাধিক ডিসপ্লে অ্যাস্পেক্ট রেশিও (উচ্চতা:প্রস্থ) সেট করে যার জন্য অ্যাক্টিভিটি স্প্লিট দেখানো হয়। কোনও পোর্ট্রেট ডিসপ্লের অ্যাস্পেক্ট রেশিও সর্বাধিক অ্যাস্পেক্ট রেশিওর থেকে বেশি হলে, ডিসপ্লের প্রস্থ যাই হোক না কেন, স্প্লিট করার সুবিধা বন্ধ করে দেওয়া হয়। মনে রাখবেন: ডিফল্ট ভ্যালু হল ১.৪, যার ফলে বেশিরভাগ ট্যাবলেটে পোর্ট্রেট ওরিয়েন্টেশনে অ্যাক্টিভিটি পুরো টাস্ক উইন্ডো জুড়ে থাকে। এছাড়াও দেখুন SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT এবং setMaxAspectRatioInLandscape(). ল্যান্ডস্কেপের ডিফল্ট ভ্যালু হল ALWAYS_ALLOW।
      • setFinishPrimaryWithSecondary(): সেকেন্ডারি কন্টেনারে সব অ্যাক্টিভিটি সম্পূর্ণ করলে, প্রাইমারি কন্টেনারের অ্যাক্টিভিটি কীভাবে প্রভাবিত হয় তা সেট করে। NEVER থেকে বোঝা যায় যে সেকেন্ডারি কন্টেনারের সব অ্যাক্টিভিটি শেষ হয়ে গেলেও সিস্টেম যেন প্রাইমারি অ্যাক্টিভিটি শেষ না করে (অ্যাক্টিভিটি শেষ করা দেখুন)।
      • setFinishSecondaryWithPrimary(): প্রাইমারি কন্টেনারের সব অ্যাক্টিভিটি সম্পূর্ণ হলে তা সেকেন্ডারি কন্টেনারের অ্যাক্টিভিটিকে কীভাবে প্রভাবিত করে তা সেট করে। ALWAYS থেকে বোঝা যায় যে প্রাইমারি কন্টেনারের সব অ্যাক্টিভিটি সম্পূর্ণ হয়ে গেলে সিস্টেমকে সবসময় সেকেন্ডারি কন্টেনারের অ্যাক্টিভিটি সম্পূর্ণ করতে হবে (দেখুন অ্যাক্টিভিটি সম্পূর্ণ করা)।
      • setClearTop(): কন্টেনারে নতুন অ্যাক্টিভিটি লঞ্চ করার সময় সেকেন্ডারি কন্টেনারের সব অ্যাক্টিভিটি শেষ হয়ে গেছে কিনা তা নির্দিষ্ট করে। false ভ্যালু থেকে বোঝা যায় যে নতুন অ্যাক্টিভিটি সেকেন্ডারি কন্টেনারে আগে থেকেই থাকা অ্যাক্টিভিটির উপরে স্ট্যাক করা হয়।
    5. WindowManager RuleController-এর সিঙ্গলটন ইনস্ট্যান্স পান, এবং এই নিয়ম যোগ করুন:

      Kotlin

      val ruleController = RuleController.getInstance(this)
      ruleController.addRule(splitPairRule)

      জাভা

      RuleController ruleController = RuleController.getInstance(this);
      ruleController.addRule(splitPairRule);

    6. কন্টেন্ট উপলভ্য না থাকলে সেকেন্ডারি কন্টেনারের জন্য প্লেসহোল্ডার তৈরি করুন:

    7. এমন একটি ActivityFilter তৈরি করুন যা সেই অ্যাক্টিভিটি শনাক্ত করে যার সাথে প্লেসহোল্ডার একটি টাস্ক উইন্ডো স্প্লিট শেয়ার করে:

      Kotlin

      val placeholderActivityFilter = ActivityFilter(
          ComponentName(this, ListActivity::class.java),
          null
      )

      জাভা

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );

    8. ফিল্টার সেটে ফিল্টার যোগ করুন:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      জাভা

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);

    9. একটি SplitPlaceholderRule তৈরি করুন:

      Kotlin

      val splitPlaceholderRule = SplitPlaceholderRule.Builder(
          placeholderActivityFilterSet,
          Intent(context, PlaceholderActivity::class.java)
      ).setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
          .setSticky(false)
          .build()

      জাভা

      SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            new Intent(this, PlaceholderActivity.class)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build();

      SplitPlaceholderRule.Builder নিয়ম তৈরি ও কনফিগার করে:

      • placeholderActivityFilterSet: এতে অ্যাক্টিভিটি ফিল্টার থাকে যা প্লেসহোল্ডার অ্যাক্টিভিটি যুক্ত আছে এমন অ্যাক্টিভিটি শনাক্ত করে কখন নিয়ম প্রয়োগ করতে হবে তা নির্ধারণ করে।
      • Intent: প্লেসহোল্ডার অ্যাক্টিভিটি লঞ্চ করার কথা নির্দিষ্ট করে।
      • setDefaultSplitAttributes(): নিয়মের উপর লেআউট অ্যাট্রিবিউট প্রয়োগ করে।
      • setMinWidthDp(): স্প্লিট করার অনুমতি দেয় এমন ন্যূনতম ডিসপ্লে প্রস্থ (ঘনত্ব-নিরপেক্ষ পিক্সেল, dp) সেট করে।
      • setMinSmallestWidthDp(): দুটি ডিসপ্লে ডাইমেনশনের মধ্যে ছোটটির ন্যূনতম ভ্যালু (ডিপি-তে) সেট করে যা ডিভাইসের ওরিয়েন্টেশন নির্বিশেষে স্প্লিট করার অনুমতি দেওয়ার জন্য থাকতে হবে।
      • setMaxAspectRatioInPortrait(): পোর্ট্রেট ওরিয়েন্টেশনে অ্যাক্টিভিটি স্প্লিট দেখানোর জন্য সর্বাধিক ডিসপ্লে অ্যাস্পেক্ট রেশিও (উচ্চতা:প্রস্থ) সেট করে। মনে রাখবেন: ডিফল্ট ভ্যালু হল ১.৪, যার ফলে বেশিরভাগ ট্যাবলেটে পোর্ট্রেট ওরিয়েন্টেশনে অ্যাক্টিভিটি টাস্ক উইন্ডো পূরণ করে। এছাড়াও দেখুন SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT এবং setMaxAspectRatioInLandscape(). ল্যান্ডস্কেপের ডিফল্ট ভ্যালু হল ALWAYS_ALLOW।
      • setFinishPrimaryWithPlaceholder(): প্রাইমারি কন্টেনারে প্লেসহোল্ডার অ্যাক্টিভিটি শেষ করার ফলে অ্যাক্টিভিটি কীভাবে প্রভাবিত হয় তা সেট করে। ALWAYS নির্দেশ করে যে প্লেসহোল্ডার শেষ হয়ে গেলে সিস্টেমকে সবসময় প্রাইমারি কন্টেনারে অ্যাক্টিভিটি শেষ করতে হবে (অ্যাক্টিভিটি শেষ করা দেখুন)।
      • setSticky(): প্লেসহোল্ডার অ্যাক্টিভিটি প্রথমে যথেষ্ট ন্যূনতম প্রস্থ সহ স্প্লিটে দেখা গেলে, ছোট ডিসপ্লেতে অ্যাক্টিভিটি স্ট্যাকের উপরে প্লেসহোল্ডার অ্যাক্টিভিটি দেখা যাবে কিনা তা নির্ধারণ করে।
    10. WindowManager RuleController-এ নিয়ম যোগ করুন:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)

      জাভা

      ruleController.addRule(splitPlaceholderRule);

  2. স্প্লিট করা উচিত নয় এমন অ্যাক্টিভিটি উল্লেখ করুন:

    1. এমন একটি ActivityFilter তৈরি করুন যা এমন অ্যাক্টিভিটি শনাক্ত করে যা সবসময় সম্পূর্ণ টাস্ক ডিসপ্লে এরিয়া জুড়ে থাকবে:

      Kotlin

      val expandedActivityFilter = ActivityFilter(
          ComponentName(this, ExpandedActivity::class.java),
          null
      )

      জাভা

      ActivityFilter expandedActivityFilter = new ActivityFilter(
          new ComponentName(this, ExpandedActivity.class),
          null
      );

    2. ফিল্টার সেটে ফিল্টার যোগ করুন:

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      জাভা

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);

    3. ActivityRule তৈরি করুন:

      Kotlin

      val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
          .setAlwaysExpand(true)
          .build()

      জাভা

      ActivityRule activityRule = new ActivityRule.Builder(
          expandedActivityFilterSet
      ).setAlwaysExpand(true)
       .build();

      ActivityRule.Builder নিয়ম তৈরি ও কনফিগার করে:

      • expandedActivityFilterSet: এতে অ্যাক্টিভিটি ফিল্টার থাকে যা কখন নিয়ম প্রয়োগ করতে হবে তা নির্ধারণ করে। এটি সেইসব অ্যাক্টিভিটি শনাক্ত করে যেগুলি আপনি স্প্লিট থেকে বাদ দিতে চান।
      • setAlwaysExpand(): অ্যাক্টিভিটি সম্পূর্ণ টাস্ক উইন্ডো পূর্ণ করবে কিনা তা নির্দিষ্ট করে।
    4. WindowManager RuleController-এ নিয়ম যোগ করুন:

      Kotlin

      ruleController.addRule(activityRule)

      জাভা

      ruleController.addRule(activityRule);

ক্রস-অ্যাপ্লিকেশন এম্বেডিং

Android 13 (API লেভেল 33) ও এর পরের যেকোনও ভার্সনে, অ্যাপ অন্যান্য অ্যাপ থেকে অ্যাক্টিভিটি এম্বেড করতে পারে। ক্রস‑অ্যাপ্লিকেশন বা ক্রস‑UID, অ্যাক্টিভিটি এম্বেডিং একাধিক Android অ্যাপ্লিকেশন থেকে অ্যাক্টিভিটির ভিজ্যুয়াল ইন্টিগ্রেশন চালু করে। সিঙ্গেল-অ্যাপ অ্যাক্টিভিটি এম্বেডিংয়ের মতো, সিস্টেম স্ক্রিনে হোস্ট অ্যাপের অ্যাক্টিভিটি এবং অন্য অ্যাপ থেকে এম্বেড করা অ্যাক্টিভিটি পাশাপাশি বা উপরে ও নিচে দেখায়।

যেমন, Settings অ্যাপটি WallpaperPicker অ্যাপ থেকে ওয়ালপেপার বাছাইকারী অ্যাক্টিভিটি এম্বেড করতে পারে:

ছবি ১৪. এমবেড করা অ্যাক্টিভিটি (ডানদিকে) হিসেবে ওয়ালপেপার বাছাইকারী সহ সেটিংস অ্যাপ (বাঁদিকে মেনু)।

ট্রাস্ট মডেল

অন্যান্য অ্যাপ থেকে অ্যাক্টিভিটি এম্বেড করা হোস্ট প্রসেস, এম্বেড করা অ্যাক্টিভিটির প্রেজেন্টেশন নতুন করে সংজ্ঞায়িত করতে পারে, এর মধ্যে সাইজ, পজিশন, ক্রপিং ও ট্রান্সপারেন্সি অন্তর্ভুক্ত। ক্ষতিকারক হোস্ট এই ক্ষমতা ব্যবহার করে ব্যবহারকারীদের বিভ্রান্ত করতে এবং ক্লিকজ্যাকিং বা অন্যান্য UI-রিড্রেসিং অ্যাটাক তৈরি করতে পারে।

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

বিশ্বস্ত হোস্ট

আপনার অ্যাপ থেকে অ্যাক্টিভিটি এম্বেড ও সম্পূর্ণ কন্ট্রোল করার জন্য অন্যান্য অ্যাপ্লিকেশনকে অনুমতি দিতে, হোস্ট অ্যাপ্লিকেশনের SHA-256 সার্টিফিকেটটি আপনার অ্যাপের ম্যানিফেস্ট ফাইলের <activity> বা <application> এলিমেন্টের android:knownActivityEmbeddingCerts অ্যাট্রিবিউটে উল্লেখ করুন।

android:knownActivityEmbeddingCerts-এর ভ্যালু একটি স্ট্রিং হিসেবে সেট করুন:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
    ... />

অথবা, একাধিক সার্টিফিকেট নির্দিষ্ট করতে, স্ট্রিংয়ের একটি অ্যারে:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
    ... />

যা নিম্নলিখিত রিসোর্সের মতো কোনও রিসোর্সকে রেফার করে:

<resources>
    <string-array name="known_host_certificate_digests">
      <item>cert1</item>
      <item>cert2</item>
      ...
    </string-array>
</resources>

অ্যাপের মালিকরা Gradle signingReport টাস্ক রান করিয়ে SHA সার্টিফিকেট ডাইজেস্ট পেতে পারেন। সার্টিফিকেট ডাইজেস্ট হল SHA-256 ফিঙ্গারপ্রিন্ট, যাতে আলাদা করার জন্য কোলন থাকে না। আরও তথ্যের জন্য, সাইন-ইন রিপোর্ট রান করানো এবং আপনার ক্লায়েন্টকে যাচাই করা দেখুন।

অবিশ্বস্ত হোস্ট

আপনার অ্যাপের অ্যাক্টিভিটি এম্বেড করতে এবং সেগুলির প্রেজেন্টেশন কন্ট্রোল করতে, অ্যাপ ম্যানিফেস্টে <activity> বা <application> এলিমেন্টে android:allowUntrustedActivityEmbedding অ্যাট্রিবিউট উল্লেখ করুন, যেমন:

<activity
    android:name=".MyEmbeddableActivity"
    android:allowUntrustedActivityEmbedding="true"
    ... />

অ্যাট্রিবিউটের ডিফল্ট ভ্যালু হল false, যা ক্রস-অ্যাপ অ্যাক্টিভিটি এম্বেড করা আটকায়।

কাস্টম যাচাইকরণ

অবিশ্বস্ত অ্যাক্টিভিটি এম্বেড করার ঝুঁকি কমাতে, হোস্টের পরিচয় যাচাই করে এমন কাস্টম যাচাইকরণ মেকানিজম তৈরি করুন। আপনি যদি হোস্ট সার্টিফিকেট জানেন, তাহলে androidx.security.app.authenticator লাইব্রেরি ব্যবহার করে প্রমাণ করুন। হোস্ট আপনার অ্যাক্টিভিটি এম্বেড করার পরে প্রমাণীকরণ করলে, আপনি আসল কন্টেন্ট দেখাতে পারবেন। যদি না হয়, তাহলে আপনি ব্যবহারকারীকে জানাতে পারেন যে অ্যাকশনটি অনুমোদিত নয় এবং কন্টেন্টটি ব্লক করতে পারেন।

কোনও হোস্ট আপনার অ্যাক্টিভিটি এম্বেড করছে কিনা তা চেক করতে, Jetpack WindowManager লাইব্রেরি থেকে ActivityEmbeddingController#isActivityEmbedded() পদ্ধতি ব্যবহার করুন, যেমন:

Kotlin

fun isActivityEmbedded(activity: Activity): Boolean {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity)
}

জাভা

boolean isActivityEmbedded(Activity activity) {
    return ActivityEmbeddingController.getInstance(context).isActivityEmbedded(activity);
}

ন্যূনতম সাইজ সংক্রান্ত বিধিনিষেধ

Android সিস্টেম, এম্বেড করা অ্যাক্টিভিটিতে অ্যাপের মেনিফেস্ট <layout> এলিমেন্টে উল্লেখ করা ন্যূনতম উচ্চতা ও প্রস্থ প্রয়োগ করে। কোনও অ্যাপ্লিকেশন ন্যূনতম উচ্চতা ও প্রস্থ উল্লেখ না করলে, সিস্টেমের ডিফল্ট ভ্যালু প্রযোজ্য হয় (sw220dp)।

হোস্ট যদি এম্বেড করা কন্টেনারকে ন্যূনতম সাইজের চেয়ে ছোট সাইজে রিসাইজ করার চেষ্টা করে, তাহলে এম্বেড করা কন্টেনারটি সম্পূর্ণ টাস্ক বাউন্ড জুড়ে প্রসারিত হয়।

<activity-alias>

বিশ্বস্ত বা অবিশ্বস্ত অ্যাক্টিভিটি এম্বেড করার জন্য <activity-alias> এলিমেন্টের সাথে কাজ করতে, android:knownActivityEmbeddingCerts বা android:allowUntrustedActivityEmbedding অবশ্যই অ্যালিয়াসের পরিবর্তে টার্গেট অ্যাক্টিভিটিতে প্রয়োগ করতে হবে। সিস্টেম সার্ভারে নিরাপত্তা যাচাই করে এমন নীতি অ্যালিয়াসের পরিবর্তে টার্গেটে সেট করা ফ্ল্যাগের উপর ভিত্তি করে তৈরি হয়।

হোস্ট অ্যাপ্লিকেশন

হোস্ট অ্যাপ্লিকেশনগুলি ক্রস-অ্যাপ অ্যাক্টিভিটি এম্বেডিং ঠিক সেইভাবে প্রয়োগ করে, যেভাবে তারা সিঙ্গেল-অ্যাপ অ্যাক্টিভিটি এম্বেডিং প্রয়োগ করে। SplitPairRule এবং SplitPairFilter অথবা ActivityRule ও ActivityFilter অবজেক্ট এম্বেড করা অ্যাক্টিভিটি ও টাস্ক উইন্ডো স্প্লিট নির্দিষ্ট করে। স্প্লিট করার নিয়ম XML-এ স্ট্যাটিক অথবা Jetpack WindowManager API কল ব্যবহার করে রানটাইমে ডিফাইন করা হয়।

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

কোনও এম্বেড করা অ্যাক্টিভিটি একই টাস্কে নতুন অ্যাক্টিভিটি শুরু করলে এবং নতুন অ্যাক্টিভিটি ক্রস-অ্যাপ এম্বেডিংয়ে অপ্ট-ইন না করলে, এম্বেড করা কন্টেনারে অ্যাক্টিভিটি ওভারলে করার পরিবর্তে অ্যাক্টিভিটি পুরো টাস্ক বাউন্ড দখল করে নেয়।

হোস্ট অ্যাপ্লিকেশন কোনও বিধিনিষেধ ছাড়াই নিজস্ব অ্যাক্টিভিটি এম্বেড করতে পারে, তবে অ্যাক্টিভিটি একই টাস্কে লঞ্চ করতে হবে।

স্প্লিট করার উদাহরণ

ফুল উইন্ডো থেকে স্প্লিট করা

ছবি ১৫. অ্যাক্টিভিটি A, অ্যাক্টিভিটি B-কে পাশে শুরু করে।

কোনও রিফ্যাক্টরিংয়ের প্রয়োজন নেই। আপনি স্ট্যাটিক্যালি বা রানটাইমে স্প্লিটের কনফিগারেশন ডিফাইন করতে পারবেন এবং তারপরে কোনও অতিরিক্ত প্যারামিটার ছাড়াই Context#startActivity() কল করতে পারবেন।

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

ডিফল্ট হিসেবে স্প্লিট করা

কোনও অ্যাপ্লিকেশনের ল্যান্ডিং পৃষ্ঠা বড় স্ক্রিনে দুটি কন্টেনারে ভাগ করার জন্য ডিজাইন করা হলে, দুটি অ্যাক্টিভিটিই একসাথে তৈরি ও দেখানো হলে ব্যবহারকারীর অভিজ্ঞতা সবচেয়ে ভালো হয়। তবে, ব্যবহারকারী প্রাইমারি কন্টেনারে থাকা অ্যাক্টিভিটির সাথে ইন্টার‍্যাক্ট না করা পর্যন্ত স্প্লিটের সেকেন্ডারি কন্টেনারে কন্টেন্ট উপলভ্য নাও থাকতে পারে (যেমন, ব্যবহারকারী নেভিগেশন মেনু থেকে কোনও আইটেম বেছে নেন)। কন্টেন্ট স্প্লিটের সেকেন্ডারি কন্টেনারে দেখানো না পর্যন্ত প্লেসহোল্ডার অ্যাক্টিভিটি ফাঁকা জায়গা পূরণ করতে পারে (প্লেসহোল্ডার বিভাগ দেখুন)।

ছবি ১৬. দুটি অ্যাক্টিভিটি একসাথে খোলার ফলে স্প্লিট তৈরি হয়। একটি অ্যাক্টিভিটি হল প্লেসহোল্ডার।

প্লেসহোল্ডার সহ স্প্লিট তৈরি করতে, প্লেসহোল্ডার তৈরি করুন এবং এটিকে প্রাথমিক অ্যাক্টিভিটির সাথে যুক্ত করুন:

<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity">
    <ActivityFilter
        window:activityName=".MainActivity"/>
</SplitPlaceholderRule>

কোনও অ্যাপ ইনটেন্ট পেলে, টার্গেট অ্যাক্টিভিটি অ্যাক্টিভিটি স্প্লিটের সেকেন্ডারি পার্ট হিসেবে দেখানো যেতে পারে; যেমন, কোনও তালিকা থেকে কোনও আইটেমের তথ্য সহ একটি বিস্তারিত স্ক্রিন দেখানোর অনুরোধ। ছোট ডিসপ্লেতে, বিবরণ সম্পূর্ণ টাস্ক উইন্ডোতে দেখানো হয়; বড় ডিভাইসে, তালিকার পাশে দেখানো হয়।

ছবি ১৭. ডিপ লিঙ্ক সংক্রান্ত বিস্তারিত অ্যাক্টিভিটি ছোট স্ক্রিনে আলাদাভাবে দেখানো হয়, কিন্তু বড় স্ক্রিনে তালিকা অ্যাক্টিভিটির সাথে একসাথে দেখানো হয়।

লঞ্চ করার অনুরোধটি মূল অ্যাক্টিভিটিতে রুট করা উচিত এবং টার্গেট বিবরণ অ্যাক্টিভিটি স্প্লিটে লঞ্চ করা উচিত। উপলভ্য ডিসপ্লের প্রস্থের উপর ভিত্তি করে সিস্টেম অটোমেটিক সঠিক প্রেজেন্টেশন—স্ট্যাক করা বা পাশাপাশি—বেছে নেয়।

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    RuleController.getInstance(this)
        .addRule(SplitPairRule.Builder(filterSet).build())
    startActivity(Intent(this, DetailActivity::class.java))
}

জাভা

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    RuleController.getInstance(this)
        .addRule(new SplitPairRule.Builder(filterSet).build());
    startActivity(new Intent(this, DetailActivity.class));
}

ডিপ লিঙ্ক ডেস্টিনেশন হল একমাত্র অ্যাক্টিভিটি যা ব্যাক নেভিগেশন স্ট্যাকে ব্যবহারকারীর জন্য উপলভ্য থাকা উচিত এবং আপনি হয়ত বিবরণ অ্যাক্টিভিটি বাতিল করা এবং শুধুমাত্র মূল অ্যাক্টিভিটি রেখে দেওয়া এড়াতে চান:

পাশাপাশি তালিকা অ্যাক্টিভিটি ও বিস্তারিত অ্যাক্টিভিটি সহ বড় ডিসপ্লে।
          ব্যাক নেভিগেশন, বিস্তারিত অ্যাক্টিভিটি ডিসমিস করতে এবং স্ক্রিনে তালিকা
          অ্যাক্টিভিটি ছেড়ে যেতে পারছে না।

শুধুমাত্র বিস্তারিত অ্যাক্টিভিটি সহ ছোট ডিসপ্লে। ব্যাক নেভিগেশন, বিবরণ অ্যাক্টিভিটি ডিসমিস করতে এবং
          তালিকা অ্যাক্টিভিটি দেখাতে পারছে না।

পরিবর্তে, আপনি finishPrimaryWithSecondary অ্যাট্রিবিউট ব্যবহার করে একই সাথে দুটি অ্যাক্টিভিটি সম্পূর্ণ করতে পারবেন:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".ListActivity"
        window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

কনফিগারেশন অ্যাট্রিবিউট বিভাগ দেখুন।

স্প্লিট কন্টেনারে একাধিক অ্যাক্টিভিটি

স্প্লিট কন্টেনারে একাধিক অ্যাক্টিভিটি স্ট্যাক করলে ব্যবহারকারীরা ডিপ কন্টেন্ট অ্যাক্সেস করতে পারেন। যেমন, লিস্ট-ডিটেল স্প্লিটের ক্ষেত্রে, ব্যবহারকারীকে হয়ত সাব-ডিটেল বিভাগে যেতে হবে, কিন্তু প্রাইমারি অ্যাক্টিভিটি একই জায়গায় রাখতে হবে:

ছবি ১৮. টাস্ক উইন্ডোর সেকেন্ডারি প্যানেলে অ্যাক্টিভিটি খোলা হয়েছে।

Kotlin

class DetailActivity : AppCompatActivity() {
    fun onOpenSubdetail() {
        startActivity(Intent(this, SubdetailActivity::class.java))
    }
}

জাভা

public class DetailActivity  extends AppCompatActivity {
    void onOpenSubdetail() {
        startActivity(new Intent(this, SubdetailActivity.class));
    }
}

সাব-ডিটেল অ্যাক্টিভিটি, ডিটেল অ্যাক্টিভিটির উপরে প্লেস করা হয়, ফলে সেটি দেখা যায় না:

ব্যবহারকারী তারপরে স্ট্যাকের মাধ্যমে নেভিগেট করে আগের বিবরণ লেভেলে ফিরে যেতে পারবেন:

ছবি ১৯. স্ট্যাকের সবচেয়ে উপরের অ্যাক্টিভিটি সরিয়ে দেওয়া হয়েছে।

একই সেকেন্ডারি কন্টেনার থেকে অ্যাক্টিভিটি লঞ্চ করা হলে, ডিফল্ট আচরণ হিসেবে একটির উপরে আরেকটি অ্যাক্টিভিটি স্ট্যাক করা হয়। অ্যাক্টিভ স্প্লিটের মধ্যে প্রাইমারি কন্টেনার থেকে লঞ্চ করা অ্যাক্টিভিটি অ্যাক্টিভিটি স্ট্যাকের উপরে সেকেন্ডারি কন্টেনারে শেষ হয়।

নতুন টাস্কে অ্যাক্টিভিটি

স্প্লিট টাস্ক উইন্ডোতে অ্যাক্টিভিটি নতুন টাস্কে অ্যাক্টিভিটি শুরু করলে, নতুন টাস্কটি স্প্লিট করা টাস্ক থেকে আলাদা হয় এবং সেটি সম্পূর্ণ উইন্ডোতে দেখানো হয়। 'সাম্প্রতিক' স্ক্রিনে দুটি টাস্ক দেখানো হয়: স্প্লিট করা টাস্ক এবং নতুন টাস্ক।

ছবি ২০. অ্যাক্টিভিটি B থেকে নতুন টাস্কে অ্যাক্টিভিটি C শুরু করুন।

অ্যাক্টিভিটি রিপ্লেসমেন্ট

সেকেন্ডারি কন্টেনার স্ট্যাকে অ্যাক্টিভিটি রিপ্লেস করা যেতে পারে; যেমন, যখন টপ-লেভেল নেভিগেশনের জন্য প্রাইমারি অ্যাক্টিভিটি ব্যবহার করা হয় এবং সেকেন্ডারি অ্যাক্টিভিটি হল বেছে নেওয়া ডেস্টিনেশন। টপ-লেভেল নেভিগেশন থেকে প্রতিটি বেছে নেওয়া বিকল্প সেকেন্ডারি কন্টেনারে নতুন অ্যাক্টিভিটি শুরু করবে এবং আগে থেকে থাকা অ্যাক্টিভিটি বা অ্যাক্টিভিটিগুলি সরিয়ে দেবে।

ছবি ২১. প্রাইমারি প্যানেলের সবচেয়ে উপরের লেভেলের নেভিগেশন অ্যাক্টিভিটি সেকেন্ডারি প্যানেলের গন্তব্য সংক্রান্ত অ্যাক্টিভিটির পরিবর্তে দেখানো হয়।

নেভিগেশন সিলেকশন পরিবর্তন হলে, অ্যাপ যদি সেকেন্ডারি কন্টেনারে অ্যাক্টিভিটি সম্পূর্ণ করতে না পারে, তাহলে স্প্লিট কলাপ্স করা হলে (ডিভাইস ফোল্ড করা হলে) ব্যাক নেভিগেশন বিভ্রান্তিকর হতে পারে। যেমন, আপনার যদি প্রাইমারি প্যানে একটি মেনু থাকে এবং সেকেন্ডারি প্যানে A ও B স্ক্রিন স্ট্যাক করা থাকে, তাহলে ব্যবহারকারী ফোন ফোল্ড করলে, B, A-এর উপরে এবং A, মেনুর উপরে থাকবে। ব্যবহারকারী B থেকে নেভিগেট করে ফিরে এলে, মেনুর পরিবর্তে A দেখা যায়।

এই ধরনের ক্ষেত্রে, স্ক্রিন 'ক' অবশ্যই ব্যাক স্ট্যাক থেকে সরিয়ে দিতে হবে।

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

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

Kotlin

inner class MenuActivity : AppCompatActivity() {
    fun onMenuItemSelected(selectedMenuItem: Int) {
        startActivity(Intent(this, classForItem(selectedMenuItem)))
    }
}

জাভা

public class MenuActivity extends AppCompatActivity{
    void onMenuItemSelected(int selectedMenuItem) {
        startActivity(new Intent(this, classForItem(selectedMenuItem)));
    }
}

অথবা, একই সেকেন্ডারি অ্যাক্টিভিটি ব্যবহার করুন এবং প্রাইমারি (মেনু) অ্যাক্টিভিটি থেকে নতুন ইনটেন্ট পাঠান যা একই ইনস্ট্যান্সের সাথে সমাধান করে কিন্তু সেকেন্ডারি কন্টেনারে একটি স্টেট বা UI আপডেট ট্রিগার করে।

একাধিক স্প্লিট

অ্যাপ, পাশে অতিরিক্ত অ্যাক্টিভিটি লঞ্চ করে মাল্টি-লেভেল ডিপ নেভিগেশন প্রদান করতে পারে ।

সেকেন্ডারি কন্টেনারে থাকা কোনও অ্যাক্টিভিটি পাশে নতুন অ্যাক্টিভিটি লঞ্চ করলে, আগে থেকে থাকা স্প্লিটের উপরে নতুন স্প্লিট তৈরি হয়।

ছবি ২২. অ্যাক্টিভিটি B, অ্যাক্টিভিটি C-কে পাশে শুরু করে।

ব্যাক স্ট্যাকে আগে খোলা সব অ্যাক্টিভিটি থাকে, তাই ব্যবহারকারীরা C শেষ করার পরে A/B স্প্লিটে নেভিগেট করতে পারবেন।

স্ট্যাকে অ্যাক্টিভিটি A, B ও C। অ্যাক্টিভিটিগুলি
          উপর থেকে নিচে এই ক্রমে সাজানো হয়েছে: C, B, A.

নতুন স্প্লিট তৈরি করতে, আগে থেকে থাকা সেকেন্ডারি কন্টেনার থেকে নতুন অ্যাক্টিভিটি পাশে লঞ্চ করুন। A/B ও B/C স্প্লিটের জন্য কনফিগারেশন ঘোষণা করুন এবং B থেকে অ্যাক্টিভিটি C স্বাভাবিকভাবে লঞ্চ করুন:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
    <SplitPairFilter
        window:primaryActivityName=".B"
        window:secondaryActivityName=".C"/>
</SplitPairRule>

Kotlin

class B : AppCompatActivity() {
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

জাভা

public class B extends AppCompatActivity{
    void onOpenC() {
        startActivity(new Intent(this, C.class));
    }
}

স্প্লিট স্টেট পরিবর্তনের ব্যাপারে প্রতিক্রিয়া জানানো

কোনও অ্যাপের বিভিন্ন অ্যাক্টিভিটিতে UI এলিমেন্ট থাকতে পারে যা একই ফাংশন পারফর্ম করে; যেমন, একটি কন্ট্রোল যা অ্যাকাউন্ট সেটিং সহ একটি উইন্ডো খোলে।

ছবি ২৩. কার্যকারিতার দিক থেকে একই ধরনের UI এলিমেন্ট সহ বিভিন্ন অ্যাক্টিভিটি।

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

ছবি ২৪. অ্যাক্টিভিটি স্প্লিটে ডুপ্লিকেট UI এলিমেন্ট।

অ্যাক্টিভিটি কখন স্প্লিট করা হয়েছে তা জানতে, SplitController.splitInfoList ফ্লো চেক করুন অথবা স্প্লিট স্টেটে পরিবর্তন হলে তা জানতে SplitControllerCallbackAdapter-এর সাথে লিসনার রেজিস্টার করুন। তারপর, সেই অনুযায়ী UI অ্যাডজাস্ট করুন:

Kotlin

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance.
            .collect { list ->
                view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
            }
    }
}

জাভা

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    new SplitControllerCallbackAdapter(SplitController.getInstance(this))
        .addSplitListener(
            this,
            Runnable::run,
            splitInfoList -> {
                View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
                layout.findViewById(R.id.infoButton).setVisibility(
                    splitInfoList.isEmpty() ? View.VISIBLE : View.GONE);
            });
}

যেকোনও লাইফসাইকেল স্টেটে কোরাউটিন লঞ্চ করা যায়, তবে সাধারণত রিসোর্স সেভ করার জন্য STARTED স্টেটে লঞ্চ করা হয় (আরও তথ্যের জন্য লাইফসাইকেল-অ্যাওয়ার কম্পোনেন্টের সাথে Kotlin কোরাউটিন ব্যবহার করুন দেখুন)।

অ্যাক্টিভিটি বন্ধ হয়ে গেলেও, যেকোনও লাইফসাইকেল স্টেটে কলব্যাক করা যেতে পারে। শ্রোতাদের সাধারণত onStart()-এ রেজিস্টার করা এবং onStop()-এ আনরেজিস্টার করা উচিত।

সম্পূর্ণ-উইন্ডো মোডাল

কিছু অ্যাক্টিভিটি নির্দিষ্ট অ্যাকশন না নেওয়া পর্যন্ত ব্যবহারকারীদের অ্যাপ্লিকেশনটির সাথে ইন্টার‍্যাক্ট করতে বাধা দেয়; যেমন, লগ-ইন স্ক্রিন অ্যাক্টিভিটি, নীতি স্বীকৃতি স্ক্রিন বা এরর মেসেজ। মডেল অ্যাক্টিভিটি যাতে স্প্লিটে দেখা না যায় তা নিশ্চিত করতে হবে।

expand configuration ব্যবহার করে কোনও অ্যাক্টিভিটিকে সবসময় টাস্ক উইন্ডো পূরণ করতে বাধ্য করা যেতে পারে:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

অ্যাক্টিভিটি সম্পূর্ণ করা

ডিসপ্লের প্রান্ত থেকে সোয়াইপ করে ব্যবহারকারীরা স্প্লিটের যেকোনও দিকে অ্যাক্টিভিটি সম্পূর্ণ করতে পারবেন:

ছবি ২৫. সোয়াইপ জেসচার অ্যাক্টিভিটি 'খ' শেষ করছে।
ছবি ২৬. সোয়াইপ জেসচার অ্যাক্টিভিটি 'ক' সম্পূর্ণ করছে।

ডিভাইসটি জেসচার নেভিগেশনের পরিবর্তে 'ফিরে যান' বোতাম ব্যবহার করার জন্য সেট-আপ করা থাকলে, ইনপুটটি ফোকাস করা অ্যাক্টিভিটিতে পাঠানো হয়—যে অ্যাক্টিভিটি স্পর্শ করা হয়েছে বা সর্বশেষ লঞ্চ করা হয়েছে।

কোনও কন্টেনারে সব অ্যাক্টিভিটি সম্পূর্ণ করার ফলে বিপরীত কন্টেনারের উপর যে প্রভাব পড়ে তা স্প্লিট কনফিগারেশনের উপর নির্ভর করে।

কনফিগারেশন অ্যাট্রিবিউট

আপনি স্প্লিট পেয়ার রুল অ্যাট্রিবিউট নির্দিষ্ট করতে পারেন, যাতে স্প্লিটের একদিকের সব অ্যাক্টিভিটি সম্পূর্ণ হলে তা স্প্লিটের অন্যদিকের অ্যাক্টিভিটিকে কীভাবে প্রভাবিত করে তা কনফিগার করা যায়। অ্যাট্রিবিউটগুলি হল:

  • window:finishPrimaryWithSecondary — How finishing all activities in the secondary container affects the activities in the primary container
  • window:finishSecondaryWithPrimary — প্রাইমারি কন্টেনারে সব অ্যাক্টিভিটি সম্পূর্ণ করলে সেকেন্ডারি কন্টেনারের অ্যাক্টিভিটির উপর কী প্রভাব পড়ে

অ্যাট্রিবিউটের সম্ভাব্য ভ্যালুর মধ্যে এগুলি অন্তর্ভুক্ত:

  • always — সবসময় সংশ্লিষ্ট কন্টেনারে অ্যাক্টিভিটি সম্পূর্ণ করুন
  • never — সংশ্লিষ্ট কন্টেনারে অ্যাক্টিভিটি কখনও শেষ করবেন না
  • adjacent — দুটি কন্টেনার একে অপরের পাশে দেখানো হলে, সংশ্লিষ্ট কন্টেনারে থাকা অ্যাক্টিভিটি সম্পূর্ণ করুন, কিন্তু দুটি কন্টেনার একে অপরের উপরে থাকলে নয়

যেমন:

<SplitPairRule
    <!-- Do not finish primary container activities when all secondary container activities finish. -->
    window:finishPrimaryWithSecondary="never"
    <!-- Finish secondary container activities when all primary container activities finish. -->
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

ডিফল্ট কনফিগারেশন

স্প্লিট উইন্ডোর একটি কন্টেনারের সব অ্যাক্টিভিটি শেষ হয়ে গেলে, বাকি কন্টেনারটি সম্পূর্ণ উইন্ডো জুড়ে জায়গা নেয়:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

অ্যাক্টিভিটি A ও B সহ স্প্লিট। A সম্পূর্ণ হয়ে গেছে, তাই B-কে
          সম্পূর্ণ উইন্ডো জুড়ে দেখানো হচ্ছে।

অ্যাক্টিভিটি A ও B সহ স্প্লিট। B সম্পূর্ণ হয়ে গেছে, ফলে A-কে
          পুরো উইন্ডো জুড়ে দেখানো হচ্ছে।

একসাথে অ্যাক্টিভিটি সম্পূর্ণ করা

সেকেন্ডারি কন্টেনারের সব অ্যাক্টিভিটি শেষ হয়ে গেলে প্রাইমারি কন্টেনারের অ্যাক্টিভিটি অটোমেটিক শেষ করুন:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

অ্যাক্টিভিটি A ও B সহ স্প্লিট। B সম্পূর্ণ হয়ে গেছে, এর ফলে
          A-ও সম্পূর্ণ হয়ে গেছে এবং টাস্ক উইন্ডো খালি হয়ে গেছে।

অ্যাক্টিভিটি A ও B সহ স্প্লিট। A সম্পূর্ণ হয়ে গেছে, টাস্ক উইন্ডোতে শুধু B আছে।

প্রাইমারি কন্টেনারের সব অ্যাক্টিভিটি শেষ হয়ে গেলে, সেকেন্ডারি কন্টেনারের অ্যাক্টিভিটি অটোমেটিক শেষ করুন:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

অ্যাক্টিভিটি A ও B সহ স্প্লিট। A সম্পূর্ণ হয়ে গেছে, এর ফলে B-ও সম্পূর্ণ হয়ে গেছে, যার ফলে টাস্ক উইন্ডো খালি হয়ে গেছে।

অ্যাক্টিভিটি A ও B সহ স্প্লিট। B সম্পূর্ণ হয়ে গেছে, টাস্ক উইন্ডোতে শুধু A
          রয়েছে।

প্রাইমারি বা সেকেন্ডারি কন্টেনারের সব অ্যাক্টিভিটি শেষ হয়ে গেলে, একসাথে অ্যাক্টিভিটি শেষ করুন:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

অ্যাক্টিভিটি A ও B সহ স্প্লিট। A সম্পূর্ণ হয়ে গেছে, এর ফলে B-ও সম্পূর্ণ হয়ে গেছে, যার ফলে টাস্ক উইন্ডো খালি হয়ে গেছে।

অ্যাক্টিভিটি A ও B সহ স্প্লিট। B সম্পূর্ণ হয়ে গেছে, এর ফলে
          A-ও সম্পূর্ণ হয়ে গেছে এবং টাস্ক উইন্ডো খালি হয়ে গেছে।

কন্টেনারে একাধিক অ্যাক্টিভিটি সম্পূর্ণ করা

স্প্লিট কন্টেনারে একাধিক অ্যাক্টিভিটি স্ট্যাক করা থাকলে, স্ট্যাকের সবচেয়ে নিচে থাকা অ্যাক্টিভিটি শেষ করলে, সবচেয়ে উপরে থাকা অ্যাক্টিভিটি অটোমেটিক শেষ হয়ে যায় না।

যেমন, দুটি অ্যাক্টিভিটি সেকেন্ডারি কন্টেনারে থাকলে, B-এর উপরে C:

সেকেন্ডারি অ্যাক্টিভিটি স্ট্যাক, যার মধ্যে অ্যাক্টিভিটি C, B-এর উপরে স্ট্যাক করা আছে
          সেটি প্রাইমারি অ্যাক্টিভিটি স্ট্যাকের উপরে স্ট্যাক করা আছে, যার মধ্যে অ্যাক্টিভিটি
          A আছে।

এবং স্প্লিটের কনফিগারেশন, অ্যাক্টিভিটি ক ও খ-এর কনফিগারেশন দ্বারা নির্ধারিত হয়:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

সবচেয়ে গুরুত্বপূর্ণ অ্যাক্টিভিটি সম্পূর্ণ করলে স্প্লিট বজায় থাকে।

প্রাইমারি কন্টেনারে অ্যাক্টিভিটি A এবং সেকেন্ডারি কন্টেনারে অ্যাক্টিভিটি B ও C
          স্প্লিট করা হয়েছে, C, B-এর উপরে স্ট্যাক করা হয়েছে। C শেষ করে, A ও B-কে
          অ্যাক্টিভিটি স্প্লিটে রেখে দেয়।

সেকেন্ডারি কন্টেনারের সবচেয়ে নিচের (রুট) অ্যাক্টিভিটি শেষ হয়ে গেলেও, তার উপরে থাকা অ্যাক্টিভিটি মুছে যায় না; তাই স্প্লিটও বজায় থাকে।

প্রাইমারি কন্টেনারে অ্যাক্টিভিটি A এবং সেকেন্ডারি কন্টেনারে অ্যাক্টিভিটি B ও C
          স্প্লিট করা হয়েছে, C, B-এর উপরে স্ট্যাক করা হয়েছে। B শেষ করে, A ও C-কে অ্যাক্টিভিটি স্প্লিটে
          রেখে দেয়।

একসাথে অ্যাক্টিভিটি শেষ করার জন্য কোনও অতিরিক্ত নিয়ম, যেমন, প্রাথমিক অ্যাক্টিভিটির সাথে সেকেন্ডারি অ্যাক্টিভিটি শেষ করা, সেগুলিও প্রয়োগ করা হয়:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

প্রাইমারি কন্টেনারে অ্যাক্টিভিটি A এবং সেকেন্ডারি কন্টেনারে অ্যাক্টিভিটি B ও C
          স্প্লিট করা হয়েছে, C, B-এর উপরে স্ট্যাক করা আছে। A শেষ করে, এছাড়াও
          B ও C শেষ করে।

এবং স্প্লিট যদি প্রাথমিক ও গৌণ, দু'টিই একসাথে সম্পূর্ণ করার জন্য কনফিগার করা হয়:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

প্রাইমারি কন্টেনারে অ্যাক্টিভিটি A এবং সেকেন্ডারি কন্টেনারে অ্যাক্টিভিটি B ও C
          স্প্লিট করা হয়েছে, C, B-এর উপরে স্ট্যাক করা হয়েছে। C শেষ করে, A ও B-কে
          অ্যাক্টিভিটি স্প্লিটে রেখে দেয়।

প্রাইমারি কন্টেনারে অ্যাক্টিভিটি A এবং সেকেন্ডারি কন্টেনারে অ্যাক্টিভিটি B ও C
          স্প্লিট করা হয়েছে, C, B-এর উপরে স্ট্যাক করা হয়েছে। B শেষ করে, A ও C-কে অ্যাক্টিভিটি স্প্লিটে
          রেখে দেয়।

প্রাইমারি কন্টেনারে অ্যাক্টিভিটি A এবং সেকেন্ডারি কন্টেনারে অ্যাক্টিভিটি B ও C
          স্প্লিট করা হয়েছে, C, B-এর উপরে স্ট্যাক করা হয়েছে। A শেষ হলে, B ও
          C-ও শেষ হয়ে যায়।

রানটাইমে স্প্লিট প্রপার্টি পরিবর্তন করা

অ্যাক্টিভ ও দৃশ্যমান স্প্লিটের প্রপার্টি পরিবর্তন করা যায় না। স্প্লিট করার নিয়ম পরিবর্তন করলে, অতিরিক্ত অ্যাক্টিভিটি লঞ্চ ও নতুন কন্টেনার প্রভাবিত হয়, তবে আগে থেকে থাকা ও অ্যাক্টিভ স্প্লিট প্রভাবিত হয় না।

অ্যাক্টিভ স্প্লিটের প্রপার্টি পরিবর্তন করতে, স্প্লিটের মধ্যে থাকা সাইড অ্যাক্টিভিটি বা অ্যাক্টিভিটি সম্পূর্ণ করুন এবং নতুন কনফিগারেশন সহ আবার সাইডে লঞ্চ করুন।

ডায়নামিক স্প্লিট প্রপার্টি

Jetpack WindowManager 1.4 ও এর পরের যেকোনও ভার্সনে কাজ করে এমন Android 15 (API লেভেল ৩৫) ও এর পরের যেকোনও ভার্সন ডায়নামিক ফিচার অফার করে যা অ্যাক্টিভিটি এমবেডিং স্প্লিটের কনফিগার করার সুবিধা দেয়, এর মধ্যে এগুলি অন্তর্ভুক্ত:

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

প্যানেল বড় করা

প্যানেল বড় করার সুবিধা ব্যবহার করে, ডুয়াল‑প্যানেল লেআউটে থাকা দুটি অ্যাক্টিভিটির জন্য বরাদ্দ করা স্ক্রিন স্পেসের পরিমাণ ব্যবহারকারীরা অ্যাডজাস্ট করতে পারেন।

উইন্ডো ডিভাইডারের চেহারা কাস্টমাইজ করতে এবং ডিভাইডারের ড্র্যাগ করার রেঞ্জ সেট করতে, নিম্নলিখিত কাজগুলি করুন:

  1. DividerAttributes-এর ইনস্ট্যান্স তৈরি করো

  2. ডিভাইডার অ্যাট্রিবিউট কাস্টমাইজ করা:

    • color: ড্র্যাগ করা যায় এমন প্যানেল ডিভাইডারের রঙ।

    • widthDp: ড্র্যাগ করা যায় এমন প্যানেল সেপারেটরের প্রস্থ। ডিভাইডারের প্রস্থ সিস্টেমকে নির্ধারণ করতে দিতে, WIDTH_SYSTEM_DEFAULT-এ সেট করুন।

    • ড্র্যাগ রেঞ্জ: স্ক্রিনের সর্বনিম্ন শতাংশ যা যেকোনও প্যানে দখল করতে পারে। ০.৩৩ থেকে ০.৬৬ পর্যন্ত হতে পারে। সিস্টেমকে ড্র্যাগ রেঞ্জ নির্ধারণ করতে দিতে, DRAG_RANGE_SYSTEM_DEFAULT-এ সেট করুন ।

    Kotlin

    val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder()
        .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
        .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
    
    if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
        splitAttributesBuilder.setDividerAttributes(
            DividerAttributes.DraggableDividerAttributes.Builder()
                .setColor(getColor(R.color.divider_color))
                .setWidthDp(4)
                .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
                .build()
        )
    }
    val splitAttributes: SplitAttributes = splitAttributesBuilder.build()

    জাভা

    SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder()
        .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
        .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT);
    
    if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
        splitAttributesBuilder.setDividerAttributes(
          new DividerAttributes.DraggableDividerAttributes.Builder()
            .setColor(ContextCompat.getColor(this, R.color.divider_color))
            .setWidthDp(4)
            .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
            .build()
        );
    }
    SplitAttributes _splitAttributes = splitAttributesBuilder.build();

অ্যাক্টিভিটি স্ট্যাক পিনিং

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

আপনার অ্যাপে অ্যাক্টিভিটি স্ট্যাক পিনিং চালু করতে, নিম্নলিখিত কাজগুলি করুন:

  1. আপনি যে অ্যাক্টিভিটি পিন করতে চান, তার লেআউট ফাইলে একটি বোতাম যোগ করুন, যেমন, একটি লিস্ট‑ডিটেল লেআউটের বিবরণ অ্যাক্টিভিটি:

    <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/detailActivity"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/white"
     tools:context=".DetailActivity">
    
    <TextView
       android:id="@+id/textViewItemDetail"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textSize="36sp"
       android:textColor="@color/obsidian"
       app:layout_constraintBottom_toTopOf="@id/pinButton"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
    
    <androidx.appcompat.widget.AppCompatButton
       android:id="@+id/pinButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/pin_this_activity"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. অ্যাক্টিভিটির onCreate() পদ্ধতিতে, বোতামে onclick লিসনার সেট করুন:

    Kotlin

    val pinButton: Button = findViewById(R.id.pinButton)
    pinButton.setOnClickListener {
        val splitAttributes: SplitAttributes = SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build()
    
        val pinSplitRule = SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build()
    
        SplitController.getInstance(applicationContext)
            .pinTopActivityStack(taskId, pinSplitRule)
    }

    জাভা

    Button pinButton = findViewById(R.id.pinButton);
    pinButton.setOnClickListener( (view) -> {
        SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();
    
        SplitPinRule pinSplitRule = new SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build();
    
        SplitController.getInstance(getApplicationContext())
            .pinTopActivityStack(getTaskId(), pinSplitRule);
    });

ডায়ালগ ফুল-স্ক্রিন ডিম

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

WindowManager 1.4 ও তার পরের যেকোনও ভার্সনে, কোনও ডায়ালগ খুললে, ডিফল্ট হিসেবে পুরো অ্যাপ উইন্ডোটি ডিম হয়ে যায় (EmbeddingConfiguration.DimAreaBehavior.ON_TASK দেখুন)।

ডায়ালগ খোলা অ্যাক্টিভিটির কন্টেনারকে শুধুমাত্র ডিম করতে, EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK ব্যবহার করুন।

স্প্লিট উইন্ডো থেকে ফুল উইন্ডোতে অ্যাক্টিভিটি এক্সট্র্যাক্ট করা

এমন একটি নতুন কনফিগারেশন তৈরি করুন যা সাইড অ্যাক্টিভিটি সম্পূর্ণ উইন্ডোতে দেখায় এবং তারপরে একই ইনস্ট্যান্সের সাথে সমাধান করা যায় এমন ইনটেন্ট সহ অ্যাক্টিভিটি আবার চালু করুন।

রানটাইমে স্প্লিট সাপোর্ট চেক করা

অ্যাক্টিভিটি এম্বেডিং Android 12L (API লেভেল 32) ও তার পরের যেকোনও ভার্সনে কাজ করে, তবে আগের প্ল্যাটফর্ম ভার্সনে চলে এমন কিছু ডিভাইসেও উপলভ্য। ফিচারের উপলভ্যতা রানটাইমে চেক করতে, SplitController.splitSupportStatus প্রপার্টি বা SplitController.getSplitSupportStatus() মেথড ব্যবহার করুন:

Kotlin

if (SplitController.getInstance(this).splitSupportStatus ==
    SplitController.SplitSupportStatus.SPLIT_AVAILABLE
) {
    // Device supports split activity features.
}

জাভা

if (SplitController.getInstance(this).getSplitSupportStatus() ==
    SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
    // Device supports split activity features.
}

স্প্লিট কাজ না করলে, অ্যাক্টিভিটি স্ট্যাকের উপরে অ্যাক্টিভিটি লঞ্চ করা হয় (নন-অ্যাক্টিভিটি এম্বেডিং মডেল অনুসরণ করে)।

সিস্টেম ওভাররাইড প্রতিরোধ করা

Android ডিভাইসের প্রস্তুতকারক (মূল সরঞ্জাম প্রস্তুতকারক বা OEM) ডিভাইস সিস্টেমের ফাংশন হিসেবে অ্যাক্টিভিটি এম্বেডিং প্রয়োগ করতে পারে। সিস্টেম মাল্টি-অ্যাক্টিভিটি অ্যাপের জন্য স্প্লিট করার নিয়ম নির্দিষ্ট করে, যা অ্যাপের উইন্ডো বিহেভিয়ারকে ওভাররাইড করে। সিস্টেম ওভাররাইড একাধিক অ্যাক্টিভিটি থাকা অ্যাপকে সিস্টেম-সংজ্ঞায়িত অ্যাক্টিভিটি এম্বেডিং মোডে বাধ্য করে।

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

আপনার অ্যাপ, অ্যাপ ম্যানিফেস্ট ফাইলে PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE সেট করার মাধ্যমে সিস্টেম অ্যাক্টিভিটি এম্বেড করা আটকাতে বা অনুমতি দিতে পারে, যেমন:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
            android:value="true|false" />
    </application>
</manifest>

Jetpack WindowManager WindowProperties অবজেক্টে প্রপার্টির নাম সংজ্ঞায়িত করা হয়। আপনার অ্যাপে অ্যাক্টিভিটি এম্বেডিং প্রয়োগ করা হলে অথবা আপনার অ্যাপে সিস্টেমের অ্যাক্টিভিটি এম্বেডিং নিয়ম প্রয়োগ করা আটকাতে চাইলে, ভ্যালু false হিসেবে সেট করুন। আপনার অ্যাপে সিস্টেম-সংজ্ঞায়িত অ্যাক্টিভিটি এম্বেডিং প্রয়োগ করার অনুমতি দিতে, ভ্যালু true হিসেবে সেট করুন।

সীমাবদ্ধতা, বিধিনিষেধ ও সতর্কতা

  • টাস্কের শুধু সেই হোস্ট অ্যাপ, যা টাস্কে রুট অ্যাক্টিভিটির মালিক হিসেবে শনাক্ত করা হয়েছে, সেটিই টাস্কে অন্যান্য অ্যাক্টিভিটি সাজাতে ও এম্বেড করতে পারে। এম্বেডিং ও স্প্লিট কাজ করে এমন অ্যাক্টিভিটি যদি এমন টাস্কে চলে যা অন্য কোনও অ্যাপ্লিকেশনের অন্তর্গত, তাহলে এম্বেডিং ও স্প্লিট সেইসব অ্যাক্টিভিটির জন্য কাজ করবে না।
  • অ্যাক্টিভিটি শুধুমাত্র একটি টাস্কের মধ্যেই সাজানো যেতে পারে। নতুন টাস্কে কোনও অ্যাক্টিভিটি লঞ্চ করলে, সেটি সবসময় আগে থেকে থাকা কোনও স্প্লিটের বাইরে নতুন এক্সপ্যান্ড করা উইন্ডোতে খোলে।
  • একই প্রসেসের অ্যাক্টিভিটিগুলিই শুধুমাত্র সাজানো ও স্প্লিট করা যাবে। যেহেতু আলাদা প্রসেসে হওয়া অ্যাক্টিভিটি সম্পর্কে জানার কোনও উপায় নেই, তাই SplitInfo কলব্যাক শুধুমাত্র একই প্রসেসের অ্যাক্টিভিটি সম্পর্কে রিপোর্ট করে।
  • প্রতিটি পেয়ার বা সিঙ্গুলার অ্যাক্টিভিটি নিয়ম শুধুমাত্র সেইসব অ্যাক্টিভিটি লঞ্চের ক্ষেত্রে প্রযোজ্য হয় যেগুলি নিয়ম রেজিস্টার করার পরে ঘটে। বর্তমানে আগে থেকে থাকা স্প্লিট বা সেগুলির ভিজ্যুয়াল প্রপার্টি আপডেট করার কোনও উপায় নেই।
  • সম্পূর্ণ অ্যাক্টিভিটি লঞ্চ করার সময় ব্যবহৃত ইনটেন্টের সাথে স্প্লিট পেয়ার ফিল্টার কনফিগারেশন অবশ্যই মিলতে হবে। অ্যাপ্লিকেশন প্রসেস থেকে নতুন অ্যাক্টিভিটি শুরু হলে ম্যাচিং হয়, তাই এটি কম্পোনেন্টের নাম সম্পর্কে নাও জানতে পারে যা ইমপ্লিসিট ইনটেন্ট ব্যবহার করার সময় সিস্টেম প্রসেসে পরে সমাধান করা হয়। লঞ্চ করার সময় কম্পোনেন্টের নাম জানা না থাকলে, তার পরিবর্তে ওয়াইল্ডকার্ড ব্যবহার করা যেতে পারে ("*/*") এবং ইনটেন্ট অ্যাকশনের উপর ভিত্তি করে ফিল্টার করা যেতে পারে।
  • কন্টেনার তৈরি করার পরে সেগুলির মধ্যে বা স্প্লিট থেকে বের করে অ্যাক্টিভিটি সরানোর কোনও উপায় বর্তমানে নেই। ম্যাচিং নিয়ম সহ নতুন অ্যাক্টিভিটি লঞ্চ করা হলে, তবেই WindowManager লাইব্রেরি স্প্লিট তৈরি করে এবং স্প্লিট কন্টেনারের শেষ অ্যাক্টিভিটি শেষ হয়ে গেলে স্প্লিট ধ্বংস হয়ে যায়।
  • কনফিগারেশনে পরিবর্তন হলে অ্যাক্টিভিটি আবার লঞ্চ করা যেতে পারে, তাই স্প্লিট তৈরি বা সরানো হলে এবং অ্যাক্টিভিটি বাউন্ড পরিবর্তন হলে, অ্যাক্টিভিটি আগের ইনস্ট্যান্স সম্পূর্ণভাবে মুছে দিয়ে নতুন ইনস্ট্যান্স তৈরি করতে পারে। এর ফলে, অ্যাপ ডেভেলপারদের নিম্নলিখিত বিষয়গুলি সম্পর্কে সতর্ক থাকতে হবে, যেমন লাইফসাইকেল কলব্যাক থেকে নতুন অ্যাক্টিভিটি লঞ্চ করা।
  • অ্যাক্টিভিটি এম্বেডিং কাজ করার জন্য ডিভাইসে উইন্ডো এক্সটেনশন ইন্টারফেস থাকতে হবে। Android 12L (API লেভেল 32) বা তার পরের যেকোনও ভার্সনে চলে এমন প্রায় সব বড় স্ক্রিনের ডিভাইসে ইন্টারফেস অন্তর্ভুক্ত থাকে। তবে, কিছু বড় স্ক্রিনের ডিভাইস যা একাধিক অ্যাক্টিভিটি চালাতে পারে না, সেগুলিতে উইন্ডো এক্সটেনশন ইন্টারফেস থাকে না। কোনও বড় স্ক্রিন ডিভাইসে মাল্টি-উইন্ডো মোড কাজ না করলে, তাতে অ্যাক্টিভিটি এম্বেডিং কাজ নাও করতে পারে।

অতিরিক্ত রিসোর্স