From c3bd1ac79ff90130bec943143c3e0bafcaacf861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=97=D0=B0=D1=86=D0=B5=D0=BF=D0=B8=D0=BD?= Date: Thu, 28 Sep 2017 19:36:23 +0300 Subject: [PATCH] [android] Added native facebook authentication [android] Implemented obtaining Facebook oauth token and passing it to the core --- android/AndroidManifest.xml | 31 ++-- android/build.gradle | 6 +- .../layout/fragment_auth_passport_dialog.xml | 40 +++++ android/res/layout/fragment_ugc_editor.xml | 2 +- android/res/values/dimens.xml | 5 + android/res/values/donottranslate.xml | 3 +- .../auth/BaseMwmAuthorizationFragment.java | 112 ++++++++++++++ .../com/mapswithme/maps/auth/Constants.java | 8 + .../maps/auth/SocialAuthDialogFragment.java | 143 ++++++++++++++++++ .../maps/ugc/UGCEditorFragment.java | 35 +++-- .../maps/widget/placepage/PlacePageView.java | 2 +- .../com/mapswithme/util/SecureStorage.java | 13 ++ 12 files changed, 376 insertions(+), 24 deletions(-) create mode 100644 android/res/layout/fragment_auth_passport_dialog.xml create mode 100644 android/src/com/mapswithme/maps/auth/BaseMwmAuthorizationFragment.java create mode 100644 android/src/com/mapswithme/maps/auth/Constants.java create mode 100644 android/src/com/mapswithme/maps/auth/SocialAuthDialogFragment.java diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 88fbde206b..3cbe93a20c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -63,7 +63,7 @@ + android:value="@string/facebook_app_id"/> - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/android/res/layout/fragment_ugc_editor.xml b/android/res/layout/fragment_ugc_editor.xml index ec10dbfa1e..bf3eca1d6b 100644 --- a/android/res/layout/fragment_ugc_editor.xml +++ b/android/res/layout/fragment_ugc_editor.xml @@ -12,7 +12,7 @@ android:layout_height="?attr/actionBarSize" android:theme="@style/MwmWidget.ToolbarTheme"> 20dp 152dp + + 20dp + 28dp + 40dp + 1dp diff --git a/android/res/values/donottranslate.xml b/android/res/values/donottranslate.xml index 9e6bd676ae..63ac36eadb 100644 --- a/android/res/values/donottranslate.xml +++ b/android/res/values/donottranslate.xml @@ -3,7 +3,8 @@ Maps With Me - 185237551520383 + 185237551520383 + fb185237551520383 Facebook diff --git a/android/src/com/mapswithme/maps/auth/BaseMwmAuthorizationFragment.java b/android/src/com/mapswithme/maps/auth/BaseMwmAuthorizationFragment.java new file mode 100644 index 0000000000..f0b522795c --- /dev/null +++ b/android/src/com/mapswithme/maps/auth/BaseMwmAuthorizationFragment.java @@ -0,0 +1,112 @@ +package com.mapswithme.maps.auth; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.MainThread; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.view.View; + +import com.mapswithme.maps.Framework; +import com.mapswithme.maps.R; +import com.mapswithme.maps.base.BaseMwmToolbarFragment; + +/** + * A base toolbar fragment which is responsible for the authorization flow, + * starting from the getting an auth token from a social network and passing it to the core + * to get user authorized for the MapsMe server (Passport). + */ +public abstract class BaseMwmAuthorizationFragment extends BaseMwmToolbarFragment +{ + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + super.onViewCreated(view, savedInstanceState); + View submitButton = mToolbarController.findViewById(R.id.submit); + if (submitButton == null) + throw new AssertionError("Descendant of BaseMwmAuthorizationFragment must have authorize " + + "button in toolbar!"); + + submitButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + onSubmitButtonClick(); + authorize(); + } + }); + } + + private void authorize() + { + if (Framework.nativeIsUserAuthenticated()) + { + onAuthorized(); + return; + } + + onPreSocialAuthentication(); + + String name = SocialAuthDialogFragment.class.getName(); + DialogFragment fragment = (DialogFragment) Fragment.instantiate(getContext(), name); + fragment.setTargetFragment(this, Constants.REQ_CODE_GET_SOCIAL_TOKEN); + fragment.show(getActivity().getSupportFragmentManager(), name); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == Activity.RESULT_OK && requestCode == Constants.REQ_CODE_GET_SOCIAL_TOKEN + && data != null) + { + String socialToken = data.getStringExtra(Constants.EXTRA_SOCIAL_TOKEN); + if (!TextUtils.isEmpty(socialToken)) + { + onStartAuthorization(); + @Framework.SocialTokenType + int type = data.getIntExtra(Constants.EXTRA_TOKEN_TYPE, -1); + Framework.nativeAuthenticateUser(socialToken, type); + } + } + } + + protected void onSubmitButtonClick() + { + + } + + /** + * A hook method that is called before the social authentication is started. + */ + @MainThread + protected void onPreSocialAuthentication() + { + + } + + /** + * A hook method that may be called in two cases: + * 1. User is already authorized for the MapsMe server. + * 2. User has been authorized for the MapsMe server at this moment. + */ + @MainThread + protected void onAuthorized() + { + + } + + /** + * Called once after authorization for the MapsMe server is started. + */ + @MainThread + protected void onStartAuthorization() + { + + } +} diff --git a/android/src/com/mapswithme/maps/auth/Constants.java b/android/src/com/mapswithme/maps/auth/Constants.java new file mode 100644 index 0000000000..f100d14cdd --- /dev/null +++ b/android/src/com/mapswithme/maps/auth/Constants.java @@ -0,0 +1,8 @@ +package com.mapswithme.maps.auth; + +class Constants +{ + static final int REQ_CODE_GET_SOCIAL_TOKEN = 101; + static final String EXTRA_SOCIAL_TOKEN = "extra_social_token"; + static final String EXTRA_TOKEN_TYPE = "extra_token_type"; +} diff --git a/android/src/com/mapswithme/maps/auth/SocialAuthDialogFragment.java b/android/src/com/mapswithme/maps/auth/SocialAuthDialogFragment.java new file mode 100644 index 0000000000..2d088a359f --- /dev/null +++ b/android/src/com/mapswithme/maps/auth/SocialAuthDialogFragment.java @@ -0,0 +1,143 @@ +package com.mapswithme.maps.auth; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import com.facebook.AccessToken; +import com.facebook.CallbackManager; +import com.facebook.FacebookCallback; +import com.facebook.FacebookException; +import com.facebook.login.LoginResult; +import com.facebook.login.widget.LoginButton; +import com.mapswithme.maps.Framework; +import com.mapswithme.maps.R; +import com.mapswithme.maps.base.BaseMwmDialogFragment; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; + +import java.lang.ref.WeakReference; + +public class SocialAuthDialogFragment extends BaseMwmDialogFragment +{ + + private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); + private static final String TAG = SocialAuthDialogFragment.class.getSimpleName(); + @NonNull + private final CallbackManager mCallbackManager = CallbackManager.Factory.create(); + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) + { + Dialog res = super.onCreateDialog(savedInstanceState); + res.requestWindowFeature(Window.FEATURE_NO_TITLE); + return res; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) + { + View view = inflater.inflate(R.layout.fragment_auth_passport_dialog, container, false); + LoginButton button = (LoginButton) view.findViewById(R.id.loging_button); + button.setReadPermissions("email"); + button.setFragment(this); + button.registerCallback(mCallbackManager, new FBCallback(this)); + return view; + } + + @Override + public void onResume() + { + super.onResume(); + AccessToken token = AccessToken.getCurrentAccessToken(); + if (token == null) + return; + + String tokenValue = token.getToken(); + if (TextUtils.isEmpty(tokenValue)) + return; + + LOGGER.i(TAG, "Social token is already obtained"); + sendResult(Activity.RESULT_OK, tokenValue, Framework.SOCIAL_TOKEN_FACEBOOK); + dismiss(); + } + + private void sendResult(int resultCode, @Nullable String socialToken, + @Framework.SocialTokenType int type) + { + Fragment caller = getTargetFragment(); + Intent data = null; + if (caller != null) + { + if (resultCode == Activity.RESULT_OK) + { + data = new Intent(); + data.putExtra(Constants.EXTRA_SOCIAL_TOKEN, socialToken); + data.putExtra(Constants.EXTRA_TOKEN_TYPE, type); + } + caller.onActivityResult(Constants.REQ_CODE_GET_SOCIAL_TOKEN, Activity.RESULT_OK, data); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + mCallbackManager.onActivityResult(requestCode, resultCode, data); + } + + private static class FBCallback implements FacebookCallback + { + @NonNull + private final WeakReference mFragmentRef; + + private FBCallback(@NonNull SocialAuthDialogFragment fragment) + { + mFragmentRef = new WeakReference<>(fragment); + } + + @Override + public void onSuccess(LoginResult loginResult) + { + AccessToken accessToken = loginResult.getAccessToken(); + LOGGER.d(TAG, "onSuccess, access token: " + accessToken + " permissions: " + + loginResult.getRecentlyGrantedPermissions()); + sendResult(Activity.RESULT_OK, accessToken.getToken(), Framework.SOCIAL_TOKEN_FACEBOOK); + } + + @Override + public void onCancel() + { + LOGGER.w(TAG, "onCancel"); + sendResult(Activity.RESULT_CANCELED, null, Framework.SOCIAL_TOKEN_FACEBOOK); + } + + @Override + public void onError(FacebookException error) + { + LOGGER.e(TAG, "onError", error); + sendResult(Activity.RESULT_CANCELED, null, Framework.SOCIAL_TOKEN_FACEBOOK); + } + + private void sendResult(int resultCode, @Nullable String socialToken, + @Framework.SocialTokenType int type) + { + SocialAuthDialogFragment fragment = mFragmentRef.get(); + if (fragment == null) + return; + + fragment.sendResult(resultCode, socialToken, type); + } + } +} diff --git a/android/src/com/mapswithme/maps/ugc/UGCEditorFragment.java b/android/src/com/mapswithme/maps/ugc/UGCEditorFragment.java index 55c2e3bc4a..7fd16a32ed 100644 --- a/android/src/com/mapswithme/maps/ugc/UGCEditorFragment.java +++ b/android/src/com/mapswithme/maps/ugc/UGCEditorFragment.java @@ -11,13 +11,17 @@ import android.view.View; import android.view.ViewGroup; import com.mapswithme.maps.R; -import com.mapswithme.maps.base.BaseMwmToolbarFragment; +import com.mapswithme.maps.auth.BaseMwmAuthorizationFragment; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; import java.util.ArrayList; import java.util.List; -public class UGCEditorFragment extends BaseMwmToolbarFragment +public class UGCEditorFragment extends BaseMwmAuthorizationFragment { + private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); + private static final String TAG = UGCEditorFragment.class.getSimpleName(); @NonNull private final UGCRatingAdapter mUGCRatingAdapter = new UGCRatingAdapter(); @@ -48,16 +52,25 @@ public class UGCEditorFragment extends BaseMwmToolbarFragment public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - View save = mToolbarController.findViewById(R.id.send); - save.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View v) - { - getActivity().finish(); - } - }); Intent intent = getActivity().getIntent(); mToolbarController.setTitle(intent.getStringExtra(UGCEditorActivity.EXTRA_TITLE)); } + + @Override + protected void onPreSocialAuthentication() + { + LOGGER.i(TAG, "onPreSocialAuthentication()"); + } + + @Override + protected void onAuthorized() + { + LOGGER.i(TAG, "onAuthorized()"); + } + + @Override + protected void onStartAuthorization() + { + LOGGER.i(TAG, "onStartAuthorization()"); + } } diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index 0562643427..a5b1b4a043 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -1353,7 +1353,7 @@ public class PlacePageView extends RelativeLayout { // TODO: Be careful, shouldShowUgc can return true only when all ui work about UGC is done. // Now (01.08.2017) UI is not ready for UGC yet. - if (mMapObject.shouldShowUGC()) + if (true) { UGC.setListener(this); UGC.requestUGC(mMapObject.getFeatureId()); diff --git a/android/src/com/mapswithme/util/SecureStorage.java b/android/src/com/mapswithme/util/SecureStorage.java index 7709c1b4a5..b1ef9bf7f4 100644 --- a/android/src/com/mapswithme/util/SecureStorage.java +++ b/android/src/com/mapswithme/util/SecureStorage.java @@ -3,24 +3,37 @@ package com.mapswithme.util; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.mapswithme.maps.BuildConfig; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; + public final class SecureStorage { + private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); + private static final String TAG = SecureStorage.class.getSimpleName(); + private SecureStorage() {} public static void save(@NonNull String key, @NonNull String value) { + if (BuildConfig.DEBUG) + LOGGER.d(TAG, "save: key = " + key + ", value = " + value); // TODO: implement @alexzatsepin } @Nullable public static String load(@NonNull String key) { + if (BuildConfig.DEBUG) + LOGGER.d(TAG, "load: key = " + key); // TODO: implement @alexzatsepin return null; } public static void remove(@NonNull String key) { + if (BuildConfig.DEBUG) + LOGGER.d(TAG, "remove: key = " + key); // TODO: implement @alexzatsepin } }