diff --git a/QtAndroidTools/QAndroidGoogleAccount.cpp b/QtAndroidTools/QAndroidGoogleAccount.cpp index ad07feb..4a7f767 100644 --- a/QtAndroidTools/QAndroidGoogleAccount.cpp +++ b/QtAndroidTools/QAndroidGoogleAccount.cpp @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include #include "QAndroidGoogleAccount.h" +#include QAndroidGoogleAccount *QAndroidGoogleAccount::m_pInstance = nullptr; @@ -31,8 +31,19 @@ QAndroidGoogleAccount::QAndroidGoogleAccount() : m_JavaGoogleAccount("com/falsin QtAndroid::androidActivity().object()) { m_pInstance = this; - connect(&m_NetworkAccessManager, &QNetworkAccessManager::finished, this, &QAndroidGoogleAccount::AccountPhotoDownloaded); - LoadLastSignedInAccountInfo(); + + if(m_JavaGoogleAccount.isValid()) + { + const JNINativeMethod JniMethod[] = { + {"loadedLastSignedInAccountInfo", "(Lcom/falsinsoft/qtandroidtools/AndroidGoogleAccount$AccountInfo;)V", reinterpret_cast(&QAndroidGoogleAccount::LoadedLastSignedInAccountInfo)} + }; + QAndroidJniEnvironment JniEnv; + jclass ObjectClass; + + ObjectClass = JniEnv->GetObjectClass(m_JavaGoogleAccount.object()); + JniEnv->RegisterNatives(ObjectClass, JniMethod, sizeof(JniMethod)/sizeof(JNINativeMethod)); + JniEnv->DeleteLocalRef(ObjectClass); + } } QObject* QAndroidGoogleAccount::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine) @@ -49,7 +60,74 @@ QAndroidGoogleAccount* QAndroidGoogleAccount::instance() return m_pInstance; } -bool QAndroidGoogleAccount::signIn() +bool QAndroidGoogleAccount::signIn(bool lastSignedIn) +{ + bool SignInSuccessfully; + + if(lastSignedIn == true) + { + SignInSuccessfully = SignInToLastSignedInAccount(); + + if(SignInSuccessfully == false) + { + SignInSuccessfully = SelectSignInAccount(); + } + } + else + { + SignInSuccessfully = SelectSignInAccount(); + } + + return SignInSuccessfully; +} + +void QAndroidGoogleAccount::UpdateLastSignedInAccountInfo(const QAndroidJniObject &AccountInfoObj) +{ + if(AccountInfoObj.isValid()) + { + const QAndroidJniObject PhotoObj = AccountInfoObj.getObjectField("photo", "Landroid/graphics/Bitmap;"); + + m_LastSignedInAccountInfo.Id = AccountInfoObj.getObjectField("id").toString(); + m_LastSignedInAccountInfo.DisplayName = AccountInfoObj.getObjectField("displayName").toString(); + m_LastSignedInAccountInfo.Email = AccountInfoObj.getObjectField("email").toString(); + m_LastSignedInAccountInfo.FamilyName = AccountInfoObj.getObjectField("familyName").toString(); + m_LastSignedInAccountInfo.GivenName = AccountInfoObj.getObjectField("givenName").toString(); + + if(PhotoObj.isValid()) + { + const QImage PhotoImage = AndroidBitmapToImage(PhotoObj); + + if(PhotoImage.isNull() == false) + { + m_LastSignedInAccountPhoto = QPixmap::fromImage(PhotoImage); + } + } + + emit lastSignedInAccountInfoChanged(); + } +} + +const QAndroidGoogleAccountInfo& QAndroidGoogleAccount::getLastSignedInAccountInfo() const +{ + return m_LastSignedInAccountInfo; +} + +QPixmap QAndroidGoogleAccount::GetAccountPhoto() const +{ + return m_LastSignedInAccountPhoto; +} + +bool QAndroidGoogleAccount::SignInToLastSignedInAccount() +{ + if(m_JavaGoogleAccount.isValid()) + { + return m_JavaGoogleAccount.callMethod("loadLastSignedInAccountInfo", "()Z"); + } + + return false; +} + +bool QAndroidGoogleAccount::SelectSignInAccount() { if(m_JavaGoogleAccount.isValid()) { @@ -65,53 +143,6 @@ bool QAndroidGoogleAccount::signIn() return false; } -void QAndroidGoogleAccount::LoadLastSignedInAccountInfo() -{ - if(m_JavaGoogleAccount.isValid()) - { - const QAndroidJniObject AccountInfoObj = m_JavaGoogleAccount.callObjectMethod("getLastSignedInAccountInfo", - "()Lcom/falsinsoft/qtandroidtools/AndroidGoogleAccount$AccountInfo;" - ); - if(AccountInfoObj.isValid()) - { - QString PhotoUrl; - - m_LastSignedInAccountInfo.Id = AccountInfoObj.getObjectField("id").toString(); - m_LastSignedInAccountInfo.DisplayName = AccountInfoObj.getObjectField("displayName").toString(); - m_LastSignedInAccountInfo.Email = AccountInfoObj.getObjectField("email").toString(); - m_LastSignedInAccountInfo.FamilyName = AccountInfoObj.getObjectField("familyName").toString(); - m_LastSignedInAccountInfo.GivenName = AccountInfoObj.getObjectField("givenName").toString(); - - PhotoUrl = AccountInfoObj.getObjectField("photoUrl").toString(); - if(PhotoUrl.isEmpty() == false) - { - const QNetworkRequest PhotoDownloadRequest(PhotoUrl); - m_NetworkAccessManager.get(PhotoDownloadRequest); - } - - emit lastSignedInAccountInfoChanged(); - } - } -} - -const QAndroidGoogleAccountInfo& QAndroidGoogleAccount::getLastSignedInAccountInfo() const -{ - return m_LastSignedInAccountInfo; -} - -QPixmap QAndroidGoogleAccount::GetAccountPhoto() const -{ - return m_LastSignedInAccountPhoto; -} - -void QAndroidGoogleAccount::AccountPhotoDownloaded(QNetworkReply *pReply) -{ - if(pReply->error() == QNetworkReply::NoError) - { - m_LastSignedInAccountPhoto = QPixmap(pReply->readAll()); - } -} - void QAndroidGoogleAccount::ActivityResult(int RequestCode, int ResultCode, const QAndroidJniObject &Data) { Q_UNUSED(ResultCode) @@ -121,13 +152,74 @@ void QAndroidGoogleAccount::ActivityResult(int RequestCode, int ResultCode, cons if(m_JavaGoogleAccount.isValid()) { const bool SignInSuccessfully = m_JavaGoogleAccount.callMethod("signInIntentDataResult", - "(Landroid/content/Intent;)V", + "(Landroid/content/Intent;)Z", Data.object() ); - if(SignInSuccessfully == true) + if(SignInSuccessfully == false) { - LoadLastSignedInAccountInfo(); + emit signedIn(false); } } } } + +void QAndroidGoogleAccount::LoadedLastSignedInAccountInfo(JNIEnv *env, jobject thiz, jobject accountInfo) +{ + Q_UNUSED(env) + Q_UNUSED(thiz) + + if(m_pInstance != nullptr) + { + m_pInstance->UpdateLastSignedInAccountInfo(QAndroidJniObject(accountInfo)); + emit m_pInstance->signedIn(true); + } +} + +// Copyright KDAB (BogDan Vatra) +// https://www.kdab.com/qt-on-android-how-to-convert-qt-images-to-android-images-and-vice-versa-2/ +QImage QAndroidGoogleAccount::AndroidBitmapToImage(const QAndroidJniObject &JniBmp) +{ + QAndroidJniEnvironment env; + AndroidBitmapInfo info; + if (AndroidBitmap_getInfo(env, JniBmp.object(), &info) != ANDROID_BITMAP_RESULT_SUCCESS) + return QImage(); + + QImage::Format format; + switch (info.format) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + format = QImage::Format_RGBA8888; + break; + case ANDROID_BITMAP_FORMAT_RGB_565: + format = QImage::Format_RGB16; + break; + case ANDROID_BITMAP_FORMAT_RGBA_4444: + format = QImage::Format_ARGB4444_Premultiplied; + break; + case ANDROID_BITMAP_FORMAT_A_8: + format = QImage::Format_Alpha8; + break; + default: + return QImage(); + } + + void *pixels; + if (AndroidBitmap_lockPixels(env, JniBmp.object(), &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) + return QImage(); + + QImage image(info.width, info.height, format); + + if (info.stride == uint32_t(image.bytesPerLine())) { + memcpy((void*)image.constBits(), pixels, info.stride * info.height); + } else { + uchar *bmpPtr = static_cast(pixels); + const unsigned width = std::min(info.width, (uint)image.width()); + const unsigned height = std::min(info.height, (uint)image.height()); + for (unsigned y = 0; y < height; y++, bmpPtr += info.stride) + memcpy((void*)image.constScanLine(y), bmpPtr, width); + } + + if (AndroidBitmap_unlockPixels(env, JniBmp.object()) != ANDROID_BITMAP_RESULT_SUCCESS) + return QImage(); + + return image; +} diff --git a/QtAndroidTools/QAndroidGoogleAccount.h b/QtAndroidTools/QAndroidGoogleAccount.h index d7b37d6..199bf0f 100644 --- a/QtAndroidTools/QAndroidGoogleAccount.h +++ b/QtAndroidTools/QAndroidGoogleAccount.h @@ -24,9 +24,6 @@ #pragma once #include -#include -#include -#include #include #include #include @@ -78,25 +75,27 @@ public: static QObject* qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine); static QAndroidGoogleAccount* instance(); - Q_INVOKABLE bool signIn(); + Q_INVOKABLE bool signIn(bool lastSignedIn); const QAndroidGoogleAccountInfo& getLastSignedInAccountInfo() const; signals: void lastSignedInAccountInfoChanged(); - -private slots: - void AccountPhotoDownloaded(QNetworkReply *pReply); + void signedIn(bool signInSuccessfully); private: const QAndroidJniObject m_JavaGoogleAccount; static QAndroidGoogleAccount *m_pInstance; const int m_SignInId = 9001; QAndroidGoogleAccountInfo m_LastSignedInAccountInfo; - QNetworkAccessManager m_NetworkAccessManager; QPixmap m_LastSignedInAccountPhoto; + static void LoadedLastSignedInAccountInfo(JNIEnv *env, jobject thiz, jobject accountInfo); + void ActivityResult(int RequestCode, int ResultCode, const QAndroidJniObject &Data); - void LoadLastSignedInAccountInfo(); + void UpdateLastSignedInAccountInfo(const QAndroidJniObject &AccountInfoObj); + QImage AndroidBitmapToImage(const QAndroidJniObject &JniBmp); QPixmap GetAccountPhoto() const; + bool SignInToLastSignedInAccount(); + bool SelectSignInAccount(); }; diff --git a/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidGoogleAccount.java b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidGoogleAccount.java index 8330147..c69bb33 100644 --- a/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidGoogleAccount.java +++ b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidGoogleAccount.java @@ -29,6 +29,9 @@ import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.util.Log; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.AsyncTask; import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; import android.content.ComponentName; @@ -44,15 +47,16 @@ import com.google.android.gms.common.api.Scope; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; +import java.io.InputStream; +import java.net.URL; + public class AndroidGoogleAccount { private final Activity mActivityInstance; private GoogleSignInClient mGoogleSignInClient; - private GoogleSignInAccount mLastSignedInAccount; public AndroidGoogleAccount(Activity ActivityInstance) { - mLastSignedInAccount = GoogleSignIn.getLastSignedInAccount(ActivityInstance); mActivityInstance = ActivityInstance; getSignInClient(ActivityInstance); } @@ -66,29 +70,9 @@ public class AndroidGoogleAccount mGoogleSignInClient = GoogleSignIn.getClient(ActivityInstance, SignInOptions); } - public AccountInfo getLastSignedInAccountInfo() + public boolean loadLastSignedInAccountInfo() { - AccountInfo Account = null; - - if(mLastSignedInAccount != null) - { - Uri PhotoUrl; - - Account = new AccountInfo(); - Account.id = mLastSignedInAccount.getId(); - Account.displayName = mLastSignedInAccount.getDisplayName(); - Account.email = mLastSignedInAccount.getEmail(); - Account.familyName = mLastSignedInAccount.getFamilyName(); - Account.givenName = mLastSignedInAccount.getGivenName(); - - PhotoUrl = mLastSignedInAccount.getPhotoUrl(); - if(PhotoUrl != null) - Account.photoUrl = PhotoUrl.toString(); - else - Account.photoUrl = new String(); - } - - return Account; + return loadLastSignedInAccountInfo(GoogleSignIn.getLastSignedInAccount(mActivityInstance)); } public Intent getSignInIntent() @@ -98,27 +82,87 @@ public class AndroidGoogleAccount public boolean signInIntentDataResult(Intent Data) { - Task SignInTask = GoogleSignIn.getSignedInAccountFromIntent(Data); + final Task SignInTask = GoogleSignIn.getSignedInAccountFromIntent(Data); - if(SignInTask.isSuccessful()) + try { - try + loadLastSignedInAccountInfo(SignInTask.getResult(ApiException.class)); + return true; + } + catch(ApiException e) + { + if(e.getStatusCode() == GoogleSignInStatusCodes.DEVELOPER_ERROR) { - mLastSignedInAccount = SignInTask.getResult(ApiException.class); - return true; - } - catch(ApiException e) - { - if(e.getStatusCode() == GoogleSignInStatusCodes.DEVELOPER_ERROR) - { - Log.d("AndroidGoogleAccount", "DEVELOPER_ERROR -> Have you signed your project on Android console?"); - } + Log.d("AndroidGoogleAccount", "DEVELOPER_ERROR -> Have you signed your project on Android console?"); } } return false; } + private boolean loadLastSignedInAccountInfo(final GoogleSignInAccount LastSignedInAccount) + { + if(LastSignedInAccount != null) + { + AccountInfo LastSignedInAccountInfo = new AccountInfo(); + final Uri PhotoUrl = LastSignedInAccount.getPhotoUrl(); + + LastSignedInAccountInfo.id = LastSignedInAccount.getId(); + LastSignedInAccountInfo.displayName = LastSignedInAccount.getDisplayName(); + LastSignedInAccountInfo.email = LastSignedInAccount.getEmail(); + LastSignedInAccountInfo.familyName = LastSignedInAccount.getFamilyName(); + LastSignedInAccountInfo.givenName = LastSignedInAccount.getGivenName(); + + if(PhotoUrl != null) + { + DownloadAccountPhotoTask DownloadAccountPhoto = new DownloadAccountPhotoTask(LastSignedInAccountInfo); + DownloadAccountPhoto.execute(PhotoUrl.toString()); + } + else + { + LastSignedInAccountInfo.photo = null; + loadedLastSignedInAccountInfo(LastSignedInAccountInfo); + } + + return true; + } + + return false; + } + + private class DownloadAccountPhotoTask extends AsyncTask + { + private AccountInfo mLastSignedInAccountInfo; + + DownloadAccountPhotoTask(AccountInfo LastSignedInAccountInfo) + { + mLastSignedInAccountInfo = LastSignedInAccountInfo; + } + + protected Bitmap doInBackground(String... urls) + { + final String PhotoUrl = urls[0]; + Bitmap AccountPhoto = null; + + try + { + InputStream PhotoStream = new java.net.URL(PhotoUrl).openStream(); + AccountPhoto = BitmapFactory.decodeStream(PhotoStream); + } + catch(Exception e) + { + } + + return AccountPhoto; + } + + protected void onPostExecute(Bitmap AccountPhoto) + { + mLastSignedInAccountInfo.photo = AccountPhoto; + loadedLastSignedInAccountInfo(mLastSignedInAccountInfo); + } + } + public static class AccountInfo { public String id; @@ -126,6 +170,8 @@ public class AndroidGoogleAccount public String email; public String familyName; public String givenName; - public String photoUrl; + public Bitmap photo; } + + private static native void loadedLastSignedInAccountInfo(AccountInfo accountInfo); } diff --git a/QtAndroidToolsDemo/QtAndroidToolsDemo.pro b/QtAndroidToolsDemo/QtAndroidToolsDemo.pro index 0ffb5cf..c7327d9 100644 --- a/QtAndroidToolsDemo/QtAndroidToolsDemo.pro +++ b/QtAndroidToolsDemo/QtAndroidToolsDemo.pro @@ -1,4 +1,4 @@ -QT += quick quickcontrols2 svg network +QT += quick quickcontrols2 svg CONFIG += c++11 TARGET = QtAndroidToolsDemo diff --git a/QtAndroidToolsDemo/tools/AndroidGoogleAccount.qml b/QtAndroidToolsDemo/tools/AndroidGoogleAccount.qml index c8bfe6e..2352608 100644 --- a/QtAndroidToolsDemo/tools/AndroidGoogleAccount.qml +++ b/QtAndroidToolsDemo/tools/AndroidGoogleAccount.qml @@ -8,8 +8,20 @@ Page { id: page padding: 0 + Connections { + target: QtAndroidGoogleAccount + onSignedIn: { + if(signInSuccessfully === true) + { + accountPhoto.source = "image://LastSignedInAccountPhoto/"; + } + } + } + Column { - anchors.fill: parent + width: parent.width * 0.9 + height: parent.height * 0.9 + anchors.centerIn: parent spacing: 5 Label { @@ -63,15 +75,15 @@ Page { } Image { + id: accountPhoto width: 200 height: 200 - source: "image://LastSignedInAccountPhoto/" } Button { anchors.horizontalCenter: parent.horizontalCenter text: "sigIn" - onClicked: QtAndroidGoogleAccount.signIn() + onClicked: QtAndroidGoogleAccount.signIn(true) } } }