Utilizando Google Drive API con XamarinForms

Bienvenido a todos

Primero:

He utilizado esta muestra: "https://github.com/stevenchang0529/XamarinGoogleDriveRest" Es un proyecto para explicar cómo utilizar Google Drive API con XamrinForms con la creación de un archivo de texto, guardarlo y leer sus datos, por cierto, gracias a Steven Chang.

Funciona bien, pero pide iniciar sesión en la cuenta cada vez que quiero subir el archivo a Google Drive, ¿cómo puedo hacer que guarde los datos de inicio de sesión después de introducirlo por primera vez.

Segundo: ¿Cómo puedo hacer que el usuario elija su cuenta del popup "como el de la imagen de abajo" en lugar de utilizar el navegador nativo autenticador Web:

enter image description here

la clase ViewModel que usó en el proyecto:

public class MainViewModel
    {
        private string scope = "https://www.googleapis.com/auth/drive.file";
        private string clientId = "clientId here";
        private string redirectUrl = "url:/oauth2redirect";

        

        public ICommand OnGoogleDrive { get;  set; }
        public MainViewModel()
        {
            var auth = new OAuth2Authenticator(
              this.clientId,
              string.Empty,
              scope,
              new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
              new Uri(redirectUrl),
              new Uri("https://www.googleapis.com/oauth2/v4/token"),
              isUsingNativeUI: true);

            AuthenticatorHelper.OAuth2Authenticator = auth;
            auth.Completed +=async  (sender, e) =>
            {
                if (e.IsAuthenticated)
                {

                    var initializer = new GoogleAuthorizationCodeFlow.Initializer
                    {
                        ClientSecrets = new Google.Apis.Auth.OAuth2.ClientSecrets()
                        {
                            ClientId = clientId
                        }
                    };

                    
                    initializer.Scopes = new[] { scope };
                    initializer.DataStore = new FileDataStore("Google.Apis.Auth");
                    var flow = new GoogleAuthorizationCodeFlow(initializer);
                    var user = "DriveTest";

                    var token = new TokenResponse()
                    {
                         AccessToken=e.Account.Properties["access_token"],
                         ExpiresInSeconds=Convert.ToInt64( e.Account.Properties["expires_in"]),
                         RefreshToken=e.Account.Properties["refresh_token"],
                         Scope=e.Account.Properties["scope"],
                         TokenType=e.Account.Properties["token_type"]
                    };

                    UserCredential userCredential = new UserCredential(flow, user, token);
                    var driveService = new DriveService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = userCredential,
                        ApplicationName = "Quickstart",
                    });

                    //test google drive
                    DriveServiceHelper helper = new DriveServiceHelper(driveService);
                    var id = await helper.CreateFile();
                    await helper.SaveFile(id, "test", "test save content");
                    var content = await helper.ReadFile(id);
                }
            };

            auth.Error += (sender, e) =>
            {

            };

            this.OnGoogleDrive = new Command(() =>
              {
                  var presenter = new OAuthLoginPresenter();
                  presenter.Login(auth);
              });
        }
    }

    public static class AuthenticatorHelper
    {
        public static OAuth2Authenticator OAuth2Authenticator { get; set; }
    }

Pregunta hecha hace 3 años, 4 meses, 29 días - Por logicluminary


