আপনার অ্যান্ড্রয়েড অ্যাপে Google এর মাধ্যমে সাইন ইন কীভাবে প্রয়োগ করবেন তা জানুন

১. শুরু করার আগে

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

পূর্বশর্ত

  • অ্যান্ড্রয়েড ডেভেলপমেন্টে কোটলিন ব্যবহারের প্রাথমিক ধারণা
  • জেটপ্যাক কম্পোজ সম্পর্কে প্রাথমিক ধারণা (আরও তথ্য এখানে পাওয়া যাবে)

আপনি যা শিখবেন

  • কীভাবে একটি গুগল ক্লাউড প্রজেক্ট তৈরি করবেন
  • গুগল ক্লাউড কনসোলে কীভাবে OAuth ক্লায়েন্ট তৈরি করবেন
  • বটম শীট ফ্লো ব্যবহার করে কীভাবে সাইন ইন উইথ গুগল বাস্তবায়ন করবেন
  • বাটন ফ্লো ব্যবহার করে কীভাবে গুগল দিয়ে সাইন ইন বাস্তবায়ন করবেন

আপনার যা প্রয়োজন

২. একটি অ্যান্ড্রয়েড স্টুডিও প্রজেক্ট তৈরি করুন।

সময়কাল ৩:০০ - ৫:০০

শুরু করার জন্য, আমাদের অ্যান্ড্রয়েড স্টুডিওতে একটি নতুন প্রজেক্ট তৈরি করতে হবে:

  1. অ্যান্ড্রয়েড স্টুডিও খুলুন
  2. নতুন প্রকল্পে ক্লিক করুন অ্যান্ড্রয়েড স্টুডিওতে স্বাগতম
  3. ফোন ও ট্যাবলেট নির্বাচন করুন এবং কার্যকলাপ খালি রাখুন অ্যান্ড্রয়েড স্টুডিও প্রজেক্ট
  4. পরবর্তী ক্লিক করুন
  5. এখন প্রকল্পের কয়েকটি অংশ প্রস্তুত করার সময় হয়েছে:
    • নাম : এটি আপনার প্রকল্পের নাম।
    • প্যাকেজের নাম : এটি আপনার প্রোজেক্টের নামের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে পূরণ হবে।
    • সংরক্ষণের স্থান : এটি ডিফল্টভাবে সেই ফোল্ডারে সেট করা থাকবে যেখানে অ্যান্ড্রয়েড স্টুডিও আপনার প্রোজেক্টগুলো সংরক্ষণ করে। আপনি এটি আপনার পছন্দমতো যেকোনো জায়গায় পরিবর্তন করতে পারেন।
    • ন্যূনতম SDK : এটি অ্যান্ড্রয়েড SDK-এর সর্বনিম্ন সংস্করণ, যেটিতে আপনার অ্যাপটি চলার জন্য তৈরি করা হয়েছে। এই কোডল্যাবে, আমরা API 36 (বাকলাভা) ব্যবহার করব।
    অ্যান্ড্রয়েড স্টুডিও সেটআপ প্রজেক্ট
  6. ফিনিশ ক্লিক করুন
  7. অ্যান্ড্রয়েড স্টুডিও প্রজেক্টটি তৈরি করবে এবং মূল অ্যাপ্লিকেশনের জন্য প্রয়োজনীয় ডিপেন্ডেন্সিগুলো ডাউনলোড করবে, এতে কয়েক মিনিট সময় লাগতে পারে। এটি দেখতে, শুধু বিল্ড আইকনে ক্লিক করুন: অ্যান্ড্রয়েড স্টুডিও প্রজেক্ট বিল্ডিং
  8. এটি সম্পন্ন হয়ে গেলে, অ্যান্ড্রয়েড স্টুডিও দেখতে অনেকটা এইরকম হবে: অ্যান্ড্রয়েড স্টুডিও প্রজেক্ট তৈরি

৩. আপনার গুগল ক্লাউড প্রজেক্ট সেট আপ করুন।

একটি গুগল ক্লাউড প্রজেক্ট তৈরি করুন

  1. গুগল ক্লাউড কনসোলে যান
  2. আপনার প্রজেক্টটি খুলুন অথবা একটি নতুন প্রজেক্ট তৈরি করুন। GCP নতুন প্রজেক্ট তৈরি করুনজিসিপি নতুন প্রজেক্ট ২ তৈরি করুনGCP নতুন প্রজেক্ট 3 তৈরি করুন
  3. এপিআই এবং পরিষেবাগুলিতে ক্লিক করুন জিসিপি এপিআই এবং পরিষেবা
  4. OAuth সম্মতি স্ক্রিনে যান জিসিপি ওঅথ সম্মতি স্ক্রিন
  5. চালিয়ে যাওয়ার জন্য আপনাকে ওভারভিউ- এর ফিল্ডগুলো পূরণ করতে হবে। এই তথ্যগুলো পূরণ করা শুরু করতে 'গেট স্টার্টেড'-এ ক্লিক করুন: জিসিপি শুরু করার বোতাম
    • অ্যাপের নাম : এই অ্যাপের নাম, যা অ্যান্ড্রয়েড স্টুডিওতে প্রজেক্ট তৈরি করার সময় ব্যবহৃত নামের মতোই হতে হবে।
    • ব্যবহারকারী সহায়তা ইমেল : এর মাধ্যমে আপনি যে গুগল অ্যাকাউন্ট দিয়ে লগইন করেছেন এবং আপনার পরিচালিত যেকোনো গুগল গ্রুপ দেখতে পাবেন।
    জিসিপি অ্যাপের তথ্য
    • দর্শক :
      • শুধুমাত্র আপনার প্রতিষ্ঠানের অভ্যন্তরে ব্যবহৃত অ্যাপের জন্য এটি 'অভ্যন্তরীণ'। যদি গুগল ক্লাউড প্রজেক্টের সাথে আপনার কোনো প্রতিষ্ঠান যুক্ত না থাকে, তাহলে আপনি এটি নির্বাচন করতে পারবেন না।
      • বাহ্যিক বলতে আমরা যা ব্যবহার করছি সেটাই বোঝাবে।
    জিসিপি দর্শক
    • যোগাযোগের তথ্য : এখানে যেকোনো ইমেল ঠিকানা দিতে পারেন, যা আপনি অ্যাপ্লিকেশনটির জন্য যোগাযোগের মাধ্যম হিসেবে ব্যবহার করতে চান।
    জিসিপি যোগাযোগের তথ্য
    • গুগল এপিআই পরিষেবা: ব্যবহারকারীর ডেটা নীতি পর্যালোচনা করুন।
  6. ব্যবহারকারীর তথ্য নীতি পর্যালোচনা করে তাতে সম্মত হওয়ার পর, তৈরি করুন (Create) বোতামে ক্লিক করুন। জিসিপি তৈরি করুন

OAuth ক্লায়েন্ট সেট আপ করুন

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

