From 18aec38d9f4d79afe0748b4bae1dfe11a3ee24ef Mon Sep 17 00:00:00 2001 From: Docile-Alligator <25734209+Docile-Alligator@users.noreply.github.com> Date: Thu, 23 May 2024 16:12:44 -0400 Subject: Bare bone login using Chrome custom tab. --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 36 ++- .../infinityforreddit/AppComponent.java | 3 + .../activities/LoginActivity.java | 35 +-- .../activities/LoginChromeCustomTabActivity.java | 247 +++++++++++++++++++++ .../infinityforreddit/activities/MainActivity.java | 25 +-- .../events/NewUserLoggedInEvent.java | 4 + .../layout/activity_login_chrome_custom_tab.xml | 10 + 8 files changed, 318 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/activities/LoginChromeCustomTabActivity.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/events/NewUserLoggedInEvent.java create mode 100644 app/src/main/res/layout/activity_login_chrome_custom_tab.xml diff --git a/app/build.gradle b/app/build.gradle index 8d094fd7..ed6f7d07 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,6 +63,7 @@ dependencies { implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.activity:activity:1.8.0' def lifecycleVersion = "2.7.0" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-livedata:$lifecycleVersion" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17929a31..6c533f12 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,23 +34,42 @@ android:theme="@style/AppTheme" android:usesCleartextTraffic="true" tools:replace="android:label"> + + + + + + + + + + + + + android:theme="@style/AppTheme.NoActionBar" /> + android:theme="@style/AppTheme.NoActionBar" /> + android:theme="@style/AppTheme.NoActionBar" /> - { - new MaterialAlertDialogBuilder(this, R.style.MaterialAlertDialogTheme) - .setTitle(R.string.have_trouble_login_title) - .setMessage(R.string.have_trouble_login_message) - .setPositiveButton(R.string.yes, (dialogInterface, i) -> { - enableDom = !enableDom; - ActivityCompat.recreate(this); - }) - .setNegativeButton(R.string.no, null) - .show(); - }); - if (enableDom) { binding.twoFaInfOTextViewLoginActivity.setVisibility(View.GONE); } @@ -144,6 +132,22 @@ public class LoginActivity extends BaseActivity { String url = uriBuilder.toString(); + binding.fabLoginActivity.setOnClickListener(view -> { + /*new MaterialAlertDialogBuilder(this, R.style.MaterialAlertDialogTheme) + .setTitle(R.string.have_trouble_login_title) + .setMessage(R.string.have_trouble_login_message) + .setPositiveButton(R.string.yes, (dialogInterface, i) -> { + enableDom = !enableDom; + ActivityCompat.recreate(this); + }) + .setNegativeButton(R.string.no, null) + .show();*/ + + Intent intent = new Intent(this, LoginChromeCustomTabActivity.class); + startActivity(intent); + finish(); + }); + CookieManager.getInstance().removeAllCookies(aBoolean -> { }); @@ -190,8 +194,7 @@ public class LoginActivity extends BaseActivity { ParseAndInsertNewAccount.parseAndInsertNewAccount(mExecutor, new Handler(), name, accessToken, refreshToken, profileImageUrl, bannerImageUrl, karma, authCode, mRedditDataRoomDatabase.accountDao(), () -> { - Intent resultIntent = new Intent(); - setResult(Activity.RESULT_OK, resultIntent); + EventBus.getDefault().post(new NewUserLoggedInEvent()); finish(); }); } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/LoginChromeCustomTabActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/LoginChromeCustomTabActivity.java new file mode 100644 index 00000000..84c06ea6 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/LoginChromeCustomTabActivity.java @@ -0,0 +1,247 @@ +package ml.docilealligator.infinityforreddit.activities; + +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.annotation.NonNull; +import androidx.browser.customtabs.CustomTabColorSchemeParams; +import androidx.browser.customtabs.CustomTabsIntent; +import androidx.browser.customtabs.CustomTabsService; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import org.greenrobot.eventbus.EventBus; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +import javax.inject.Inject; +import javax.inject.Named; + +import ml.docilealligator.infinityforreddit.FetchMyInfo; +import ml.docilealligator.infinityforreddit.Infinity; +import ml.docilealligator.infinityforreddit.R; +import ml.docilealligator.infinityforreddit.RedditDataRoomDatabase; +import ml.docilealligator.infinityforreddit.apis.RedditAPI; +import ml.docilealligator.infinityforreddit.asynctasks.ParseAndInsertNewAccount; +import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; +import ml.docilealligator.infinityforreddit.events.NewUserLoggedInEvent; +import ml.docilealligator.infinityforreddit.utils.APIUtils; +import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; + +public class LoginChromeCustomTabActivity extends BaseActivity { + + @Inject + @Named("no_oauth") + Retrofit mRetrofit; + @Inject + @Named("oauth") + Retrofit mOauthRetrofit; + @Inject + RedditDataRoomDatabase mRedditDataRoomDatabase; + @Inject + @Named("default") + SharedPreferences mSharedPreferences; + @Inject + @Named("current_account") + SharedPreferences mCurrentAccountSharedPreferences; + @Inject + CustomThemeWrapper mCustomThemeWrapper; + @Inject + Executor mExecutor; + + @Override + protected void onCreate(Bundle savedInstanceState) { + ((Infinity) getApplication()).getAppComponent().inject(this); + + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_login_chrome_custom_tab); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + + applyCustomTheme(); + + ArrayList resolveInfos = getCustomTabsPackages(getPackageManager()); + if (!resolveInfos.isEmpty()) { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + // add share action to menu list + builder.setShareState(CustomTabsIntent.SHARE_STATE_ON); + builder.setDefaultColorSchemeParams( + new CustomTabColorSchemeParams.Builder() + .setToolbarColor(mCustomThemeWrapper.getColorPrimary()) + .build()); + CustomTabsIntent customTabsIntent = builder.build(); + customTabsIntent.intent.setPackage(resolveInfos.get(0).activityInfo.packageName); + customTabsIntent.intent.putExtra("com.google.android.apps.chrome.EXTRA_OPEN_NEW_INCOGNITO_TAB", true); + + try { + Uri.Builder uriBuilder = Uri.parse(APIUtils.OAUTH_URL).buildUpon(); + uriBuilder.appendQueryParameter(APIUtils.CLIENT_ID_KEY, APIUtils.CLIENT_ID); + uriBuilder.appendQueryParameter(APIUtils.RESPONSE_TYPE_KEY, APIUtils.RESPONSE_TYPE); + uriBuilder.appendQueryParameter(APIUtils.STATE_KEY, APIUtils.STATE); + uriBuilder.appendQueryParameter(APIUtils.REDIRECT_URI_KEY, APIUtils.REDIRECT_URI); + uriBuilder.appendQueryParameter(APIUtils.DURATION_KEY, APIUtils.DURATION); + uriBuilder.appendQueryParameter(APIUtils.SCOPE_KEY, APIUtils.SCOPE); + + customTabsIntent.launchUrl(this, uriBuilder.build()); + } catch (ActivityNotFoundException e) { + // TODO catch this + } + } + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + Uri uri = intent.getData(); + if (uri == null) { + return; + } + + String authCode = uri.getQueryParameter("code"); + if (authCode != null) { + String state = uri.getQueryParameter("state"); + if (APIUtils.STATE.equals(state)) { + Map params = new HashMap<>(); + params.put(APIUtils.GRANT_TYPE_KEY, "authorization_code"); + params.put("code", authCode); + params.put(APIUtils.REDIRECT_URI_KEY, APIUtils.REDIRECT_URI); + + RedditAPI api = mRetrofit.create(RedditAPI.class); + Call accessTokenCall = api.getAccessToken(APIUtils.getHttpBasicAuthHeader(), params); + accessTokenCall.enqueue(new Callback<>() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful()) { + try { + String accountResponse = response.body(); + if (accountResponse == null) { + //Handle error + return; + } + + JSONObject responseJSON = new JSONObject(accountResponse); + String accessToken = responseJSON.getString(APIUtils.ACCESS_TOKEN_KEY); + String refreshToken = responseJSON.getString(APIUtils.REFRESH_TOKEN_KEY); + + FetchMyInfo.fetchAccountInfo(mOauthRetrofit, mRedditDataRoomDatabase, + accessToken, new FetchMyInfo.FetchMyInfoListener() { + @Override + public void onFetchMyInfoSuccess(String name, String profileImageUrl, String bannerImageUrl, int karma) { + mCurrentAccountSharedPreferences.edit().putString(SharedPreferencesUtils.ACCESS_TOKEN, accessToken) + .putString(SharedPreferencesUtils.ACCOUNT_NAME, name) + .putString(SharedPreferencesUtils.ACCOUNT_IMAGE_URL, profileImageUrl).apply(); + mCurrentAccountSharedPreferences.edit().remove(SharedPreferencesUtils.SUBSCRIBED_THINGS_SYNC_TIME).apply(); + ParseAndInsertNewAccount.parseAndInsertNewAccount(mExecutor, new Handler(), name, accessToken, refreshToken, profileImageUrl, bannerImageUrl, + karma, authCode, mRedditDataRoomDatabase.accountDao(), + () -> { + EventBus.getDefault().post(new NewUserLoggedInEvent()); + finish(); + }); + } + + @Override + public void onFetchMyInfoFailed(boolean parseFailed) { + if (parseFailed) { + Toast.makeText(LoginChromeCustomTabActivity.this, R.string.parse_user_info_error, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(LoginChromeCustomTabActivity.this, R.string.cannot_fetch_user_info, Toast.LENGTH_SHORT).show(); + } + + finish(); + } + }); + } catch (JSONException e) { + e.printStackTrace(); + Toast.makeText(LoginChromeCustomTabActivity.this, R.string.parse_json_response_error, Toast.LENGTH_SHORT).show(); + finish(); + } + } else { + Toast.makeText(LoginChromeCustomTabActivity.this, R.string.retrieve_token_error, Toast.LENGTH_SHORT).show(); + finish(); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + Toast.makeText(LoginChromeCustomTabActivity.this, R.string.retrieve_token_error, Toast.LENGTH_SHORT).show(); + t.printStackTrace(); + finish(); + } + }); + } else { + Toast.makeText(this, R.string.something_went_wrong, Toast.LENGTH_SHORT).show(); + finish(); + } + + } else if ("access_denied".equals(uri.getQueryParameter("error"))) { + Toast.makeText(this, R.string.access_denied, Toast.LENGTH_SHORT).show(); + finish(); + } + } + + @Override + public SharedPreferences getDefaultSharedPreferences() { + return mSharedPreferences; + } + + @Override + public SharedPreferences getCurrentAccountSharedPreferences() { + return mCurrentAccountSharedPreferences; + } + + @Override + public CustomThemeWrapper getCustomThemeWrapper() { + return mCustomThemeWrapper; + } + + @Override + protected void applyCustomTheme() { + + } + + private ArrayList getCustomTabsPackages(PackageManager pm) { + // Get default VIEW intent handler. + Intent activityIntent = new Intent() + .setAction(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_BROWSABLE) + .setData(Uri.fromParts("http", "", null)); + + // Get all apps that can handle VIEW intents. + List resolvedActivityList = pm.queryIntentActivities(activityIntent, 0); + ArrayList packagesSupportingCustomTabs = new ArrayList<>(); + for (ResolveInfo info : resolvedActivityList) { + Intent serviceIntent = new Intent(); + serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION); + serviceIntent.setPackage(info.activityInfo.packageName); + // Check if this package also resolves the Custom Tabs service. + if (pm.resolveService(serviceIntent, 0) != null) { + packagesSupportingCustomTabs.add(info); + } + } + return packagesSupportingCustomTabs; + } +} \ No newline at end of file diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/MainActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/MainActivity.java index aee81c32..b07f7a92 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/MainActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/MainActivity.java @@ -4,7 +4,6 @@ import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO; import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES; import android.Manifest; -import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -101,6 +100,7 @@ import ml.docilealligator.infinityforreddit.events.ChangeLockBottomAppBarEvent; import ml.docilealligator.infinityforreddit.events.ChangeNSFWEvent; import ml.docilealligator.infinityforreddit.events.ChangeRequireAuthToAccountSectionEvent; import ml.docilealligator.infinityforreddit.events.ChangeShowAvatarOnTheRightInTheNavigationDrawerEvent; +import ml.docilealligator.infinityforreddit.events.NewUserLoggedInEvent; import ml.docilealligator.infinityforreddit.events.RecreateActivityEvent; import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent; import ml.docilealligator.infinityforreddit.fragments.PostFragment; @@ -141,8 +141,6 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb private static final String NEW_ACCOUNT_NAME_STATE = "NANS"; private static final String INBOX_COUNT_STATE = "ICS"; - private static final int LOGIN_ACTIVITY_REQUEST_CODE = 0; - MultiRedditViewModel multiRedditViewModel; SubscribedSubredditViewModel subscribedSubredditViewModel; AccountViewModel accountViewModel; @@ -798,8 +796,7 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb } else if (stringId == R.string.settings) { intent = new Intent(MainActivity.this, SettingsActivity.class); } else if (stringId == R.string.add_account) { - Intent addAccountIntent = new Intent(MainActivity.this, LoginActivity.class); - startActivityForResult(addAccountIntent, LOGIN_ACTIVITY_REQUEST_CODE); + intent = new Intent(MainActivity.this, LoginActivity.class); } else if (stringId == R.string.anonymous_account) { SwitchToAnonymousMode.switchToAnonymousMode(mRedditDataRoomDatabase, mCurrentAccountSharedPreferences, mExecutor, new Handler(), false, () -> { @@ -1046,17 +1043,6 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb } } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == LOGIN_ACTIVITY_REQUEST_CODE && resultCode == Activity.RESULT_OK) { - Intent intent = new Intent(this, MainActivity.class); - startActivity(intent); - finish(); - } - - super.onActivityResult(requestCode, resultCode, data); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_activity, menu); @@ -1290,6 +1276,13 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb navigationWrapper.floatingActionButton.setVisibility(hideFab ? View.GONE : View.VISIBLE); } + @Subscribe + public void onNewUserLoggedInEvent(NewUserLoggedInEvent event) { + Intent intent = new Intent(this, MainActivity.class); + startActivity(intent); + finish(); + } + @Override public void onLongPress() { if (sectionsPagerAdapter != null) { diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/events/NewUserLoggedInEvent.java b/app/src/main/java/ml/docilealligator/infinityforreddit/events/NewUserLoggedInEvent.java new file mode 100644 index 00000000..9e729270 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/events/NewUserLoggedInEvent.java @@ -0,0 +1,4 @@ +package ml.docilealligator.infinityforreddit.events; + +public class NewUserLoggedInEvent { +} diff --git a/app/src/main/res/layout/activity_login_chrome_custom_tab.xml b/app/src/main/res/layout/activity_login_chrome_custom_tab.xml new file mode 100644 index 00000000..534433ff --- /dev/null +++ b/app/src/main/res/layout/activity_login_chrome_custom_tab.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file -- cgit v1.2.3