3 Respuestas:

  • Incluso con Xamarin Forms, No será 100% como tu imagen en el dispositivo iOS. Creo que esta es una manera de hacerlo similar tanto iOS como Android plataforma.

    Aquí hay un código de muestra para eso.

    Compartida

    página principal.xaml

    Este botón sólo es visible cuando el usuario no está conectado.

    ...
    
    ...
    

    InvertBooleanConverter.cs Este convertidor devuelve falso cuando IsLogedIn es Verdadero y verdadero cuando IsLogedIn es falso.

    public class InvertBooleanConverter : IValueConverter {
       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
       {
          return !(bool)value;
       }
    
       public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
       {
          return !(bool)value;
       }
    }
    

    Modelo GoogleUser.cs

     public class GoogleUser
     {
         public string Name { get; set; }
         public string Email { get; set; }
         public Uri Picture { get; set; }
     }
    

    ViewModel

    ...
    public class MainPageViewModel {
       private readonly IGoogleManager _googleManager;
    
       public DelegateCommand GoogleLoginCommand { get; set; }
    
       private bool _isLogedIn;
       public bool IsLogedIn
       {
          get { return _isLogedIn; }
          set { _isLogedIn = value; }
       }
    
       private GoogleUser _googleUser;
       public GoogleUser GoogleUser
       {
          get { return _googleUser; }
          set { _googleUser = value; }
       }
    
       ...
    
       public MainPageViewModel(IGoogleManager googleManager){ //Without IoC
           _googleManager = googleManager;
           IsLogedIn = false;
           GoogleLoginCommand = new DelegateCommand(GoogleLogin);         
       }
       
       private void GoogleLogin()
       {
           _googleManager.Login(OnLoginComplete);
       }
    
       private void OnLoginComplete(GoogleUser googleUser, string message)
       {
           if (googleUser != null){
               GoogleUser = googleUser;
               IsLogedIn = true;
               //Your code after login
           } else {
                //Error
           }
       }
    ...
    

    Interfaz para cada dispositivo

    public interface IGoogleManager
    {
       void Login(Action OnLoginComplete);
       void Logout();
    }
    

    iOS

    Your_PROJECT_NAMEios.

    1. Descargar el archivo GoogleService-info.plist y agregar su carpeta Xamarin.iOS.
    2. Abra el GoogleService-info.plist y copie el valor REVERSED_CLIENT_ID
    3. Abra el archivo info.plist, vaya a la pestaña Advanced, cree una nueva URL con un papel de editor y pegarla en el campo URL Scheme.

    AppDelegate.cs añadir este código a la clase AppDelegate

    ...
    var googleServiceDictionary = NSDictionary.FromFile("GoogleService-Info.plist");
    SignIn.SharedInstance.ClientID = googleServiceDictionary["CLIENT_ID"].ToString();
    ...
    

    GoogleManager.cs

    public class GoogleManager : NSObject, IGoogleManager, ISignInDelegate, ISignInUIDelegate
        {
            private Action _onLoginComplete;
            private UIViewController _viewController { get; set; }
    
            public GoogleManager()
            {
                SignIn.SharedInstance.UIDelegate = this;
                SignIn.SharedInstance.Delegate = this;
            }
    
            public void Login(Action OnLoginComplete)
            {
                _onLoginComplete = OnLoginComplete;
    
                var window = UIApplication.SharedApplication.KeyWindow;
                var vc = window.RootViewController;
                while (vc.PresentedViewController != null)
                {
                    vc = vc.PresentedViewController;
                }
    
                _viewController = vc;
    
                SignIn.SharedInstance.SignInUser();
            }
    
            public void Logout()
            {
                SignIn.SharedInstance.SignOutUser();
            }
    
            public void DidSignIn(SignIn signIn, Google.SignIn.GoogleUser user, NSError error)
            {
    
                if (user != null && error == null)
                    _onLoginComplete?.Invoke(new GoogleNativeLogin.Models.GoogleUser()
                    {
                        Name = user.Profile.Name,
                        Email = user.Profile.Email,
                        Picture = user.Profile.HasImage ? new Uri(user.Profile.GetImageUrl(500).ToString()) : new Uri(string.Empty)
                    }, string.Empty);
                else
                    _onLoginComplete?.Invoke(null, error.LocalizedDescription);
            }
    
            [Export("signIn:didDisconnectWithUser:withError:")]
            public void DidDisconnect(SignIn signIn, GoogleUser user, NSError error)
            {
                // When the user disconnects from app here
            }
    
            [Export("signInWillDispatch:error:")]
            public void WillDispatch(SignIn signIn, NSError error)
            {
                //myActivityIndicator.StopAnimating();
            }
    
            [Export("signIn:presentViewController:")]
            public void PresentViewController(SignIn signIn, UIViewController viewController)
            {
                _viewController?.PresentViewController(viewController, true, null);
            }
    
            [Export("signIn:dismissViewController:")]
            public void DismissViewController(SignIn signIn, UIViewController viewController)
            {
                _viewController?.DismissViewController(true, null);
            }
        }
    

    Android

    MainActivity.cs añadir este código a la MainActivity

    ...
    protected override void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent data)
    {
       base.OnActivityResult(requestCode, resultCode, data);
    
       if (requestCode == 1)
       {
           GoogleSignInResult result = Auth.GoogleSignInApi.GetSignInResultFromIntent(data);
            GoogleManager.Instance.OnAuthCompleted(result);
       }
    }
    ...
    

    GoogleManager

    public class GoogleManager : Java.Lang.Object, IGoogleManager, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
    {
       public Action _onLoginComplete;
       public static GoogleApiClient _googleApiClient { get; set; }
       public static GoogleManager Instance { get; private set; }
    
       public GoogleManager()
       {
          Instance = this;
          GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
                                                                 .RequestEmail()
                                                                 .Build();
    
          _googleApiClient = new 
    GoogleApiClient.Builder(((MainActivity)Forms.Context).ApplicationContext)
                    .AddConnectionCallbacks(this)
                    .AddOnConnectionFailedListener(this)
                    .AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
                    .AddScope(new Scope(Scopes.Profile))
                    .Build();
       }
    
       public void Login(Action onLoginComplete)
       {
          _onLoginComplete = onLoginComplete;
          Intent signInIntent = Auth.GoogleSignInApi.GetSignInIntent(_googleApiClient);
          ((MainActivity)Forms.Context).StartActivityForResult(signInIntent, 1);
              _googleApiClient.Connect();
       }
    
       public void Logout()
       {
            _googleApiClient.Disconnect();
       }
    
       public void OnAuthCompleted(GoogleSignInResult result)
       {
          if (result.IsSuccess)
          {
              GoogleSignInAccount accountt = result.SignInAccount;
              _onLoginComplete?.Invoke(new GoogleUser(){
                       Name = accountt.DisplayName,
                       Email = accountt.Email,
                       Picture = new Uri((accountt.PhotoUrl != null ? $ {accountt.PhotoUrl}" : $"//YOUR_PLACE_HOLDER_IMAGE.jpg//"))}, string.Empty);
            } else {
              _onLoginComplete?.Invoke(null, "An error occured!");
            }
        }
    
        public void OnConnected(Bundle connectionHint)
        {
    
        }
    
        public void OnConnectionSuspended(int cause)
        {
            _onLoginComplete?.Invoke(null, "Canceled!");
        }
    
        public void OnConnectionFailed(ConnectionResult result)
        {
            _onLoginComplete?.Invoke(null, result.ErrorMessage);
        }
    }
    

    Respondida el Dec 17, 2020 a las 17:39 - por quantumcoder

    Votos positivos: 0 | Votos negativos: 0

  • Para solucionar tu primer problema, es decir, guardar los datos de inicio de sesión una vez introducidos por primera vez, puedes utilizar la biblioteca de SecureStorage de Xamarin.Essentials para almacenar de forma segura y persistente las credenciales de inicio de sesión. Este es un ejemplo de cómo podrías modificar tu código para lograrlo:

    csharp
    using Xamarin.Essentials;
    
    public class MainViewModel
    {
        private string scope = "https://www.googleapis.com/auth/drive.file";
        private string clientId = "clientId here";
        private string redirectUrl = "url:/oauth2redirect";
    
        public ICommand OnGoogleDrive { get; set; }
    
        public MainViewModel()
        {
            var auth = new OAuth2Authenticator(
                  this.clientId,
                  string.Empty,
                  scope,
                  new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
                  new Uri(redirectUrl),
                  new Uri("https://www.googleapis.com/oauth2/v4/token"),
                  isUsingNativeUI: true);
    
            AuthenticatorHelper.OAuth2Authenticator = auth;
            auth.Completed += async (sender, e) =>
            {
                if (e.IsAuthenticated)
                {
                    var initializer = new GoogleAuthorizationCodeFlow.Initializer
                    {
                        ClientSecrets = new Google.Apis.Auth.OAuth2.ClientSecrets()
                        {
                            ClientId = clientId
                        }
                    };
    
                    initializer.Scopes = new[] { scope };
                    initializer.DataStore = new FileDataStore("Google.Apis.Auth");
                    var flow = new GoogleAuthorizationCodeFlow(initializer);
                    var user = "DriveTest";
    
                    var token = new TokenResponse()
                    {
                        AccessToken = e.Account.Properties["access_token"],
                        ExpiresInSeconds = Convert.ToInt64(e.Account.Properties["expires_in"]),
                        RefreshToken = e.Account.Properties["refresh_token"],
                        Scope = e.Account.Properties["scope"],
                        TokenType = e.Account.Properties["token_type"]
                    };
    
                    UserCredential userCredential = new UserCredential(flow, user, token);
    
                    // Almacenar credenciales de forma segura
                    await SecureStorage.SetAsync("AccessToken", token.AccessToken);
                    await SecureStorage.SetAsync("RefreshToken", token.RefreshToken);
    
                    var driveService = new DriveService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = userCredential,
                        ApplicationName = "Quickstart",
                    });
    
                    // Código restante...
                }
            };
    
            auth.Error += (sender, e) =>
            {
    
            };
    
            this.OnGoogleDrive = new Command(() =>
              {
                  var presenter = new OAuthLoginPresenter();
                  presenter.Login(auth);
              });
        }
    }
    
    public static class AuthenticatorHelper
    {
        public static OAuth2Authenticator OAuth2Authenticator { get; set; }
    }
    

    En este código, una vez se autentica el usuario correctamente, se almacenan de forma segura el Access Token y el Refresh Token utilizando SecureStorage de Xamarin.Essentials. Luego, puedes recuperar estos valores almacenados en futuras sesiones de la aplicación.

    En cuanto a tu segunda pregunta sobre cómo hacer que el usuario elija su cuenta desde un popup en lugar de utilizar el navegador nativo autenticador Web, desafortunadamente, actualmente no hay una forma directa de lograrlo con la biblioteca OAuth2Authenticator. Esta biblioteca utiliza el navegador nativo del dispositivo para la autenticación web.

    Podrías explorar la posibilidad de implementar tu propio control de selección de cuenta personalizado en lugar de utilizar OAuth2Authenticator, pero ten en cuenta que esto puede ser complicado y potencialmente inseguro si no se maneja correctamente la seguridad de las credenciales del usuario.

    Respondida el Dec 18, 2020 a las 17:39 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Claro para resolver tus preguntas:

    1. Almacenamiento de credenciales de inicio de sesión:

    La clase MainViewModel crea un objeto UserCredential que almacena el token de acceso y otra información de autenticación. Sin embargo, esta información no se almacena de forma persistente entre las ejecuciones de la aplicación. Para almacenar estas credenciales de forma persistente, puedes utilizar un almacén de datos seguro, como SecureStorage en las API de DependencyPlatform.

    Aquí tienes un ejemplo de cómo podrías hacerlo:

    csharp
    using Plugin.SecureStorage;
    using Plugin.SecureStorage.Exceptions;
    
    public class MainViewModel : BaseViewModel
    {
        private const string AccessTokenKey = "accessToken";
        private const string ExpiresInSecondsKey = "expiresIn";
        private const string RefreshTokenKey = "refreshToken";
        private const string ScopeKey = "scope";
        private const string TokenTypeKey = "tokenType";
    
        private SecureStorageOptions options = new SecureStorageOptions
        {
            CrossPlatformStoragePath = "com.mypackage.securestorage",
            StorageTimeout = TimeSpan.FromSeconds(10)
        };
    
        public MainViewModel()
        {
            // ...
            this.OnGoogleLogin = new Command(async () =>
            {
                // ...
                var token = new TokenResponse()
                {
                    AccessToken = e.Account.Properties["access_token"],
                    ExpiresInSeconds = long.Parse(e.Account.Properties["expires_in"], CultureInfo.InvariantCulture),
                    RefreshToken = e.Account.Properties["refresh_token"],
                    Scope = e.Account.Properties["scope"],
                     TokenType = e.Account.Properties["token_type"]
                };
    
                await SecureStorage.SaveAsync(AccessTokenName, token.AccessToken, options);
                await SecureStorage.SaveAsync(ExpiresInSecondsName, token.ExpiresInSeconds.ToString(), options);
                await SecureStorage.SaveAsync(RefreshTokenName, token.RefreshToken, options);
                await SecureStorage.SaveAsync(ScopeKeyName, token.Scope, options);
                await SecureStorage.SaveAsync(TokenTypeName, token.AccessToken, options);
    
                // ...
            });
        }
    
        public async Task RestoreCredentialsAsync()
        {
            try
            {
                string accessToken = await SecureStorage.GetAsync(AccessTokenName, options);
                long expiresIn = long.Parse(await SecureStorage.GetAsync(ExpiresInSecondsName, options));
                string refreshToken = await SecureStorage.GetAsync(RefreshTokenName, options);
                string scope = await SecureStorage.GetAsync(ScopeKeyName, options);
                string tokenType = await SecureStorage.GetAsync(TokenTypeName, options);
    
                var token = new TokenResponse()
                {
                    AccessToken = accessToken,
                    ExpiresInSeconds = expiresIn,
                    RefreshToken = refreshToken,
                    Scope = scope,
                     TokenType = tokenType
                };
    
                // ...
            }
            catch (SecureStorageException e)
            {
                // Handle exception
            }
        }
    }
    

    2. Selección de cuenta del selector:

    La API de autenticación de Google no permite actualmente la selección de cuentas desde un selector en las aplicaciones móviles. Solo se permite iniciar sesión en la cuenta por defecto o mostrar un mensaje de error si el usuario no ha iniciado sesión.

    Sin embargo, puedes utilizar una solución alternativa para mostrar un selector de cuentas personalizado utilizando las siguientes API:

    Google Sign In for Mobile (recomendada): Permite a los usuarios seleccionar una cuenta desde un selector y obtener un token de acceso. Google Identity Services (obsoleta): Proporcionaba una funcionalidad similar, pero ya no está disponible.

    Aquí tienes un ejemplo de cómo utilizar Google Sign In for Mobile:

    csharp
    using Google.SignIn;
    using Google.SignIn.Common;
    
    public class MainViewModel : BaseViewModel
    {
        private GoogleSignInClient signInClient;
    
        public MainViewModel()
        {
            // ...
            this.OnGoogleLogin = new Command(async () =>
            {
                signInClient = GoogleSignInClientBuilder.Create(GoogleSignInOptions.Default).Build();
                var signInResult = await signInClient.SignInAsync();
    
                if (signInResult.HasException)
                {
                    // Handle exception
                }
    
                if (signInResult.IsSuccessful)
                {
                    // Get the token and other information
                    var token = signInResult.AuthCode;
                    // ...
                }
            });
        }
    }
    

    Ten en cuenta que necesitarás configurar correctamente los permisos de la API de Google y el identificador de cliente para que funcione correctamente.

    Respondida el Dec 18, 2020 a las 17:43 - por Gemini

    Votos positivos: 0 | Votos negativos: 0