অ্যান্ড্রয়েড ওয়েব ক্লায়েন্টের জন্য আপনার প্রয়োজন হবে:

  • আপনার অ্যাপের প্যাকেজ নাম (যেমন, com.example.example)
  • আপনার অ্যাপের SHA-1 স্বাক্ষর
    • SHA-1 সিগনেচার বলতে কী বোঝায়?
      • SHA-1 ফিঙ্গারপ্রিন্ট হলো একটি ক্রিপ্টোগ্রাফিক হ্যাশ যা আপনার অ্যাপের সাইনিং কী থেকে তৈরি করা হয়। এটি আপনার নির্দিষ্ট অ্যাপের সাইনিং সার্টিফিকেটের জন্য একটি অনন্য শনাক্তকারী হিসেবে কাজ করে। এটিকে আপনার অ্যাপের জন্য একটি ডিজিটাল 'স্বাক্ষর' হিসেবে ভাবা যেতে পারে।
    • আমাদের SHA-1 স্বাক্ষরের প্রয়োজন কেন?
      • SHA-1 ফিঙ্গারপ্রিন্ট নিশ্চিত করে যে, শুধুমাত্র আপনার নির্দিষ্ট সাইনিং কী দিয়ে স্বাক্ষরিত আপনার অ্যাপটিই আপনার OAuth 2.0 ক্লায়েন্ট আইডি ব্যবহার করে অ্যাক্সেস টোকেনের জন্য অনুরোধ করতে পারবে। এর ফলে অন্য কোনো অ্যাপ (এমনকি একই প্যাকেজ নামের অ্যাপও) আপনার প্রোজেক্টের রিসোর্স এবং ব্যবহারকারীর ডেটা অ্যাক্সেস করতে পারবে না।
      • বিষয়টা এভাবে ভাবুন:
        • আপনার অ্যাপের সাইনিং কী হলো আপনার অ্যাপের 'দরজা'-র আসল চাবির মতো। এটিই অ্যাপের অভ্যন্তরীণ কার্যপ্রণালীতে প্রবেশের সুযোগ করে দেয়।
        • SHA-1 ফিঙ্গারপ্রিন্ট হলো একটি অনন্য কীকার্ড আইডির মতো, যা আপনার আসল চাবির সাথে সংযুক্ত থাকে। এটি একটি নির্দিষ্ট কোড যা সেই নির্দিষ্ট চাবিটিকে শনাক্ত করে।
        • OAuth 2.0 ক্লায়েন্ট আইডি হলো গুগলের কোনো নির্দিষ্ট রিসোর্স বা পরিষেবাতে (যেমন গুগল সাইন-ইন) প্রবেশের কোডের মতো।
        • OAuth ক্লায়েন্ট সেটআপের সময় যখন আপনি SHA-1 ফিঙ্গারপ্রিন্ট প্রদান করেন, তখন আপনি মূলত গুগলকে বলছেন: "শুধুমাত্র এই নির্দিষ্ট আইডি (SHA-1) যুক্ত কীকার্ডটিই এই অ্যাক্সেস কোড (ক্লায়েন্ট আইডি) খুলতে পারবে।" এটি নিশ্চিত করে যে শুধুমাত্র আপনার অ্যাপই সেই এন্ট্রি কোডের সাথে সংযুক্ত গুগল পরিষেবাগুলো অ্যাক্সেস করতে পারবে।

ওয়েব ক্লায়েন্টের জন্য, কনসোলে ক্লায়েন্টকে শনাক্ত করতে আপনি যে নামটি ব্যবহার করতে চান, শুধু সেটিই প্রয়োজন।

অ্যান্ড্রয়েড OAuth 2.0 ক্লায়েন্ট তৈরি করুন

  1. ক্লায়েন্ট পৃষ্ঠায় যান জিসিপি ক্লায়েন্ট
  2. ক্লায়েন্ট তৈরি করতে ক্লিক করুন GCP ক্লায়েন্ট তৈরি করুন
  3. অ্যাপ্লিকেশনের ধরনের জন্য অ্যান্ড্রয়েড নির্বাচন করুন
  4. আপনাকে আপনার অ্যাপের প্যাকেজ নামটি উল্লেখ করতে হবে।
  5. অ্যান্ড্রয়েড স্টুডিও থেকে আমাদের অ্যাপের SHA-1 সিগনেচারটি নিয়ে এখানে কপি/পেস্ট করতে হবে:
    1. অ্যান্ড্রয়েড স্টুডিওতে যান এবং টার্মিনালটি খুলুন।
    2. এই কমান্ডটি চালান: ম্যাক/লিনাক্স:
      keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
      
      উইন্ডোজ:
        keytool -list -v -keystore "C:\Users\USERNAME\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
      
      এই কমান্ডটি কীস্টোরের মধ্যে থাকা কোনো নির্দিষ্ট এন্ট্রির (এলিয়াস) বিবরণ তালিকাভুক্ত করার জন্য তৈরি করা হয়েছে।
      • -list : এই অপশনটি keytool-কে কীস্টোরের বিষয়বস্তু তালিকাভুক্ত করতে নির্দেশ দেয়।
      • -v : এই অপশনটি ভার্বোস আউটপুট চালু করে, যা এন্ট্রিটি সম্পর্কে আরও বিস্তারিত তথ্য প্রদান করে।
      • -keystore ~/.android/debug.keystore : এটি কীস্টোর ফাইলের পাথ নির্দিষ্ট করে।
      • -alias androiddebugkey : এটি সেই কী-টির অ্যালিয়াস (এন্ট্রি নাম) নির্দিষ্ট করে, যা আপনি পরীক্ষা করতে চান।
      • -storepass android : এটি কীস্টোর ফাইলের পাসওয়ার্ড প্রদান করে।
      • -keypass android : এটি নির্দিষ্ট অ্যালিয়াসের প্রাইভেট কী-এর পাসওয়ার্ড প্রদান করে।
    3. SHA-1 সিগনেচারের মানটি কপি করুন:
    SHA স্বাক্ষর
    1. গুগল ক্লাউড উইন্ডোতে ফিরে যান এবং SHA-1 সিগনেচার ভ্যালুটি পেস্ট করুন:
  6. আপনার স্ক্রিনটি এখন এইরকম দেখতে হবে, এবং আপনি Create-এ ক্লিক করতে পারেন: অ্যান্ড্রয়েড ক্লায়েন্টের বিবরণঅ্যান্ড্রয়েড ক্লায়েন্ট

ওয়েব OAuth 2.0 ক্লায়েন্ট তৈরি করুন

  1. একটি ওয়েব অ্যাপ্লিকেশন ক্লায়েন্ট আইডি তৈরি করতে, 'অ্যান্ড্রয়েড ক্লায়েন্ট তৈরি করুন' বিভাগ থেকে ধাপ ১-২ পুনরাবৃত্তি করুন এবং 'অ্যাপ্লিকেশন টাইপ'-এর জন্য 'ওয়েব অ্যাপ্লিকেশন' নির্বাচন করুন।
  2. ক্লায়েন্টকে একটি নাম দিন (এটিই হবে OAuth ক্লায়েন্ট):ওয়েব ক্লায়েন্টের বিবরণ
  3. তৈরি করতে ক্লিক করুনওয়েব ক্লায়েন্ট
  4. পপ-আপ উইন্ডো থেকে ক্লায়েন্ট আইডিটি কপি করে নিন, পরে আপনার এটি প্রয়োজন হবে। ক্লায়েন্ট আইডি কপি করুন

