Egy .NET fejlesztő vagy, aki mindig is szeretett volna mobil alkalmazást készíteni? Vagy talán már próbálkozott natív mobilalkalmazások készítésével Android vagy iOS segítségével, de nem tetszettek a nyelvek? Nos, akkor szerencsés vagy! A .NET világot megáldotta a Xamarin; egy olyan eszközkészlet, amellyel mobilalkalmazásokat készíthet Androidra, iOS-re és Windowsra a Visual Studióban.

A Xamarin két fő ízben létezik: Xamarin platform (Xamarin.iOS és Xamarin.Android) és Xamarin.Forms. A Xamarin.Forms segítségével az üzleti logika és a felhasználói felület túlnyomó többsége egyetlen megosztott projektben írható, amely teljesen működő alkalmazásokat készít mindhárom iOS, Android és Windows (UWP) operációs rendszeren. A Xamarin platform ezzel szemben nagyon is platformspecifikus munka, és inkább hasonlít a natív alkalmazások írásához, de C#-val.

Ebben a bemutatóban a Xamarin platformot és az Android operációs rendszer Xamarin.Android néven ismert eszközkészletét fogom közelebbről megvizsgálni. Az általános cél az, hogy lehetővé tegye egy egyszerű natív Android-alkalmazás létrehozását, alapvető felhasználói hitelesítéssel együtt.

A Visual Studio és a környezet beállítása

A követéshez szükséged lesz a Visual Studio egy példányára, valamint a ‘Mobilfejlesztés .NET-tel’ című munkarészre. Ezt a funkciót vagy a Visual Studio első telepítésekor engedélyezheti, vagy a ‘Tools -> Get Tools and Features…’ menüpontból érheti el:

Az alkalmazás tesztelése és futtatása során választhat, hogy ezt a fejlesztőgépén futó Android emulátorral, vagy közvetlenül egy meglévő Android készülékhez csatlakozva teszi. Itt nincs helyes választás, és a különböző fejlesztők különböző formátumok használatát részesítik előnyben. Ha az előbbi lehetőséget választja, akkor a munkaterhelés kiválasztása után biztosítania kell, hogy a jobb oldali ablakban (“Telepítés részletei”) az Intel Hardware Accelerated Execution Manager és a Google Android Emulator jelölőnégyzetek be legyenek jelölve (ahogy fent látható).

A Visual Studio Android környezetének ellenőrzése

Hogy ellenőrizze, hogy minden megfelelően települt és helyesen lett-e konfigurálva, menjen az ‘Eszközök -> Beállítások -> Xamarin -> Android beállítások’ menüpontba, és ellenőrizze, hogy a Java Development Kit Location és az Android SDK Location elérési útvonalak érvényesek-e (i. m.azaz van-e zöld pipa):

Ha valamelyik hiányzik, akkor manuálisan kell telepítenie a Java Development Kitet, illetve az Android SDK-t.

Hozzon létre egy Xamarin alkalmazást

Kezdje egy új projekt létrehozásával, és válassza ki az ‘Android App (Xamarin)’ master sablont (az Android menüben található). A következő oldalon a ‘Single View App’ opciót kell kiválasztania, mivel ez egy nagyszerű kezdő sablon a munkához.

A minimális Android verzióval kapcsolatban ez az Ön, mint fejlesztő személyes döntése. Itt kompromisszumot kell kötni az újabb verziók legújabb és legjobb API-funkciók elérhetősége és a régebbi verziókkal rendelkező ügyfelek támogatása között. A döntés meghozatalának megkönnyítése érdekében a Google meglehetősen rendszeresen közzéteszi a platformverzió-elosztási adatokat, amelyeket a Distribution dashboard részeként gyűjt. Személyes preferenciám az 5.0 vagy a 6.0 között van, attól függően, hogy ez egy nyilvános fogyasztásra szánt alkalmazás vagy egy megrendelt alkalmazás csak vállalati telefonokra (azaz valószínűleg rendelkeznek a legújabb frissítésekkel); ebben a példában az utóbbit választottam. Vegye figyelembe, hogy ez a verzió eltér a Target Android Version-től, és ezt mindig a legfrissebb SDK verzióra kell állítani, mivel az ennél kisebb verzió nem lesz elfogadva a Google Play áruházban.

