Totally reworking login and acess token management. Also adding a check for access token expire date.

This commit is contained in:
Tristan Roux
2019-02-07 22:56:54 +01:00
parent 8550d3dff2
commit 427ef01062
9 changed files with 250 additions and 112 deletions

View File

@@ -29,7 +29,6 @@ using MusicApp.Resources.Portable_Class;
using MusicApp.Resources.values;
using Newtonsoft.Json.Linq;
using SQLite;
using Square.OkHttp;
using Square.Picasso;
using System;
using System.Collections.Generic;
@@ -40,10 +39,10 @@ using System.Threading.Tasks;
using YoutubeExplode;
using CursorLoader = Android.Support.V4.Content.CursorLoader;
using Fragment = Android.Support.V4.App.Fragment;
using ICallback = Square.OkHttp.ICallback;
using Playlist = MusicApp.Resources.Portable_Class.Playlist;
using Request = Square.OkHttp.Request;
using SearchView = Android.Support.V7.Widget.SearchView;
using Toolbar = Android.Support.V7.Widget.Toolbar;
using TransportType = Android.Net.TransportType;
namespace MusicApp
@@ -52,18 +51,15 @@ namespace MusicApp
[IntentFilter(new[] {Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataHost = "www.youtube.com", DataMimeType = "text/*")]
[IntentFilter(new[] {Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataHost = "m.youtube.com", DataMimeType = "text/plain")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "audio/*", "application/ogg", "application/x-ogg", "application/itunes" })]
public class MainActivity : AppCompatActivity, GoogleApiClient.IOnConnectionFailedListener, ICallback, IResultCallback, IMenuItemOnActionExpandListener, View.IOnFocusChangeListener, ISessionManagerListener
public class MainActivity : AppCompatActivity, GoogleApiClient.IOnConnectionFailedListener, IResultCallback, IMenuItemOnActionExpandListener, View.IOnFocusChangeListener, ISessionManagerListener
{
public static MainActivity instance;
public new static int Theme = 1;
public static int dialogTheme;
public Android.Support.V7.Widget.Toolbar ToolBar;
public bool NoToolbarMenu = false;
public IMenu menu;
public SwipeRefreshLayout contentRefresh;
public bool usePager;
public bool HomeDetails = false;
public bool Paused = false;
public bool prepared = false;
@@ -75,9 +71,9 @@ namespace MusicApp
private const string versionURI = "https://raw.githubusercontent.com/AnonymusRaccoon/MusicApp/master/MusicApp/Assets/Version.txt";
public static GoogleSignInAccount account;
public GoogleApiClient googleClient;
private bool canAsk;
private Intent AskIntent;
public bool waitingForYoutube;
private DateTime NextRefreshDate;
private bool? PermissionGot;
public bool ResumeKiller;
@@ -98,8 +94,7 @@ namespace MusicApp
int statusHeight = Resources.GetDimensionPixelSize(Resources.GetIdentifier("status_bar_height", "dimen", "android"));
FindViewById(Resource.Id.contentLayout).SetPadding(0, statusHeight, 0, 0);
ToolBar = (Android.Support.V7.Widget.Toolbar)FindViewById(Resource.Id.toolbar);
SetSupportActionBar(ToolBar);
SetSupportActionBar(FindViewById<Toolbar>(Resource.Id.toolbar));
SupportActionBar.Title = "MusicApp";
((CoordinatorLayout.LayoutParams)FindViewById(Resource.Id.contentLayout).LayoutParameters).TopMargin = -Resources.GetDimensionPixelSize(Resources.GetIdentifier("status_bar_height", "dimen", "android"));
@@ -196,27 +191,27 @@ namespace MusicApp
{
waitingForYoutube = true;
if(account == null)
account = GoogleSignIn.GetLastSignedInAccount(this);
if (account != null)
{
CreateYoutube();
return;
}
if(googleClient == null)
{
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestIdToken(GetString(Resource.String.clientID))
.RequestServerAuthCode(GetString(Resource.String.clientID))
.RequestEmail()
.RequestScopes(new Scope(YouTubeService.Scope.Youtube))
.Build();
googleClient = new GoogleApiClient.Builder(this)
GoogleApiClient googleClient = new GoogleApiClient.Builder(this)
.AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
.Build();
googleClient.Connect();
}
if (!skipSilentLog)
{
@@ -233,7 +228,7 @@ namespace MusicApp
}
else if (silentLog != null)
{
this.canAsk = canAsk;
AskIntent = Auth.GoogleSignInApi.GetSignInIntent(googleClient);
silentLog.SetResultCallback(this);
}
else if (canAsk)
@@ -241,10 +236,8 @@ namespace MusicApp
ResumeKiller = true;
StartActivityForResult(Auth.GoogleSignInApi.GetSignInIntent(googleClient), 1598);
}
return;
}
if (canAsk)
else if (canAsk)
{
ResumeKiller = true;
StartActivityForResult(Auth.GoogleSignInApi.GetSignInIntent(googleClient), 1598);
@@ -278,41 +271,73 @@ namespace MusicApp
RunOnUiThread(() => { Picasso.With(this).Load(account.PhotoUrl).Transform(new CircleTransformation()).Into(new AccountTarget()); });
CreateYoutube();
}
else if(canAsk)
else if(AskIntent != null)
{
ResumeKiller = true;
StartActivityForResult(Auth.GoogleSignInApi.GetSignInIntent(googleClient), 1598);
StartActivityForResult(AskIntent, 1598);
AskIntent = null;
}
}
void CreateYoutube()
public async void CreateYoutube()
{
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormEncodingBuilder()
.Add("grant_type", "authorization_code")
.Add("client_id", GetString(Resource.String.clientID))
.Add("client_secret", GetString(Resource.String.clientSecret))
.Add("redirect_uri", "")
.Add("code", account.ServerAuthCode)
.Add("id_token", account.IdToken)
.Build();
Request request = new Request.Builder()
.Url("https://www.googleapis.com/oauth2/v4/token")
.Post(body)
.Build();
client.NewCall(request).Enqueue(this);
ISharedPreferences prefManager = PreferenceManager.GetDefaultSharedPreferences(this);
string refreshToken = prefManager.GetString("refresh-token", null);
Console.WriteLine("&Current refresh token: " + refreshToken);
Console.WriteLine("&Requesting access token");
}
public void OnResponse(Square.OkHttp.Response response)
//This method do not return refresh-token if the app has already been aprouved by google for this user, should force request
if (refreshToken == null)
{
string jsonFile = response.Body().String();
Console.WriteLine(jsonFile);
if (jsonFile.Contains("error"))
Console.WriteLine("&Getting refresh-token and creating a youtube service");
Console.WriteLine("&Code = " + account.ServerAuthCode);
if(account.ServerAuthCode == null)
{
Login(true);
return;
}
JToken json = JObject.Parse(jsonFile);
Dictionary<string, string> fields = new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "client_id", GetString(Resource.String.clientID) },
{ "client_secret", GetString(Resource.String.clientSecret) },
{ "redirect_uri", "" },
{ "code", account.ServerAuthCode },
{ "id_token", account.IdToken },
};
var items = from kvp in fields
select kvp.Key + "=" + kvp.Value;
string content = string.Join("&", items);
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/oauth2/v4/token");
request.Host = "www.googleapis.com";
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = content.Length;
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(content);
}
Console.WriteLine("&Content: " + content);
HttpWebResponse resp = (HttpWebResponse)await request.GetResponseAsync();
string response;
using (StreamReader responseReader = new StreamReader(request.GetResponse().GetResponseStream()))
{
response = responseReader.ReadToEnd();
Console.WriteLine("&" + response);
}
JToken json = JObject.Parse(response);
GoogleCredential credential = GoogleCredential.FromAccessToken((string)json.SelectToken("access_token"));
YoutubeEngine.youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
@@ -320,7 +345,85 @@ namespace MusicApp
ApplicationName = "MusicApp"
});
Console.WriteLine("&Youtube created");
refreshToken = (string)json.SelectToken("refresh_token");
if(refreshToken != null)
{
ISharedPreferencesEditor editor = prefManager.Edit();
editor.PutString("refresh-token", refreshToken);
editor.Apply();
}
int expireIn = (int)json.SelectToken("expires_in");
NextRefreshDate = DateTime.UtcNow.AddSeconds(expireIn - 30); //Should refresh a bit before the expiration of the acess token
}
catch (WebException ex)
{
Console.WriteLine("&Refresh token get error: " + ex.Message);
UnknowError(new Action(() => { CreateYoutube(); }));
}
}
else if(account != null)
{
Console.WriteLine("&Getting a new access-token and creating a youtube service");
Dictionary<string, string> fields = new Dictionary<string, string>
{
{ "refresh_token", refreshToken },
{ "client_id", GetString(Resource.String.clientID) },
{ "client_secret", GetString(Resource.String.clientSecret) },
{ "grant_type", "refresh_token" },
};
var items = from kvp in fields
select kvp.Key + "=" + kvp.Value;
string content = string.Join("&", items);
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/oauth2/v4/token");
request.Host = "www.googleapis.com";
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = content.Length;
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(content);
}
Console.WriteLine("&Content: " + content);
HttpWebResponse resp = (HttpWebResponse)await request.GetResponseAsync();
string response;
using (StreamReader responseReader = new StreamReader(request.GetResponse().GetResponseStream()))
{
response = responseReader.ReadToEnd();
Console.WriteLine("&" + response);
}
JToken json = JObject.Parse(response);
GoogleCredential credential = GoogleCredential.FromAccessToken((string)json.SelectToken("access_token"));
YoutubeEngine.youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MusicApp"
});
int expireIn = (int)json.SelectToken("expires_in");
NextRefreshDate = DateTime.UtcNow.AddSeconds(expireIn - 30); //Should refresh a bit before the expiration of the acess token
}
catch (WebException ex)
{
Console.WriteLine("&New access token get error: " + ex.Message);
UnknowError(new Action(() => { CreateYoutube(); }));
}
}
else
{
Login(true);
}
}
public void OnFailure(Request request, Java.IO.IOException iOException)
@@ -350,6 +453,11 @@ namespace MusicApp
await Task.Delay(10);
}
}
else if(NextRefreshDate <= DateTime.UtcNow) //Acess token has expired
{
waitingForYoutube = true;
CreateYoutube();
}
waitingForYoutube = false;
return true;
}
@@ -816,9 +924,12 @@ namespace MusicApp
snackBar.Show();
}
public void Unknow()
public void UnknowError(Action action = null)
{
Snackbar snackBar = Snackbar.Make(FindViewById(Resource.Id.snackBar), Resource.String.unknow, Snackbar.LengthIndefinite);
if (action != null)
snackBar.SetAction("Try Again", (sender) => { action.Invoke(); snackBar.Dismiss(); });
else
snackBar.SetAction("Dismiss", (sender) => { snackBar.Dismiss(); });
snackBar.View.FindViewById<TextView>(Resource.Id.snackbar_text).SetTextColor(Color.White);
snackBar.Show();

View File

@@ -94,20 +94,20 @@
<Reference Include="ExoPlayer.UI, Version=2.8.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xam.Plugins.Android.ExoPlayer.UI.2.8.8\lib\monoandroid81\ExoPlayer.UI.dll</HintPath>
</Reference>
<Reference Include="Google.Apis, Version=1.37.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.1.37.0\lib\netstandard2.0\Google.Apis.dll</HintPath>
<Reference Include="Google.Apis, Version=1.38.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.1.38.0\lib\netstandard2.0\Google.Apis.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth, Version=1.37.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.Auth.1.37.0\lib\netstandard2.0\Google.Apis.Auth.dll</HintPath>
<Reference Include="Google.Apis.Auth, Version=1.38.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.Auth.1.38.0\lib\netstandard2.0\Google.Apis.Auth.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.37.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.Auth.1.37.0\lib\netstandard2.0\Google.Apis.Auth.PlatformServices.dll</HintPath>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.38.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.Auth.1.38.0\lib\netstandard2.0\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.37.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.Core.1.37.0\lib\netstandard2.0\Google.Apis.Core.dll</HintPath>
<Reference Include="Google.Apis.Core, Version=1.38.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.Core.1.38.0\lib\netstandard2.0\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.YouTube.v3, Version=1.37.0.1469, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.YouTube.v3.1.37.0.1469\lib\netstandard2.0\Google.Apis.YouTube.v3.dll</HintPath>
<Reference Include="Google.Apis.YouTube.v3, Version=1.38.0.1488, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.YouTube.v3.1.38.0.1488\lib\netstandard2.0\Google.Apis.YouTube.v3.dll</HintPath>
</Reference>
<Reference Include="Java.Interop" />
<Reference Include="Microsoft.CSharp" />

View File

@@ -13,6 +13,7 @@ using MusicApp;
using MusicApp.Resources.Portable_Class;
using Square.Picasso;
using System;
using System.Threading.Tasks;
public class AccountPreference : Preference, IResultCallback
{
@@ -60,6 +61,27 @@ public class AccountPreference : Preference, IResultCallback
}
}
private async void LogIn()
{
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestIdToken(Preferences.instance.GetString(Resource.String.clientID))
.RequestServerAuthCode(Preferences.instance.GetString(Resource.String.clientID))
.RequestEmail()
.RequestScopes(new Scope(YouTubeService.Scope.Youtube))
.Build();
GoogleApiClient googleClient = new GoogleApiClient.Builder(Preferences.instance)
.AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
.Build();
googleClient.Connect();
while (!googleClient.IsConnected)
await Task.Delay(10);
Preferences.instance.StartActivityForResult(Auth.GoogleSignInApi.GetSignInIntent(googleClient), 5981);
}
public void OnSignedIn()
{
Button log = (Button)view.FindViewById(Resource.Id.logButton);
@@ -70,6 +92,37 @@ public class AccountPreference : Preference, IResultCallback
log.Click -= logIn;
}
private async void LogOut()
{
MainActivity.account = null;
YoutubeEngine.youtubeService = null;
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestIdToken(Preferences.instance.GetString(Resource.String.clientID))
.RequestServerAuthCode(Preferences.instance.GetString(Resource.String.clientID))
.RequestEmail()
.RequestScopes(new Scope(YouTubeService.Scope.Youtube))
.Build();
GoogleApiClient googleClient = new GoogleApiClient.Builder(Preferences.instance)
.AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
.Build();
googleClient.Connect();
while (!googleClient.IsConnected)
await Task.Delay(10);
Auth.GoogleSignInApi.RevokeAccess(googleClient).SetResultCallback(this);
ISharedPreferences prefManager = PreferenceManager.GetDefaultSharedPreferences(Preferences.instance);
ISharedPreferencesEditor editor = prefManager.Edit();
editor.Remove("refresh-token");
editor.Apply();
}
//Log out result
public void OnResult(Java.Lang.Object result)
{
Button log = (Button)view.FindViewById(Resource.Id.logButton);
@@ -84,32 +137,4 @@ public class AccountPreference : Preference, IResultCallback
log.Click -= logOut;
MainActivity.instance.InvalidateOptionsMenu();
}
void LogIn()
{
if (MainActivity.instance.googleClient == null)
{
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestIdToken(Preferences.instance.GetString(Resource.String.clientID))
.RequestServerAuthCode(Preferences.instance.GetString(Resource.String.clientID))
.RequestEmail()
.RequestScopes(new Scope(YouTubeService.Scope.Youtube))
.Build();
MainActivity.instance.googleClient = new GoogleApiClient.Builder(Preferences.instance)
.AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
.Build();
MainActivity.instance.googleClient.Connect();
}
Preferences.instance.StartActivityForResult(Auth.GoogleSignInApi.GetSignInIntent(MainActivity.instance.googleClient), 5981);
}
void LogOut()
{
MainActivity.account = null;
YoutubeEngine.youtubeService = null;
Auth.GoogleSignInApi.SignOut(MainActivity.instance.googleClient).SetResultCallback(this);
}
}