এখন যেহেতু আমাদের OAuth ক্লায়েন্টগুলো সব সেট আপ করা হয়ে গেছে, আমরা অ্যান্ড্রয়েড স্টুডিওতে ফিরে গিয়ে আমাদের 'সাইন ইন উইথ গুগল' অ্যান্ড্রয়েড অ্যাপটি তৈরি করতে পারি!

৪. অ্যান্ড্রয়েড ভার্চুয়াল ডিভাইস সেট আপ করুন

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

একটি অ্যান্ড্রয়েড ভার্চুয়াল ডিভাইস তৈরি করুন

  1. অ্যান্ড্রয়েড স্টুডিওতে, ডিভাইস ম্যানেজার খুলুন। ডিভাইস ম্যানেজার
  2. + বোতামে ক্লিক করুন > ভার্চুয়াল ডিভাইস তৈরি করুন ভার্চুয়াল ডিভাইস তৈরি করুন
  3. এখান থেকে আপনি আপনার প্রোজেক্টের জন্য প্রয়োজনীয় যেকোনো ডিভাইস যোগ করতে পারেন। এই কোডল্যাবের জন্য, 'মিডিয়াম ফোন' নির্বাচন করুন এবং তারপর 'নেক্সট'-এ ক্লিক করুন।মাঝারি ফোন
  4. এখন আপনি আপনার প্রোজেক্টের জন্য ডিভাইসটিকে একটি অনন্য নাম দিয়ে, ডিভাইসটিতে কোন অ্যান্ড্রয়েড সংস্করণ চলবে তা বেছে নিয়ে এবং আরও অনেক কিছু কনফিগার করতে পারেন। নিশ্চিত করুন যে API-টি API 36 "Baklava"; Android 16- এ সেট করা আছে, তারপর Finish-এ ক্লিক করুন। ভার্চুয়াল ডিভাইস কনফিগার করুন
  5. আপনি ডিভাইস ম্যানেজারে নতুন ডিভাইসটি দেখতে পাবেন। ডিভাইসটি চালু আছে কিনা তা যাচাই করতে, ক্লিক করুন।ডিভাইস চালান আপনি এইমাত্র তৈরি করা ডিভাইসটির পাশেডিভাইস ২ চালান
  6. ডিভাইসটি এখন চালু থাকার কথা! চলমান ডিভাইস

অ্যান্ড্রয়েড ভার্চুয়াল ডিভাইসে সাইন ইন করুন

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

  1. সেটিংসে যান:
    1. ভার্চুয়াল ডিভাইসের স্ক্রিনের মাঝখানে ক্লিক করুন এবং উপরের দিকে সোয়াইপ করুন
    ক্লিক এবং সোয়াইপ করুন
    1. সেটিংস অ্যাপটি খুঁজুন এবং তাতে ক্লিক করুন।
    সেটিংস অ্যাপ
  2. সেটিংসে গুগল- এ ক্লিক করুন গুগল পরিষেবা এবং পছন্দসমূহ
  3. আপনার গুগল অ্যাকাউন্টে সাইন ইন করতে সাইন ইন-এ ক্লিক করুন এবং নির্দেশাবলী অনুসরণ করুন। ডিভাইস সাইন ইন
  1. এখন আপনার ডিভাইসে সাইন ইন করা থাকা উচিত। ডিভাইস সাইন ইন করা হয়েছে

আপনার ভার্চুয়াল অ্যান্ড্রয়েড ডিভাইসটি এখন পরীক্ষার জন্য প্রস্তুত!

৫. নির্ভরতা যোগ করুন

সময়কাল ৫:০০

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

  • libs.googleid
  • libs.play.services.auth
  1. ফাইল > প্রজেক্ট স্ট্রাকচার-এ যান:প্রকল্পের কাঠামো
  2. তারপর Dependencies > app > '+' > Library Dependency- তে যাননির্ভরশীলতা
  3. এখন আমাদের লাইব্রেরিগুলো যোগ করতে হবে:
    1. সার্চ ডায়ালগ বক্সে googleid টাইপ করুন এবং সার্চ বাটনে ক্লিক করুন।
    2. এখানে কেবল একটিই এন্ট্রি থাকা উচিত, সেটি এবং উপলব্ধ সর্বোচ্চ সংস্করণটি নির্বাচন করুন (এই কোডল্যাবের সময় এটি ছিল ১.১.১)।
    3. ঠিক আছে ক্লিক করুন গুগল আইডি প্যাকেজ
    4. ধাপ ১-৩ পুনরাবৃত্তি করুন, কিন্তু এবার 'play-services-auth' লিখে অনুসন্ধান করুন এবং যে লাইনে গ্রুপ আইডি হিসেবে 'com.google.android.gms' এবং আর্টিফ্যাক্ট নেম হিসেবে 'play-services-auth' রয়েছে, সেই লাইনটি নির্বাচন করুন। প্লে সার্ভিসেস অথোরাইজেশন
  4. ঠিক আছে ক্লিক করুন সমাপ্ত নির্ভরতা

৬. বটম শীট ফ্লো

বটম শীট ফ্লো

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