Amint ezt létrehozta, már csak a szükséges NuGet csomagok importálása van hátra. Ehhez a bemutatóhoz szükséged lesz:

  • Xamarin.OpenId.AppAuth.Android – A felhasználó hitelesítéséhez az OpenID Connect szabványt fogod használni (az OAuth 2.0 továbbfejlesztése). A legegyszerűbben a specifikációt betartó klienskódot az AppAuth kliens SDK for Android segítségével lehet megvalósítani, és hasznos módon a Xamarin portolt egy csomagot ebből a funkcionalitásból, amely elérhető az Ön számára.
  • System.IdentityModel.Tokens.Jwt – A hitelesítési módszer itt JSON Web Tokeneket használ. Az ezekből a tokenekből szükséges adatok kinyeréséhez erre a csomagra lesz szükséged.

Kiegészítésképpen erre a két csomagra is szükséged lesz, mivel az AppAuth támaszkodik rájuk:

  • Xamarin.Android.Support.v4
  • Xamarin.Android.Support.CustomTabs

Ismerkedjünk meg a Xamarin projekttel

Ha még sosem dolgoztunk Androiddal, az egyik legfontosabb alapelv, amit meg kell ismernünk, az Activity fogalma. A Tevékenységek a felhasználói felület megjelenítésére használt komponensek; a legalapvetőbb formában úgy gondolhatsz a Tevékenységekre, mint egy alkalmazás lapjaira, amelyek között a felhasználó navigálhat. Egy Activity-t a kódban egy osztály reprezentál, azonban az ASP.NET-ben egy oldalhoz hasonlóan egy XML-alapú elrendezési fájlt (egy .axml fájlt) is társíthat (és szinte mindig társít is) a megjeleníthető dizájn érdekében. Minden új projekt létrehoz egy “MainActivity.cs” és egy “activity_main.axml” fájlt, amely az alkalmazás megnyitásakor az első futtatandó Activity (azaz oldal). Ez az osztály Activity attribútumán belül a MainLauncher = true tulajdonság felhasználásával bármely más Activityre módosítható.

A források úgy vannak kialakítva, hogy saját könyvtárukban kezelhetők legyenek, és egy kissé szigorú elnevezési konvenciót követnek. Erősen ajánlom, hogy a lehető legtöbb erőforrásodat ebben a könyvtárban tárold, mivel ez leegyszerűsíti ezeknek a változóknak az újrafelhasználását a folyamatos fejlesztés során. A Resources könyvtár ‘values’ könyvtárában találod a speciális célú fájlokat:

  • Strings.xml – Itt található az összes felhasználóval szembenéző karakterlánc. Ezt különösen fontos használni, mivel lehetővé teszi a karakterláncok lokalizálását a globális közönség számára.
  • Styles.xml – Itt találod a tervezési objektumok formázásához szükséges attribútumokat; gondolj rá úgy, mint egy CSS fájlra.
  • Colors.xml – A hely, ahol a leggyakrabban használt színekre való hivatkozásokat tárolhatod a formázás részeként.
  • Dimens.xml – Ahogy a neve is sugallja, itt határozza meg az alkalmazás elrendezésének meghatározott méreteit.

A többi nevezetes könyvtár nem követ semmilyen elnevezési konvenciót a fájljaikra vonatkozóan, de bizonyos fájltípusokat tartalmazniuk kell:

  • Layout – Az .axml fájlok tárolásának helye. Ezek a fájlok definiálják a teljes tevékenység elrendezéseket, a programozottan generált és feltöltött elrendezési komponenseket, a párbeszédpanelek (figyelmeztető ablakok) elrendezéseit stb.
  • Menu – Itt találhatók a menük és elemeik definíciói. Ezek olyan .xml fájlok, amelyeknek menu a gyökéreleme és item gyermekelemei vannak, amelyek közül group elemekkel lehet csoportosítani. A leggyakrabban előforduló menük a túlfolyó menü (a három függőleges pont gombból) vagy a navigációs menü (a home ‘hamburger’ gombból).
  • Mipmap – Ahol olyan képeket kell definiálni, amelyeket a képernyő sűrűségétől függően kell méretezni, azaz amelyekre más alkalmazások hivatkoznak, és amelyeket nem használnak belsőleg. Az alkalmazás ikonja a leggyakoribb tartalom, amit a mipmap könyvtárakba helyezne.
  • Drawable – Nem szabványos az alapértelmezett projektsablonban, de létrehozható saját maga. Itt tárolja az alkalmazáson belül használni kívánt képeket/rajzolható tartalmakat, pl. a kezdőképernyőt, az egyéni haladási sáv terveit, az alkalmazáson belül használt ikonokat stb.

Végül, ha bármilyen nyers tartalomfájlja van, amelyet az alkalmazás részeként szeretne használni (pl. egy szöveg- vagy betűtípusfájl), akkor az Assets könyvtárban kell elhelyezni. Assets-ként ezek a fájlok az alkalmazással együtt kerülnek telepítésre, hogy Ön az Asset Managerrel hozzáférhessen.