View File

@@ -196,7 +196,7 @@ namespace MusicApp.Resources.Portable_Class
}
catch
{
MainActivity.instance.Unknow();
MainActivity.instance.UnknowError();
Cancel();
}
}

View File

@@ -20,6 +20,7 @@ using static Android.Provider.MediaStore.Audio;
using CursorLoader = Android.Support.V4.Content.CursorLoader;
using PopupMenu = Android.Support.V7.Widget.PopupMenu;
using RecyclerView = Android.Support.V7.Widget.RecyclerView;
using Toolbar = Android.Support.V7.Widget.Toolbar;
namespace MusicApp.Resources.Portable_Class
{
@@ -835,7 +836,7 @@ namespace MusicApp.Resources.Portable_Class
if (instance == null)
return;
if (System.Math.Abs(verticalOffset) <= appBarLayout.TotalScrollRange - MainActivity.instance.ToolBar.Height)
if (System.Math.Abs(verticalOffset) <= appBarLayout.TotalScrollRange - MainActivity.instance.FindViewById<Toolbar>(Resource.Id.toolbar).Height)
{
Activity.FindViewById<RelativeLayout>(Resource.Id.playlistHeader).Visibility = ViewStates.Visible;
MainActivity.instance.SupportActionBar.SetDisplayShowTitleEnabled(false);

View File

@@ -106,6 +106,7 @@ namespace MusicApp.Resources.Portable_Class
{
MainActivity.account = result.SignInAccount;
PreferencesFragment.instance?.SignedIn();
MainActivity.instance.CreateYoutube();
}
else
{

View File

@@ -976,7 +976,7 @@ namespace MusicApp.Resources.Portable_Class
if (ex is System.Net.Http.HttpRequestException)
MainActivity.instance.Timout();
else
MainActivity.instance.Unknow();
MainActivity.instance.UnknowError();
return;
}

View File

@@ -16,11 +16,11 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.36.1.0" newVersion="1.36.1.0" />
<bindingRedirect oldVersion="0.0.0.0-1.38.0.0" newVersion="1.38.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.36.1.0" newVersion="1.36.1.0" />
<bindingRedirect oldVersion="0.0.0.0-1.38.0.0" newVersion="1.38.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="PInvoke.BCrypt" publicKeyToken="9e300f9f87f04a7a" culture="neutral" />

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AngleSharp" version="0.9.11" targetFramework="monoandroid90" />
<package id="Google.Apis" version="1.37.0" targetFramework="monoandroid90" />
<package id="Google.Apis.Auth" version="1.37.0" targetFramework="monoandroid90" />
<package id="Google.Apis.Core" version="1.37.0" targetFramework="monoandroid90" />
<package id="Google.Apis.YouTube.v3" version="1.37.0.1469" targetFramework="monoandroid90" />
<package id="Google.Apis" version="1.38.0" targetFramework="monoandroid90" />
<package id="Google.Apis.Auth" version="1.38.0" targetFramework="monoandroid90" />
<package id="Google.Apis.Core" version="1.38.0" targetFramework="monoandroid90" />
<package id="Google.Apis.YouTube.v3" version="1.38.0.1488" targetFramework="monoandroid90" />
<package id="Karamunting.Android.AnderWeb.DiscreteSeekBar" version="1.0.1.1" targetFramework="monoandroid81" />
<package id="Microsoft.CSharp" version="4.5.0" targetFramework="monoandroid81" />
<package id="Microsoft.NETCore.Platforms" version="2.2.0" targetFramework="monoandroid81" />