সাইন-ইন অনুরোধ তৈরি করুন

  1. শুরু করার জন্য, MainActivity.kt থেকে Greeting() এবং GreetingPreview() ফাংশনগুলো সরিয়ে ফেলুন, আমাদের এগুলোর প্রয়োজন হবে না।
  2. এখন আমাদের নিশ্চিত করতে হবে যে এই প্রজেক্টের জন্য প্রয়োজনীয় প্যাকেজগুলো ইম্পোর্ট করা হয়েছে। ৩ নং লাইন থেকে শুরু করে বিদ্যমান ইম্পোর্ট স্টেটমেন্টগুলোর পরে নিম্নলিখিত import স্টেটমেন্টগুলো যোগ করুন:
    import android.content.ContentValues.TAG
    import android.content.Context
    import android.credentials.GetCredentialException
    import android.os.Build
    import android.util.Log
    import android.widget.Toast
    import androidx.annotation.RequiresApi
    import androidx.compose.foundation.clickable
    import androidx.compose.foundation.Image
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Column
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Surface
    import androidx.compose.runtime.rememberCoroutineScope
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.platform.LocalContext
    import androidx.compose.ui.res.painterResource
    import androidx.credentials.CredentialManager
    import androidx.credentials.exceptions.GetCredentialCancellationException
    import androidx.credentials.exceptions.GetCredentialCustomException
    import androidx.credentials.exceptions.NoCredentialException
    import androidx.credentials.GetCredentialRequest
    import com.google.android.libraries.identity.googleid.GetGoogleIdOption
    import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
    import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
    import java.security.SecureRandom
    import java.util.Base64
    import kotlinx.coroutines.CoroutineScope
    import androidx.compose.runtime.LaunchedEffect
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    
  3. এরপরে, বটম শীট রিকোয়েস্ট তৈরি করার জন্য আমাদের ফাংশনটি তৈরি করতে হবে। এই কোডটি MainActivity ক্লাসের নিচে পেস্ট করুন।
   //This line is not needed for the project to build, but you will see errors if it is not present.
   //This code will not work on Android versions < UpsideDownCake
   @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
   @Composable
    fun BottomSheet(webClientId: String) {
        val context = LocalContext.current

        // LaunchedEffect is used to run a suspend function when the composable is first launched.
        LaunchedEffect(Unit) {
            // Create a Google ID option with filtering by authorized accounts enabled.
            val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
                .setFilterByAuthorizedAccounts(true)
                .setServerClientId(webClientId)
                .setNonce(generateSecureRandomNonce())
                .build()

            // Create a credential request with the Google ID option.
            val request: GetCredentialRequest = GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOption)
                .build()

            // Attempt to sign in with the created request using an authorized account
            val e = signIn(request, context)
            // If the sign-in fails with NoCredentialException,  there are no authorized accounts.
            // In this case, we attempt to sign in again with filtering disabled.
            if (e is NoCredentialException) {
                val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder()
                    .setFilterByAuthorizedAccounts(false)
                    .setServerClientId(webClientId)
                    .setNonce(generateSecureRandomNonce())
                    .build()

                val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder()
                    .addCredentialOption(googleIdOptionFalse)
                    .build()

                //We will build out this function in a moment
                signIn(requestFalse, context)
            }
        }
    }

   //This function is used to generate a secure nonce to pass in with our request
   fun generateSecureRandomNonce(byteLength: Int = 32): String {
      val randomBytes = ByteArray(byteLength)
      SecureRandom.getInstanceStrong().nextBytes(randomBytes)
      return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes)
   }

চলুন এই কোডটি কী করছে তা বিশ্লেষণ করা যাক:

fun BottomSheet(webClientId: String) {...} : BottomSheet নামে একটি ফাংশন তৈরি করে যা webClientid নামে একটি স্ট্রিং আর্গুমেন্ট গ্রহণ করে।

  • val context = LocalContext.current : বর্তমান অ্যান্ড্রয়েড কনটেক্সট পুনরুদ্ধার করে। UI কম্পোনেন্ট চালু করা সহ বিভিন্ন অপারেশনের জন্য এটি প্রয়োজন।
  • LaunchedEffect(Unit) { ... } : LaunchedEffect হলো একটি Jetpack Compose কম্পোজেবল যা আপনাকে কম্পোজেবলটির লাইফসাইকেলের মধ্যে একটি সাসপেন্ড ফাংশন (এমন একটি ফাংশন যা তার এক্সিকিউশন থামাতে এবং পুনরায় শুরু করতে পারে) চালানোর সুযোগ দেয়। Key হিসেবে Unit থাকার অর্থ হলো, এই এফেক্টটি শুধুমাত্র একবারই চলবে, যখন কম্পোজেবলটি প্রথমবার চালু করা হবে।
    • val googleIdOption: GetGoogleIdOption = ... : একটি GetGoogleIdOption অবজেক্ট তৈরি করে। এই অবজেক্টটি গুগলের কাছে অনুরোধ করা ক্রেডেনশিয়ালের ধরন নির্ধারণ করে।
      • .Builder() : অপশনগুলো কনফিগার করার জন্য একটি বিল্ডার প্যাটার্ন ব্যবহার করা হয়।
      • .setFilterByAuthorizedAccounts(true) : এটি নির্দিষ্ট করে যে ব্যবহারকারীকে সমস্ত Google অ্যাকাউন্ট থেকে বেছে নেওয়ার অনুমতি দেওয়া হবে, নাকি শুধুমাত্র সেই অ্যাকাউন্টগুলো থেকে যারা ইতিমধ্যেই অ্যাপটিকে অনুমোদন দিয়েছে। এই ক্ষেত্রে, এটি 'true' সেট করা আছে, যার অর্থ হলো, যদি কোনো ক্রেডেনশিয়াল উপলব্ধ থাকে, তবে ব্যবহারকারীর পূর্বে এই অ্যাপটি ব্যবহারের জন্য অনুমোদিত ক্রেডেনশিয়াল ব্যবহার করেই অনুরোধটি করা হবে।
      • .setServerClientId(webClientId) : সার্ভার ক্লায়েন্ট আইডি সেট করে, যা আপনার অ্যাপের ব্যাকএন্ডের জন্য একটি অনন্য শনাক্তকারী। একটি আইডি টোকেন পেতে এটি প্রয়োজন।
      • .setNonce(generateSecureRandomNonce()) : রিপ্লে অ্যাটাক প্রতিরোধ করতে এবং আইডি টোকেনটি নির্দিষ্ট রিকোয়েস্টের সাথে যুক্ত আছে কিনা তা নিশ্চিত করতে একটি ননস (nonce) বা র‍্যান্ডম ভ্যালু সেট করে।
      • .build() : নির্দিষ্ট কনফিগারেশন সহ GetGoogleIdOption অবজেক্টটি তৈরি করে।
    • val request: GetCredentialRequest = ... : একটি GetCredentialRequest অবজেক্ট তৈরি করে। এই অবজেক্টটি সম্পূর্ণ ক্রেডেনশিয়াল অনুরোধটিকে ধারণ করে।
      • .Builder() : অনুরোধটি কনফিগার করার জন্য বিল্ডার প্যাটার্ন শুরু করে।
      • .addCredentialOption(googleIdOption) : অনুরোধে googleIdOption যোগ করে, যা নির্দেশ করে যে আমরা একটি গুগল আইডি টোকেন অনুরোধ করতে চাই।
      • .build() : GetCredentialRequest অবজেক্টটি তৈরি করে।
    • val e = signIn(request, context) : এটি তৈরি করা রিকোয়েস্ট এবং বর্তমান কনটেক্সট ব্যবহার করে ব্যবহারকারীকে সাইন ইন করার চেষ্টা করে। signIn ফাংশনের ফলাফল e ভেরিয়েবলে সংরক্ষিত হয়। এই ভেরিয়েবলটিতে হয় সফল ফলাফল অথবা একটি এক্সেপশন থাকবে।
    • if (e is NoCredentialException) { ... } : এটি একটি শর্তসাপেক্ষ যাচাই। যদি সাইন-ইন ফাংশনটি NoCredentialException-এর কারণে ব্যর্থ হয়, তার মানে হলো পূর্বে অনুমোদিত কোনো অ্যাকাউন্ট আর উপলব্ধ নেই।
      • val googleIdOptionFalse: GetGoogleIdOption = ... : যদি আগের signIn ব্যর্থ হয়, তাহলে এই অংশটি একটি নতুন GetGoogleIdOption তৈরি করে।
      • .setFilterByAuthorizedAccounts(false) : প্রথম বিকল্পটির সাথে এর মূল পার্থক্য হলো এটি। এটি অনুমোদিত অ্যাকাউন্টগুলোর ফিল্টারিং নিষ্ক্রিয় করে দেয়, যার অর্থ হলো ডিভাইসের যেকোনো গুগল অ্যাকাউন্ট ব্যবহার করে সাইন ইন করা যাবে।
      • val requestFalse: GetCredentialRequest = ... : googleIdOptionFalse ব্যবহার করে একটি নতুন GetCredentialRequest তৈরি করা হয়।
      • signIn(requestFalse, context) : এটি নতুন অনুরোধের মাধ্যমে ব্যবহারকারীকে সাইন ইন করার চেষ্টা করে, যার ফলে যেকোনো অ্যাকাউন্ট ব্যবহার করা যায়।

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