Az Assets-ről és az erőforrásokról, valamint azok használatáról többet megtudhat, az újonnan létrehozott projekt minden könyvtárában praktikus “About” szöveges fájlok találhatók.

Add User Authentication to Xamarin with OpenID Connect

A legtöbb alkalmazás manapság megköveteli a felhasználói azonosítás valamilyen formáját, hogy a fejlesztő testre szabott élményeket kínálhasson, és viszont lehetővé tegye a felhasználó számára az adatainak megőrzését az eszközök/installációk között. Ezen kívül ott van még a hozzáférés-szabályozás kérdése, amely hasznos lehet a felhasználók egy részhalmazának engedélyezéséhez extra funkciókhoz. Természetesen ez elég fáradságos feladat lehet, különösen akkor, ha alkalmazások készítésével foglalkozik, és minden egyes alkalmazáshoz új rendszerre van szüksége. Szerencsére az Okta segítségével néhány perc alatt beállíthatsz egy alkalmazást, és akkor minden nehéz munkát elvégeztél magad helyett! Az Okta szolgáltatás megfelel az OAuth 2.0 szabványnak és tanúsított OpenID Connect szolgáltató, így tökéletesen együttműködik az AppAuth kliens SDK-val minden hitelesítési és engedélyezési igénye kielégítésére.

Az Okta alkalmazás beállítása

Először is létre kell hoznia egy új alkalmazást az Okta fiókjában ehhez a projekthez. Ha még nincs, nagyon egyszerű létrehozni egy új, örökké ingyenes fejlesztői fiókot.

Ha ez megtörtént, és bejelentkeztél a fejlesztői műszerfalra, jegyezd fel az Org URL-t, mert később szükségünk lesz rá:

A műszerfalról menj az “Alkalmazások” fülre, majd onnan az “Alkalmazás hozzáadása”. Ön egy natív Android alkalmazást hoz létre, ezért a legjobb, ha a ‘Native’ platformsablont választja.

Erről az oldalról adjon nevet az alkalmazásnak, minden mást pedig hagyjon alapértelmezettnek. A mentés után jegyezze meg a bejelentkezési átirányítási URI-ket és az ügyfél-azonosítót, mivel ezekre a következőkben szüksége lesz.

Azért, hogy a jövőben könnyedén használhassa ezt a három értéket az Okta-tól, javaslom, hogy a hitelesítési logikának egy új könyvtáron belül egy saját statikus osztályba helyezze őket:

public static class Configuration{ public const string ClientId = "{yourClientId}"; public const string LoginRedirectUri = "{yourRedirectUri}"; public const string OrgUrl = "https://{yourOktaDomain}";}

Hitelesítési szolgáltató létrehozása

Lépjünk túl a boilerplate kódon. Be kell konfigurálnod az alkalmazást, hogy tájékoztasd az átirányítási URI-sémádról. A séma a bejelentkezési átirányítási URI (a szokásos fordított tartománynév formájában) az elérési útvonal nélkül. Például a fenti képernyőkép alapján az én sémám ‘com.oktapreview.dev-123456’ lenne.

A legegyszerűbb módja ennek, ha az alábbi szándékszűrő részletet beilleszted a megoldásod Properties mappájában lévő AndroidManifest.xml fájlodba. A Application tagen belül illessze be az alábbi XML-t, és a séma értékét változtassa meg a sajátjára:

<activity android:name="net.openid.appauth.RedirectUriReceiverActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="{yourRedirectRriScheme}" /> </intent-filter></activity>

Az engedélyezés eredményének modelljét is meg kell határoznia egy egyszerű osztállyal. Bár nem fogom használni az összes értéket, amit alább írtam a kódon belül, megmutatom, hogyan kell feltölteni őket, hogy utána használhassa őket. Mivel ez az alkalmazásod modelljének része, hozz létre egy Models nevű mappát, és adj hozzá egy AuthorizationResult.cs osztályt benne. Ezután add hozzá a következő kódot:

public class AuthorizationResult{ public string AccessToken { get; set; } public string IdentityToken { get; set; } public bool IsAuthorized { get; set; } public string RefreshToken { get; set; } public string Scope { get; set; }}