fun generateSecureRandomNonce(byteLength: Int = 32): String : এটি generateSecureRandomNonce নামের একটি ফাংশন সংজ্ঞায়িত করে। এটি byteLength (যার ডিফল্ট মান ৩২) নামের একটি ইন্টিজার আর্গুমেন্ট গ্রহণ করে, যা বাইটে ননসের কাঙ্ক্ষিত দৈর্ঘ্য নির্দিষ্ট করে। এটি একটি স্ট্রিং রিটার্ন করে, যা র‍্যান্ডম বাইটগুলোর Base64-এনকোডেড রূপ হবে।

  • val randomBytes = ByteArray(byteLength) : র‍্যান্ডম বাইটগুলো ধারণ করার জন্য নির্দিষ্ট byteLength দৈর্ঘ্যের একটি বাইট অ্যারে তৈরি করে।
  • SecureRandom.getInstanceStrong().nextBytes(randomBytes) :
    • SecureRandom.getInstanceStrong() : এটি একটি ক্রিপ্টোগ্রাফিকভাবে শক্তিশালী র‍্যান্ডম নম্বর জেনারেটর সংগ্রহ করে। এটি নিরাপত্তার জন্য অত্যন্ত গুরুত্বপূর্ণ, কারণ এটি নিশ্চিত করে যে উৎপন্ন সংখ্যাগুলো প্রকৃত অর্থেই র‍্যান্ডম এবং অনুমানযোগ্য নয়। এটি সিস্টেমে উপলব্ধ এনট্রপির সবচেয়ে শক্তিশালী উৎসটি ব্যবহার করে।
    • .nextBytes(randomBytes) : এটি SecureRandom ইনস্ট্যান্স দ্বারা তৈরি র‍্যান্ডম বাইট দিয়ে randomBytes অ্যারেটি পূরণ করে।
  • return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes) :
    • Base64.getUrlEncoder() : এটি এমন একটি Base64 এনকোডার নিয়ে আসে যা URL-এর জন্য নিরাপদ বর্ণমালা ব্যবহার করে (অর্থাৎ + এবং / এর পরিবর্তে - এবং _ ব্যবহার করে)। এটি গুরুত্বপূর্ণ কারণ এটি নিশ্চিত করে যে ফলাফলস্বরূপ প্রাপ্ত স্ট্রিংটি কোনো অতিরিক্ত এনকোডিং ছাড়াই URL-এ নিরাপদে ব্যবহার করা যাবে।
    • .withoutPadding() : এটি Base64 এনকোড করা স্ট্রিং থেকে যেকোনো প্যাডিং ক্যারেক্টার সরিয়ে দেয়। ননসটিকে কিছুটা ছোট এবং আরও সংহত করার জন্য এটি প্রায়শই কাম্য।
    • .encodeToString(randomBytes) : এটি randomBytes-কে একটি Base64 স্ট্রিং-এ এনকোড করে এবং সেটি রিটার্ন করে।

সংক্ষেপে, এই ফাংশনটি একটি নির্দিষ্ট দৈর্ঘ্যের ক্রিপ্টোগ্রাফিকভাবে শক্তিশালী র‍্যান্ডম ননস তৈরি করে, সেটিকে ইউআরএল-সেফ বেস৬৪ (URL-safe Base64) ব্যবহার করে এনকোড করে এবং ফলাফলস্বরূপ প্রাপ্ত স্ট্রিংটি ফেরত দেয়। নিরাপত্তা-সংবেদনশীল প্রেক্ষাপটে ব্যবহারের জন্য নিরাপদ ননস তৈরির ক্ষেত্রে এটি একটি প্রচলিত পদ্ধতি।

সাইন-ইন করার অনুরোধ করুন

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

এটি করার জন্য আপনি BottomSheet() ফাংশনের নিচে এই ফাংশনটি পেস্ট করতে পারেন।

//This code will not work on Android versions < UPSIDE_DOWN_CAKE when GetCredentialException is
//is thrown.
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
    val credentialManager = CredentialManager.create(context)
    val failureMessage = "Sign in failed!"
    var e: Exception? = null
    //using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
    //on the initial running of our app
    delay(250)
    try {
        // The getCredential is called to request a credential from Credential Manager.
        val result = credentialManager.getCredential(
            request = request,
            context = context,
        )
        Log.i(TAG, result.toString())

        Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
        Log.i(TAG, "(☞゚ヮ゚)☞  Sign in Successful!  ☜(゚ヮ゚☜)")

    } catch (e: GetCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Failure getting credentials", e)

    } catch (e: GoogleIdTokenParsingException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)

    } catch (e: NoCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": No credentials found", e)
        return e

    } catch (e: GetCredentialCustomException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with custom credential request", e)

    } catch (e: GetCredentialCancellationException) {
        Toast.makeText(context, ": Sign-in cancelled", Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
    }
    return e
}

এবার কোডটি এখানে কী করছে তা ব্যাখ্যা করা যাক:

suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? : এটি signIn নামের একটি সাসপেন্ড ফাংশন সংজ্ঞায়িত করে। এর মানে হলো, এটি মেইন থ্রেডকে ব্লক না করেই থামানো এবং পুনরায় চালু করা যায়। এটি একটি Exception? রিটার্ন করে, যা সাইন ইন সফল হলে null হবে অথবা সাইন ইন ব্যর্থ হলে নির্দিষ্ট এক্সেপশনটি রিটার্ন করবে।

এর জন্য দুটি প্যারামিটার প্রয়োজন:

  • request : একটি GetCredentialRequest অবজেক্ট, যাতে কোন ধরনের ক্রেডেনশিয়াল পুনরুদ্ধার করতে হবে তার কনফিগারেশন থাকে (যেমন, গুগল আইডি)।
  • context : সিস্টেমের সাথে যোগাযোগ করার জন্য প্রয়োজনীয় অ্যান্ড্রয়েড কনটেক্সট।

ফাংশনের মূল অংশের জন্য:

  • val credentialManager = CredentialManager.create(context) : CredentialManager-এর একটি ইনস্ট্যান্স তৈরি করে, যা Credential Manager API-এর সাথে যোগাযোগের প্রধান ইন্টারফেস। এভাবেই অ্যাপটি সাইন ইন প্রক্রিয়া শুরু করবে।
  • val failureMessage = "Sign in failed!" : সাইন-ইন ব্যর্থ হলে টোস্টে প্রদর্শিত হওয়ার জন্য একটি স্ট্রিং (failureMessage) নির্ধারণ করে।
  • var e: Exception? = null : এই লাইনটি 'e' নামের একটি ভেরিয়েবলকে ইনিশিয়ালাইজ করে, যা প্রসেস চলাকালীন ঘটতে পারে এমন যেকোনো এক্সেপশনকে null থেকে শুরু করে সংরক্ষণ করবে।
  • delay(250) : ২৫০-মিলিসেকেন্ডের একটি বিলম্ব যোগ করে। এটি একটি সম্ভাব্য সমস্যার সমাধান, যেখানে অ্যাপ শুরু হওয়ার সাথে সাথেই NoCredentialException থ্রো হতে পারে, বিশেষ করে যখন একটি BottomSheet ফ্লো ব্যবহার করা হয়। এটি সিস্টেমকে ক্রেডেনশিয়াল ম্যানেজার ইনিশিয়ালাইজ করার জন্য সময় দেয়।
  • try { ... } catch (e: Exception) { ... } : শক্তিশালী ত্রুটি ব্যবস্থাপনার জন্য একটি try-catch ব্লক ব্যবহার করা হয়। এটি নিশ্চিত করে যে সাইন-ইন প্রক্রিয়ার সময় কোনো ত্রুটি ঘটলে, অ্যাপটি ক্র্যাশ করবে না এবং এটি ব্যতিক্রমটিকে সুন্দরভাবে সামলাতে পারবে।
    • val result = credentialManager.getCredential(request = request, context = context) : এখানেই ক্রেডেনশিয়াল ম্যানেজার এপিআই-কে প্রকৃত কল করা হয় এবং ক্রেডেনশিয়াল পুনরুদ্ধারের প্রক্রিয়া শুরু হয়। এটি ইনপুট হিসেবে রিকোয়েস্ট এবং কনটেক্সট গ্রহণ করে এবং ব্যবহারকারীকে একটি ক্রেডেনশিয়াল বেছে নেওয়ার জন্য একটি ইউজার ইন্টারফেস (UI) দেখায়। সফল হলে, এটি নির্বাচিত ক্রেডেনশিয়াল সম্বলিত একটি ফলাফল ফেরত দেয়। এই অপারেশনের ফলাফল, GetCredentialResponse , result ভেরিয়েবলে সংরক্ষিত হয়।
    • Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show() : সাইন-ইন সফল হয়েছে তা নির্দেশ করে একটি সংক্ষিপ্ত টোস্ট বার্তা প্রদর্শন করে।
    • Log.i(TAG, "Sign in Successful!") : লগক্যাটে একটি মজার, সফলতার বার্তা লগ করে।
    • catch (e: GetCredentialException) : GetCredentialException টাইপের এক্সেপশনগুলো হ্যান্ডেল করে। ক্রেডেনশিয়াল ফেচিং প্রক্রিয়ার সময় ঘটতে পারে এমন বেশ কিছু নির্দিষ্ট এক্সেপশনের জন্য এটি একটি প্যারেন্ট ক্লাস।
    • catch (e: GoogleIdTokenParsingException) : গুগল আইডি টোকেন পার্স করার সময় কোনো ত্রুটি হলে যে ব্যতিক্রমগুলো ঘটে, তা পরিচালনা করে।
    • catch (e: NoCredentialException) : NoCredentialException হ্যান্ডেল করে, যা তখন থ্রো করা হয় যখন ব্যবহারকারীর কোনো ক্রেডেনশিয়াল উপলব্ধ থাকে না (যেমন, তারা কোনো ক্রেডেনশিয়াল সেভ করেনি, বা তাদের কোনো গুগল অ্যাকাউন্ট নেই)।
      • গুরুত্বপূর্ণভাবে, এই ফাংশনটি e তে সংরক্ষিত NoCredentialException এক্সেপশনটি রিটার্ন করে, যা কলারকে কোনো ক্রেডেনশিয়াল উপলব্ধ না থাকলে নির্দিষ্ট পরিস্থিতিটি সামাল দেওয়ার সুযোগ দেয়।
    • catch (e: GetCredentialCustomException) : ক্রেডেনশিয়াল প্রোভাইডার দ্বারা নিক্ষিপ্ত হতে পারে এমন কাস্টম এক্সেপশনগুলো হ্যান্ডেল করে।
    • catch (e: GetCredentialCancellationException) : GetCredentialCancellationException হ্যান্ডেল করে, যা ব্যবহারকারী সাইন-ইন প্রক্রিয়া বাতিল করলে থ্রো হয়।
    • Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show() : failureMessage ব্যবহার করে সাইন-ইন ব্যর্থ হয়েছে তা নির্দেশ করে একটি টোস্ট বার্তা প্রদর্শন করে।
    • Log.e(TAG, "", e) : এটি Log.e ব্যবহার করে অ্যান্ড্রয়েড লগক্যাটে এক্সেপশনটি লগ করে, যা ত্রুটির জন্য ব্যবহৃত হয়। এটি ডিবাগ করতে সাহায্য করার জন্য এক্সেপশনটির স্ট্যাকট্রেস অন্তর্ভুক্ত করবে। মজার জন্য এতে একটি রাগান্বিত ইমোটিকনও থাকে।
  • return e : কোনো এক্সেপশন ধরা পড়লে ফাংশনটি সেটি রিটার্ন করে, অথবা সাইন ইন সফল হলে null রিটার্ন করে।

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

অ্যাপে বটম শীট ফ্লোটি প্রয়োগ করুন।

এখন আমরা পূর্বে গুগল ক্লাউড কনসোল থেকে কপি করা আমাদের ওয়েব অ্যাপ্লিকেশন ক্লায়েন্ট আইডি এবং নিম্নলিখিত কোড ব্যবহার করে আমাদের MainActivity ক্লাসে BottomSheet ফ্লো ট্রিগার করার জন্য একটি কল সেট আপ করতে পারি:

class MainActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //replace with your own web client ID from Google Cloud Console
        val webClientId = "YOUR_CLIENT_ID_HERE"

        setContent {
            //ExampleTheme - this is derived from the name of the project not any added library
            //e.g. if this project was named "Testing" it would be generated as TestingTheme
            ExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
                ) {
                    //This will trigger on launch
                    BottomSheet(webClientId)
                }
            }
        }
    }
}