Az Android nem rendelkezik globális állapottal, amellyel dolgozhatnál, ezért ha egyszerű értékeket szeretnél átadni a tevékenységek között, akkor a legjobb módja ennek a Intent objektum Extras funkciója. A Intent egy előre definiált osztály az Androidban, és egy másik alapvető fogalom, amelyet meg kell értenünk. Ez az elvégzendő művelet absztrakciója (azaz a “szándékaid”), és ahhoz, hogy egy másik tevékenységhez navigálj előre, létre kell hoznod ennek az osztálynak egy példányát azzal, hogy melyik tevékenységhez “szándékozol” menni. Az Extrák tulajdonságai a Intent objektumon tulajdonképpen csak egy szótár, amely az objektum értékeihez tartozó kulcsokat tartalmazza, és Put és tipizált Get metódusokkal érhető el.

Míg ezek a metódusok viszonylag egyértelművé és egyszerűvé teszik a használatot, én személy szerint szeretem, ha minden hozzáférés a saját osztályukon belül marad (pontosabban egy bővítmény osztályon belül), hogy jobban elkülönüljenek a problémák. Ez rendkívül hasznos, mivel nem kell osztályokon átívelően hozzáférni a kulcsokhoz, és garantálhatjuk a típusbiztonságot ezen értékek be- és kinyerésekor. A jogosultsági szolgáltatódban a következőket szeretnéd: tárolni a AuthState-t, ellenőrizni, hogy ott van-e, és visszaadni, ha igen. Hozzon létre egy új mappát Extensions néven a megoldás gyökerében. Ezután adjunk hozzá egy új osztályt IntentExtensions.cs néven. Legyen az osztály public és static, majd adjuk hozzá a következő kódot az osztályon belül:

public const string AuthStateKey = "authState";public static string GetAuthStateExtra(this Intent intent){ return intent.GetStringExtra(AuthStateKey);}public static bool HasAuthStateExtra(this Intent intent){ return intent.HasExtra(AuthStateKey);}public static void PutAuthStateExtra(this Intent intent, AuthState authState){ intent.PutExtra(AuthStateKey, authState.JsonSerializeString());}public static bool TryGetAuthStateFromExtra(this Intent intent, out AuthState authState){ authState = null; if (!intent.HasAuthStateExtra()) { return false; } try { authState = AuthState.JsonDeserialize(intent.GetAuthStateExtra()); } catch (JSONException) { return false; } return authState != null;}

Most itt az ideje definiálni az engedélyezési szolgáltatót, AuthorizationProvider.cs a Authentication mappában, amelyet korábban a Configuration.cs osztályhoz készítettünk. Először is távolítsuk el az összes using utasítást az újonnan létrehozott osztályon belül, majd deklaráljuk a konfigurációt static readonly változóként:

private static readonly Uri ConfigurationEndpoint = Uri.Parse($"{Configuration.OrgUrl}/oauth2/default/.well-known/openid-configuration");private static readonly string Scopes = new { "openid", "profile", "email", "offline_access" };

A konfigurációs végpont az OpenID-ben szabványos, mint a felfedező végpont, hogy mindent megtaláljunk, ami támogatott. Megjegyzés itt írtam, hogy ez az “alapértelmezett” szolgáltató nevet használja. Ha más szolgáltatóval rendelkezik, akkor ezt itt meg kell változtatnia. Azt is vegye figyelembe, hogy ez az Uri osztály Android.Net ízét használja, és nem a System változatot – az előbbit kell hozzáadni a felhasználáshoz, vagy teljesen minősíteni a típust, hogy ez működjön. A Scopes változó, mint minden más OpenID rendszerben, meghatározza, hogy mihez vagyunk jogosultak hozzáférni.

A következőkben deklarálnunk kell a tagváltozóinkat:

private AuthorizationRequest authorizationRequest;private PendingIntent completedIntent;private AuthState authorizationState;private readonly AuthorizationService authorizationService;private readonly Context context;private readonly TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();

Egy gyors magyarázat mindegyikről:

  • A felhatalmazási kérelem és a befejezett szándék a felhatalmazási hívás végrehajtásához létrehozott paraméterek. Itt globális változóként írtam őket, hogy minimalizáljam a különböző metódusokba történő paraméterátadás mennyiségét.
  • A authorizationState változó, ahogy a neve is mutatja, az aktuálisan adott engedélyezési állapotot határozza meg.
  • A authorizationService változó az engedélyezési szolgáltatás egy példányát tartalmazza.
  • A context változó itt a hívó tevékenységé, így szükség esetén hivatkozhatunk rá.
  • Végül a taskCompletionSource lehetővé teszi, hogy mindezeket a hívásokat aszinkron módon végezzük el, majd a befejezés után visszatérjünk.