এখন আমরা আমাদের প্রজেক্টটি সেভ ( ফাইল > সেভ ) করে রান করতে পারি:

  1. রান বাটন টিপুন:রান প্রজেক্ট
  2. একবার আপনার অ্যাপটি এমুলেটরে চালু হলে, আপনি সাইন-ইন বটমশিটটি পপ আপ হতে দেখবেন। সাইন-ইন পরীক্ষা করার জন্য এগিয়ে যান এবং 'Continue'-তে ক্লিক করুন।নিচের শীট
  3. আপনি একটি টোস্ট মেসেজ দেখতে পাবেন, যেখানে লেখা থাকবে যে সাইন-ইন সফল হয়েছে! বটম শীট সাফল্য

৭. বাটন প্রবাহ

বাটন ফ্লো জিআইএফ

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

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

প্রকল্পে ব্র্যান্ড আইকন যোগ করুন

  1. পূর্ব-অনুমোদিত ব্র্যান্ড আইকনগুলির জিপ ফাইলটি এখান থেকে ডাউনলোড করুন।
  2. আপনার ডাউনলোড থেকে signin-assest.zip ফাইলটি আনকম্প্রেস করুন (এটি আপনার কম্পিউটারের অপারেটিং সিস্টেমের উপর নির্ভর করে ভিন্ন হতে পারে)। এখন আপনি signin-assets ফোল্ডারটি খুলে উপলব্ধ আইকনগুলো দেখতে পারেন। এই কোডল্যাবের জন্য, আমরা signin-assets/Android/png@2x/neutral/android_neutral_sq_SI@2x.png ব্যবহার করব।
  3. ফাইলটি কপি করুন
  4. অ্যান্ড্রয়েড স্টুডিও প্রজেক্টের res > drawable ফোল্ডারে রাইট - ক্লিক করে পেস্ট-এ ক্লিক করুন (এটি দেখার জন্য আপনাকে res ফোল্ডারটি এক্সপ্যান্ড করতে হতে পারে)।অঙ্কনযোগ্য
  5. একটি ডায়ালগ বক্স আসবে যেখানে ফাইলটির নাম পরিবর্তন করতে এবং এটি যে ডিরেক্টরিতে যুক্ত করা হবে তা নিশ্চিত করতে বলা হবে। অ্যাসেটটির নাম পরিবর্তন করে siwg_button.png রাখুন, তারপর OK- তে ক্লিক করুন। বাটন যোগ করা

বাটন ফ্লো কোড

এই কোডটি BottomSheet() ফাংশনের জন্য ব্যবহৃত signIn() ফাংশনটিই ব্যবহার করবে, কিন্তু GetGoogleIdOption এর পরিবর্তে GetSignInWithGoogleOption ব্যবহার করবে, কারণ এই ফ্লোটি সাইন-ইন অপশন দেখানোর জন্য ডিভাইসে সংরক্ষিত ক্রেডেনশিয়াল এবং পাস-কি ব্যবহার করে না। নিচে সেই কোডটি দেওয়া হলো যা আপনি BottomSheet() ফাংশনের নিচে পেস্ট করতে পারেন:

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    val onClick: () -> Unit = {
        val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
            .Builder(serverClientId = webClientId)
            .setNonce(generateSecureRandomNonce())
            .build()

        val request: GetCredentialRequest = GetCredentialRequest.Builder()
            .addCredentialOption(signInWithGoogleOption)
            .build()

        coroutineScope.launch {
            signIn(request, context)
        }
    }
    Image(
        painter = painterResource(id = R.drawable.siwg_button),
        contentDescription = "",
        modifier = Modifier
            .fillMaxSize()
            .clickable(enabled = true, onClick = onClick)
    )
}

কোডটি কী করছে তা বিশদভাবে বলতে গেলে:

fun ButtonUI(webClientId: String) : এটি ButtonUI নামের একটি ফাংশন ঘোষণা করে যা আর্গুমেন্ট হিসেবে একটি webClientId (আপনার গুগল ক্লাউড প্রজেক্টের ক্লায়েন্ট আইডি) গ্রহণ করে।

val context = LocalContext.current : বর্তমান অ্যান্ড্রয়েড কনটেক্সট পুনরুদ্ধার করে। UI কম্পোনেন্ট চালু করা সহ বিভিন্ন অপারেশনের জন্য এটি প্রয়োজন।

val coroutineScope = rememberCoroutineScope() : একটি কো-রুটিন স্কোপ তৈরি করে। এটি অ্যাসিঙ্ক্রোনাস টাস্ক পরিচালনা করতে ব্যবহৃত হয়, যা মেইন থ্রেডকে ব্লক না করে কোড চালানোর সুযোগ দেয়। rememberCoroutineScope () হলো Jetpack Compose-এর একটি কম্পোজেবল ফাংশন, যা এমন একটি স্কোপ প্রদান করে যা কম্পোজেবলটির লাইফসাইকেলের সাথে সংযুক্ত থাকে।

val onClick: () -> Unit = { ... } : এটি একটি ল্যাম্বডা ফাংশন তৈরি করে যা বাটনটি ক্লিক করা হলে এক্সিকিউট হবে। ল্যাম্বডা ফাংশনটি যা করবে:

  • val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder(serverClientId = webClientId).setNonce(generateSecureRandomNonce()).build() : এই অংশটি একটি GetSignInWithGoogleOption অবজেক্ট তৈরি করে। এই অবজেক্টটি "গুগল দিয়ে সাইন ইন" প্রক্রিয়ার জন্য প্যারামিটার নির্দিষ্ট করতে ব্যবহৃত হয়, এর জন্য webClientId এবং একটি nonce (নিরাপত্তার জন্য ব্যবহৃত একটি র‍্যান্ডম স্ট্রিং) প্রয়োজন হয়।
  • val request: GetCredentialRequest = GetCredentialRequest.Builder().addCredentialOption(signInWithGoogleOption).build() : এটি একটি GetCredentialRequest অবজেক্ট তৈরি করে। ক্রেডেনশিয়াল ম্যানেজার ব্যবহার করে ব্যবহারকারীর ক্রেডেনশিয়াল পেতে এই অনুরোধটি ব্যবহৃত হবে। "সাইন ইন উইথ গুগল" ক্রেডেনশিয়ালের জন্য অনুরোধ করতে, GetCredentialRequest পূর্বে তৈরি করা GetSignInWithGoogleOption একটি অপশন হিসেবে যুক্ত করে।
  • coroutineScope.launch { ... } : অ্যাসিঙ্ক্রোনাস অপারেশন (কোরুটিন ব্যবহার করে) পরিচালনার জন্য একটি CoroutineScope
    • signIn(request, context) : আমাদের পূর্বে সংজ্ঞায়িত signIn () ফাংশনটিকে কল করে।

Image(...) : এটি R.drawable.siwg_button ইমেজটি লোড করে এমন painterResource ব্যবহার করে একটি ইমেজ রেন্ডার করে।

  • Modifier.fillMaxSize().clickable(enabled = true, onClick = onClick) :
    • fillMaxSize() : ছবিটিকে উপলব্ধ স্থান জুড়ে প্রদর্শন করে।
    • clickable(enabled = true, onClick = onClick) : ছবিটিকে ক্লিকযোগ্য করে তোলে, এবং ক্লিক করা হলে, এটি পূর্বে সংজ্ঞায়িত onClick ল্যাম্বডা ফাংশনটি কার্যকর করে।