Most meg kell határoznunk ezen readonly változók értékeit a konstruktorunkban, és deklarálnunk kell azokat a nyilvános metódusokat, amelyeket a kódunk meghív:

public AuthorizationProvider(Context context){ authorizationService = new AuthorizationService(context); this.context = context;}public async Task<AuthorizationResult> SignInAsync(){ ...}public void NotifyCallback(Intent intent){ ...}

A SignInAsync metódus, ahogy azt már sejteni lehetett, egy aszinkron módszer a felhasználó bejelentkezésére. Ez a korábban írt AuthorizationResult osztályt adja vissza. A NotifyCallback viszont arra szolgál, hogy a hívó tevékenység, miután visszatért a külső bejelentkezési oldalról, visszahívja az engedélyezési szolgáltatót, és tudassa vele, hogy elkészült. A bejelentkezési módszert több alprogramra bontottam, és így néz ki:

AuthorizationServiceConfiguration serviceConfiguration = await AuthorizationServiceConfiguration.FetchFromUrlAsync(ConfigurationEndpoint);BuildAuthorizationRequest(serviceConfiguration);BuildCompletedIntent(serviceConfiguration);return await RequestAuthorization();

Ezzel meghatároztad a szolgáltatás konfigurációját, felépítetted az engedélyezési kérelmet és a szándékot, hogy az engedélyezés befejezése után hívd fel, majd várd az engedélyezési kérelmet. Az engedélyezési kérelem felépítése a következőképpen történik:

private void BuildAuthorizationRequest(AuthorizationServiceConfiguration serviceConfiguration){ AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder( serviceConfiguration, Configuration.ClientId, ResponseTypeValues.Code, Uri.Parse(Configuration.LoginRedirectUri)); builder.SetScope(string.Join(" ", Scopes)); authorizationRequest = builder.Build();}

Ez a metódus feladata, hogy absztrahálja a AuthorizationRequest.Builder munkát és létrehozza a kérelmet. Ezután meg kell építened a Intent-t a művelet befejezése után:

private void BuildCompletedIntent(AuthorizationServiceConfiguration serviceConfiguration){ Intent intent = new Intent(context, typeof(MainActivity)); intent.PutAuthStateExtra(new AuthState()); completedIntent = PendingIntent.GetActivity(context, authorizationRequest.GetHashCode(), intent, 0);}

A “szándék”, amit itt végre akarsz hajtani, az, hogy visszatérj a MainActivity-ba egy új AuthState csatolásával. Utoljára ebben az áramlásban a kérés végrehajtásával kell foglalkozni:

private async Task<AuthorizationResult> RequestAuthorization(){ authorizationService.PerformAuthorizationRequest(authorizationRequest, completedIntent); await taskCompletionSource.Task; return new AuthorizationResult() { AccessToken = authorizationState?.AccessToken, IdentityToken = authorizationState?.IdToken, IsAuthorized = authorizationState?.IsAuthorized ?? false, RefreshToken = authorizationState?.RefreshToken, Scope = authorizationState?.Scope };}

Mivel a PerformAuthorizationRequest szinkron és üresen tér vissza, a kód a taskCompletionSource tagra vár, tudván, hogy az mindig csak akkor lesz beállítva, ha a válasz lekérése megtörtént. Ugyanezen a ponton tudod, hogy az authorization state feltöltődik (ha minden sikerült), és így az AuthorizationResult részeként visszaadhatod az értékeiket.

A második nyilvános NotifyCallback metódus, ahogy már említettem, az, amit a MainActivity osztálynak vissza kell hívnia, miután a fenti completedIntent futtatása megtörtént. Ebben a metódusban ellenőrizni akarod a választ, megfelelően frissíteni az állapotot, és ha sikeres, végrehajtani a tokencsere-kérést:

if (!intent.TryGetAuthStateFromExtra(out authorizationState)){ taskCompletionSource.SetResult(false); return;}AuthorizationResponse authorizationResponse = AuthorizationResponse.FromIntent(intent);AuthorizationException authorizationException = AuthorizationException.FromIntent(intent);authorizationState.Update(authorizationResponse, authorizationException);if (authorizationException != null){ taskCompletionSource.SetResult(false); return;}authorizationService.PerformTokenRequest(authorizationResponse.CreateTokenExchangeRequest(), ReceivedTokenResponse);

Itt láthatod, hogy a fail esetekben a taskCompletionSource eredményét false-ra állítottam, és ez feloldja a fenti RequestAuthorization metódus blokkolását. Továbbá a PerformTokenRequest metódus egy ReceivedTokenResponse delegátust vesz fel, amelyet a befejezés után lefuttat. Ez a metódus a következő:

private void ReceivedTokenResponse(TokenResponse tokenResponse, AuthorizationException authorizationException){ authorizationState.Update(tokenResponse, authorizationException); taskCompletionSource.SetResult(true);}

Az összes szükséges jogosultsági adattal rendelkeznie kell, így megfelelően frissítheti az állapotot (ahol megtalálja a bejelentkezési metódusból visszaadandó értékeket), és az eredményt beállíthatja a taskCompletionSource feladat blokkolásának feloldására.

A hitelesítés implementálása a Xamarin felületébe

Tisztításként, ha úgy kívánja, nyugodtan távolítsa el a “Floating Action Bar” (más néven “FAB”) minden hivatkozását a fő tevékenység osztály/axml fájlokban, mivel ezek ebben a szakaszban feleslegesen felduzzadtak.

Hogy a felhasználó bejelentkezhessen, most ezt a funkciót kell implementálnia a felhasználói felületbe. Tekintettel az alapértelmezett dizájnra, a legintuitívabb módja ennek az lenne, ha a jobb felső sarokban lévő túlfolyómenühöz hozzáadna egy elemet. Ezt a ‘Resources -> menu’ mappában található menu_main.xml fájl szerkesztésével érheti el, és a menu tag gyermekeként hozzáadhatja ezt az elemet:

<item android:id="@+id/action_signin" android:orderInCategory="100" android:title="@string/action_signin" app:showAsAction="never" />

Ezzel a kóddal létrehozott egy új opciót a string resources fájlban definiálandó címmel. Mint korábban említettük, Androidban a legjobb gyakorlat, hogy az összes felhasználóval szembenéző szöveget a string resources fájlban helyezzük el. Ezen adatok deklarálásához szerkesszük meg a strings.xml fájlt a ‘Resources -> values’ mappában, és adjuk hozzá ezeket a sorokat:

<string name="action_signin">Sign In</string><string name="welcome_message_format">Hi, %1$s!</string>

Nem csak a ‘Sign In’ gombhoz deklaráltam itt egy stringet, hanem fentebb hozzáadtam egy stringet a felhasználónak szóló üdvözlő üzenethez is, miután bejelentkezett. A C# kódnak megfelelő this string would be “Hi, {0}!”`, ahol a helyőrző string típusú.

Megjegyzendő, hogy a Resource-alapú fájlok minden frissítésénél a Resource.designer.cs osztály automatikusan újratermelődik az egyes létrehozott objektumok új azonosítóival, amelyekre a kódban hivatkozhatunk. Ha ez nem működik egy adott fájl esetében, akkor válassza ki azt a Solution Explorerben, és nézze meg a Properties ablakot. Győződjön meg róla, hogy a CustomTool tulajdonság MSBuild:UpdateGeneratedFiles értékre van állítva, mivel ez valószínűleg hiányzik, és megakadályozza, hogy a tervezőfájl erőforrásként ismerje fel.

Ezután adjon hozzá egy ProgressBar-t a meglévő activity_main.axml elrendezési fájlhoz:

<ProgressBar android:id="@+id/signin_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:paddingBottom="12dp" android:visibility="gone" />

Ez a ProgressBar (vagy pörgettyű, ahogy az eset áll) olyan azonosítóval rendelkezik, amelyre kóddal hivatkozhat, és úgy van beállítva, hogy a képernyő közepe körül helyezkedik el. A láthatóság egyelőre nincs beállítva, de amint az engedélyezési kódod fut, láthatóvá teheted, és tájékoztathatod a felhasználót, hogy az alkalmazás foglalt.

Most már van egy gombod a hitelesítés megnyitásához és egy előrehaladási pörgettyűd, amely tájékoztatja a felhasználót, hogy az alkalmazás foglalt, itt az ideje használni őket. A MainActivity osztályodon belül add hozzá a következő tulajdonságot a Activity attribútumhoz (az osztályfejléc felett):

LaunchMode = LaunchMode.SingleTask

Ez a tulajdonság biztosítja, hogy a MainActivity osztályból csak egy példány létezik, és nem nyitsz folyamatosan újakat. Miután ezt megtetted, adj hozzá egy statikus tagváltozót a fentebb írt AuthorizationProvder osztályhoz, és hozz létre egy példányt a OnCreate metódus meglévő felülbírálásán belül. Vegye figyelembe, hogy ezt a metóduson belüli meglévő kód után kell megtennie:

private static AuthorizationProvider authorizationProvider;protected override void OnCreate(Bundle savedInstanceState){ ... authorizationProvider = new AuthorizationProvider(this);}