সংক্ষেপে, এই কোডটি একটি Jetpack Compose UI-তে একটি "Sign in with Google" বাটন সেট আপ করে। বাটনটিতে ক্লিক করা হলে, এটি ক্রেডেনশিয়াল ম্যানেজার চালু করার জন্য একটি ক্রেডেনশিয়াল রিকোয়েস্ট প্রস্তুত করে, যাতে ব্যবহারকারী তার গুগল অ্যাকাউন্ট দিয়ে সাইন ইন করতে পারেন।

এখন আমাদের ButtonUI() ফাংশনটি চালানোর জন্য MainActivity ক্লাসটি আপডেট করতে হবে:

class MainActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //replace with your own web client ID from Google Cloud Console
        val webClientId = "YOUR_CLIENT_ID_HERE"

        setContent {
            //ExampleTheme - this is derived from the name of the project not any added library
            //e.g. if this project was named "Testing" it would be generated as TestingTheme
            ExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
                ) {
                    Column(
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally

                    ) {
                        //This will trigger on launch
                        BottomSheet(webClientId)

                        //This requires the user to press the button
                        ButtonUI(webClientId)
                    }
                }
            }
        }
    }
}

এখন আমরা আমাদের প্রজেক্টটি সেভ ( ফাইল > সেভ ) করে রান করতে পারি:

  1. রান বাটন টিপুন:রান প্রজেক্ট
  2. এমুলেটরে অ্যাপটি চালু হলে বটমশিটটি দেখা যাবে। এটি বন্ধ করতে এর বাইরে ক্লিক করুন।এখানে ট্যাপ করুন
  3. এখন আপনি অ্যাপে আমাদের তৈরি করা বাটনটি দেখতে পাবেন। সাইন-ইন ডায়ালগটি দেখতে এটিতে ক্লিক করুন। সংলাপে স্বাক্ষর করুন
  4. সাইন ইন করতে আপনার অ্যাকাউন্টে ক্লিক করুন!

৮. উপসংহার

আপনি এই কোডল্যাবটি সম্পন্ন করেছেন! অ্যান্ড্রয়েডে 'সাইন ইন উইথ গুগল' সম্পর্কিত আরও তথ্য বা সাহায্যের জন্য, নিচের প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী বিভাগটি দেখুন:

প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী

সম্পূর্ণ MainActivity.kt কোড

আপনার বিবেচনার জন্য MainActivity.kt-এর সম্পূর্ণ কোডটি নিচে দেওয়া হলো:

package com.example.example

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.example.example.ui.theme.ExampleTheme
import android.content.ContentValues.TAG
import android.content.Context
import android.credentials.GetCredentialException
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.credentials.CredentialManager
import androidx.credentials.exceptions.GetCredentialCancellationException
import androidx.credentials.exceptions.GetCredentialCustomException
import androidx.credentials.exceptions.NoCredentialException
import androidx.credentials.GetCredentialRequest
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
import java.security.SecureRandom
import java.util.Base64
import kotlinx.coroutines.CoroutineScope
import androidx.compose.runtime.LaunchedEffect
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //replace with your own web client ID from Google Cloud Console
        val webClientId = "YOUR_CLIENT_ID_HERE"

        setContent {
            //ExampleTheme - this is derived from the name of the project not any added library
            //e.g. if this project was named "Testing" it would be generated as TestingTheme
            ExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
                ) {
                    Column(
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally

                    ) {
                        //This will trigger on launch
                        BottomSheet(webClientId)

                        //This requires the user to press the button
                        ButtonUI(webClientId)
                    }
                }
            }
        }
    }
}

//This line is not needed for the project to build, but you will see errors if it is not present.
//This code will not work on Android versions < UpsideDownCake
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
    fun BottomSheet(webClientId: String) {
        val context = LocalContext.current

        // LaunchedEffect is used to run a suspend function when the composable is first launched.
        LaunchedEffect(Unit) {
            // Create a Google ID option with filtering by authorized accounts enabled.
            val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
                .setFilterByAuthorizedAccounts(true)
                .setServerClientId(webClientId)
                .setNonce(generateSecureRandomNonce())
                .build()

            // Create a credential request with the Google ID option.
            val request: GetCredentialRequest = GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOption)
                .build()

            // Attempt to sign in with the created request using an authorized account
            val e = signIn(request, context)
            // If the sign-in fails with NoCredentialException,  there are no authorized accounts.
            // In this case, we attempt to sign in again with filtering disabled.
            if (e is NoCredentialException) {
                val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder()
                    .setFilterByAuthorizedAccounts(false)
                    .setServerClientId(webClientId)
                    .setNonce(generateSecureRandomNonce())
                    .build()

                val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder()
                    .addCredentialOption(googleIdOptionFalse)
                    .build()

                signIn(requestFalse, context)
            }
        }
    }

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    val onClick: () -> Unit = {
        val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
            .Builder(serverClientId = webClientId)
            .setNonce(generateSecureRandomNonce())
            .build()

        val request: GetCredentialRequest = GetCredentialRequest.Builder()
            .addCredentialOption(signInWithGoogleOption)
            .build()

        signIn(coroutineScope, request, context)
    }
    Image(
        painter = painterResource(id = R.drawable.siwg_button),
        contentDescription = "",
        modifier = Modifier
            .fillMaxSize()
            .clickable(enabled = true, onClick = onClick)
    )
}

fun generateSecureRandomNonce(byteLength: Int = 32): String {
    val randomBytes = ByteArray(byteLength)
    SecureRandom.getInstanceStrong().nextBytes(randomBytes)
    return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes)
}

//This code will not work on Android versions < UPSIDE_DOWN_CAKE when GetCredentialException is
//is thrown.
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
    val credentialManager = CredentialManager.create(context)
    val failureMessage = "Sign in failed!"
    var e: Exception? = null
    //using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
    //on the initial running of our app
    delay(250)
    try {
        // The getCredential is called to request a credential from Credential Manager.
        val result = credentialManager.getCredential(
            request = request,
            context = context,
        )
        Log.i(TAG, result.toString())

        Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
        Log.i(TAG, "(☞゚ヮ゚)☞  Sign in Successful!  ☜(゚ヮ゚☜)")

    } catch (e: GetCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Failure getting credentials", e)

    } catch (e: GoogleIdTokenParsingException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)

    } catch (e: NoCredentialException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": No credentials found", e)
        return e

    } catch (e: GetCredentialCustomException) {
        Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Issue with custom credential request", e)

    } catch (e: GetCredentialCancellationException) {
        Toast.makeText(context, ": Sign-in cancelled", Toast.LENGTH_SHORT).show()
        Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
    }
    return e
}