A következő lépésben írja felül a OnNewIntent metódust. Ennek az a célja, hogy amikor egy új szándékot hozunk létre ebből az osztályból (azaz amikor a külső bejelentkező ablak visszatér), akkor a AuthorizationProvider-ből hívjuk meg a NotifyCallback metódust. Ebben is szerepel egy gyors ellenőrzés, hogy megbizonyosodjunk arról, hogy ez az elvárt folyamat:

protected override void OnNewIntent(Intent intent){ base.OnNewIntent(intent); if (intent != null && intent.Data.Path.Equals("/callback", StringComparison.OrdinalIgnoreCase)) { authorizationProvider.NotifyCallback(intent); }}

Most hozzáadjuk a kódot a hozzáadott menüpont mögé. A OnOptionsItemSelected metódus meglévő felülírásához adjunk hozzá egy if utasítást egy új metódus hívásával, amely a következőképpen kezeli a bejelentkezési folyamatot:

if (id == Resource.Id.action_signin){ OnSignInAttempted(); return true;}

Ez az új metódus azzal kezdi, hogy láthatóvá teszi a pillanatokkal ezelőtt hozzáadott ProgressBar-t. Ha bármelyik komponenst szeretnénk lekérni a layout fájlból, használjuk a FindViewById általános metódust, és argumentumként adjuk meg a komponens azonosítóját. Ezt követően hívja meg a SignInAsync metódust, és várja meg annak eredményét. A hívás visszatérése után az eredményt ellenőrzi, hogy az engedélyezett-e. Ha ez az engedélyezés valamilyen okból nem sikerült, hibaüzenet jelenik meg, és a progress spinner ismét eltűnik. A siker esetének részletezését egyelőre elhagyom, mivel ilyenkor még szükség van valahová el kell jutni:

private async void OnSignInAttempted(){ ProgressBar signInProgress = FindViewById<ProgressBar>(Resource.Id.signin_progress); signInProgress.Visibility = ViewStates.Visible; AuthorizationResult authorizationResult = await authorizationProvider.SignInAsync(); if (!string.IsNullOrWhiteSpace(authorizationResult.AccessToken) && authorizationResult.IsAuthorized) { // Placeholder: Success case } else { // Display an error to the user AlertDialog authorizationErrorDialog = new AlertDialog.Builder(this) .SetTitle("Error!") .SetMessage("We were unable to authorize you.") .Create(); authorizationErrorDialog.Show(); signInProgress.Visibility = ViewStates.Gone; }}

Ha a felhasználó hitelesítése megtörtént, akkor át kell irányítanod a következő oldalra a tapasztalatodban. Ha emlékszel a korábbiakra, minden oldalt egy Activity képvisel, ezért most egy újat kell létrehoznod.

Kezdésként a ‘Resources -> layout’ mappán belül létre kell hoznod az új activity layout fájlt “activity_dashboard.axml”. Ezt legegyszerűbben úgy tehetjük meg, hogy a kontextusmenüben az Új elem… opciót választjuk, és kiválasztjuk az “Android elrendezés” sablont. Az új elrendezési fájlon belül adjunk hozzá egy egyszerű TextView komponenst a szöveg megjelenítéséhez, például így:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:id="@+id/welcome_message" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /></LinearLayout>

Ebben a részletben van egy TextView komponens hivatkozható azonosítóval, amely az oldal közepén van középre helyezve, és amelyből egy üdvözlő üzenetet fog megjeleníteni. Ezután hozzon létre egy megfelelő ‘DashboardActivity’ aktivitás osztályt a megoldáskeresőben a projektből a kontextusmenüben található ‘New Item…’ opcióval és válassza ki az ‘Activity’ sablont. Ahhoz, hogy ezt az osztályt összekapcsolja az elrendezési fájljával, meg kell hívnia a SetContentView függvényt a létrehozott OnCreate() metódusban (az örökölt alapmetódus meghívása alatt):

SetContentView(Resource.Layout.activity_dashboard);

Az üdvözlő üzenet személyre szabásához át kell adnia a felhasználó nevét az új aktivitásnak. Ha emlékszel korábban a legjobb módja ennek az Extrák a szándékon volt, és létrehoztál egy extensions osztályt ennek kezelésére. A korábbiakhoz hasonlóan adj hozzá új metódusokat a ‘Put’ és ‘Get’ egy ‘name’ extrához a fent létrehozott IntentExtensions.cs fájlban:

private const string NameKey = "Name";public static void PutNameExtra(this Intent intent, string name){ intent.PutExtra(NameKey, name);}public static string GetNameExtra(this Intent intent){ return intent.GetStringExtra(NameKey);}

Most ezt a kiterjesztett funkciót használva, a SetContentView hívása után, amit a OnCreate() metódusban tettél, kérd ki a felhasználó nevét, és állítsd be a TextView komponens szövegét ennek megfelelően:

string name = Intent.GetNameExtra();TextView welcomeMessage = FindViewById<TextView>(Resource.Id.welcome_message);welcomeMessage.Text = Resources.GetString(Resource.String.welcome_message_format, name);

Ez a kivonat a TextView példány lekérdezésekor annak értékét az üdvözlő üzenetre állítja be, amelynek az Android Resources megfelelője a string.Format() segítségével jön létre.

Ezzel elkészült a műszerfal aktivitása, és most meg kell hívnia azt. A OnSignInAttempted metódusból nyitva hagyott siker esetének helyőrzőjében ezt a következő kód hozzáadásával érheted el:

JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();JwtSecurityToken jsonToken = tokenHandler.ReadJwtToken(authorizationResult.IdentityToken);IEnumerable<Claim> claims = jsonToken?.Payload?.Claims;string name = claims?.FirstOrDefault(x => x.Type == "name")?.Value;Intent dashboardIntent = new Intent(this, typeof(DashboardActivity));dashboardIntent.PutNameExtra(name);StartActivity(dashboardIntent);Finish();

Az első blokk beolvassa a tokent és lekérdezi a felhasználó nevét (ha létezik). A másodikban egy új Intent jön létre a dashboard-aktivitáshoz, a felhasználó neve a fent definiált bővítési módszered segítségével tárolódik ebben a Intent-ben, majd elindul a tevékenység (azaz navigálsz hozzá). Annak érdekében, hogy a felhasználó ne tudjon utólag visszanavigálni erre az oldalra, a kód a Finish() metódus meghívásával zárul.

Futtassa az Android alkalmazást

Most itt az ideje, hogy elindítsa az alkalmazást a kiválasztott eszközzel!

Ha az emulátor segítségével végez hibakeresést, ez olyan egyszerűnek kell lennie, mint az F5 lenyomása, amely először megnyitja és betölti az emulátort, majd a kódot telepíti rá. Mellékesen megjegyezzük, hogy az emulátort nem kell bezárni a futtatási/hibakeresési kísérletek között, mivel csak az alkalmazást kell újra telepítenie.

Ha olyan eszközzel debuggolsz, amelyet még nem használtál erre a célra, akkor először be kell állítanod az eszközt. Ehhez engedélyezze a fejlesztői beállításokat, és az új menüben kapcsolja be az “Android hibakeresés” opciót a “Hibakeresés” fejléc alatt. Ezt követően csak csatlakoztassa az eszközt, fogadja el a készülékén megjelenő párbeszédpanelt, amely megerősíti, hogy ez egy biztonságos hibakeresési kapcsolat, és máris telepítheti és futtathatja az alkalmazását az F5 segítségével. Ne feledje, hogy a fizikai eszközök magasabb prioritást élveznek, mint az emulátor, és a csatlakoztatáskor átváltanak rá, mint alapértelmezett hibakeresési lehetőségre.

Amint az alkalmazás telepítése és betöltése megtörtént, az alapértelmezett egyoldalas sablon fogadja Önt. Nyissa meg a jobb felső sarokban lévő menüt a bejelentkezéshez, és miután megadta az adatait, vissza kell térnie erre az oldalra a forgó előrehaladási sávval, mielőtt automatikusan a műszerfal oldalra kerülne az üdvözlő üzenettel:

Tudjon meg többet a Xamarinról, az OpenID Connectről és a biztonságos hitelesítésről

Ha követte az összes fenti lépést, akkor már rendelkezik egy alapvető Android-alkalmazással, amely a Xamarin segítségével készült.Androiddal, teljesen működő, OpenID-n és az Okta szolgáltatáson alapuló felhasználói hitelesítéssel. Innen már könnyedén kibővítheti a műszerfal-aktivitást a saját funkcióinak megvalósításához.

Az ebben a bejegyzésben szereplő kód teljes egészében megtekinthető a GitHub-oldalunkon.

Ha ez a bemutató meghozta a kedvét a Xamarin fejlesztéshez, és szeretne többet megtudni, akkor erősen ajánlom, hogy nézze meg ezeket a többi nagyszerű cikket:

  • Add Identity Management to Your Android App
  • Add Authentication to Your Xamarin.Forms App with OpenID Connect
  • Build an App for iOS and Android with Xamarin

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.