diff --git a/android/build.gradle b/android/build.gradle
index 47fcc4e03..aa44779c6 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -40,10 +40,18 @@ if (localPropFile.exists()) {
}
android {
- compileSdkVersion 34
namespace "com.thebluealliance.androidclient"
signingConfigs {
+ if (localProps.containsKey("debug.key")) {
+ debug {
+ storeFile file(localProps.getProperty("debug.key"))
+ storePassword localProps.getProperty("debug.key.password")
+ keyAlias localProps.getProperty("debug.key.alias")
+ keyPassword localProps.getProperty("debug.key.aliasPass")
+ }
+ }
+
release {
storeFile file(localProps.getProperty("release.key", "somefile.jks"))
storePassword localProps.getProperty("release.key.password", "notRealPassword")
@@ -94,8 +102,9 @@ android {
defaultConfig {
applicationId "com.thebluealliance.androidclient"
+ compileSdk 34
minSdkVersion 19
- targetSdkVersion 31
+ targetSdkVersion 34
versionCode versionNum
versionName version.toString()
multiDexEnabled true
@@ -240,7 +249,7 @@ dependencies {
// See http://developer.android.com/google/play-services/setup.html
implementation 'com.google.guava:guava:24.1-jre'
// implementation "com.google.firebase:firebase-bom:31.2.3"
- implementation "com.google.android.gms:play-services-base:18.2.0"
+ implementation "com.google.android.gms:play-services-base:18.3.0"
implementation "com.google.android.gms:play-services-analytics:18.0.4"
implementation "com.google.firebase:firebase-messaging:23.4.0"
implementation "com.google.android.gms:play-services-auth:20.7.0"
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 22ed2e90c..c2b19a377 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -14,6 +14,8 @@
+
+
diff --git a/android/src/main/java/com/thebluealliance/androidclient/activities/LaunchActivity.java b/android/src/main/java/com/thebluealliance/androidclient/activities/LaunchActivity.java
index f4c4d8a1f..75814f039 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/activities/LaunchActivity.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/activities/LaunchActivity.java
@@ -1,16 +1,22 @@
package com.thebluealliance.androidclient.activities;
+import android.Manifest;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.ContextCompat;
import com.thebluealliance.androidclient.BuildConfig;
import com.thebluealliance.androidclient.Constants;
import com.thebluealliance.androidclient.TbaLogger;
import com.thebluealliance.androidclient.Utilities;
+import com.thebluealliance.androidclient.accounts.AccountController;
import com.thebluealliance.androidclient.background.RecreateSearchIndexes;
import com.thebluealliance.androidclient.background.firstlaunch.LoadTBADataWorker;
import com.thebluealliance.androidclient.datafeed.status.TBAStatusController;
@@ -28,6 +34,9 @@ public class LaunchActivity extends AppCompatActivity {
@Inject Cache mDatafeedCache;
@Inject SharedPreferences mSharedPreferences;
+ @Inject
+ AccountController mAccountController;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -35,6 +44,10 @@ protected void onCreate(Bundle savedInstanceState) {
// Create intent to launch data download activity
Intent redownloadIntent = new Intent(this, RedownloadActivity.class);
boolean redownload = checkDataRedownload(redownloadIntent);
+ boolean needsToRequestNotificationPermission = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
+ && mAccountController.isMyTbaEnabled();
+
if (mSharedPreferences.getBoolean(Constants.ALL_DATA_LOADED_KEY, false) && !redownload) {
if (Intent.ACTION_VIEW.equals(getIntent().getAction())) {
Uri data = getIntent().getData();
@@ -47,19 +60,21 @@ protected void onCreate(Bundle savedInstanceState) {
if (intent != null) {
startActivity(intent);
finish();
- return;
} else {
goToHome();
- return;
}
} else {
goToHome();
- return;
}
} else {
goToHome();
- return;
}
+ } else if (needsToRequestNotificationPermission) {
+ // Starting with Android 33, we need to request notification permissions
+ Toast.makeText(this, "Notification permission not found!", Toast.LENGTH_LONG).show();
+ Intent mytbaIntent = new Intent(this, MyTBAOnboardingActivity.class);
+ startActivity(mytbaIntent);
+ finish();
} else if (redownload) {
// Start redownload activity
startActivity(redownloadIntent);
diff --git a/android/src/main/java/com/thebluealliance/androidclient/activities/MyTBAOnboardingActivity.java b/android/src/main/java/com/thebluealliance/androidclient/activities/MyTBAOnboardingActivity.java
index ed0094d09..cfd4c1235 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/activities/MyTBAOnboardingActivity.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/activities/MyTBAOnboardingActivity.java
@@ -1,39 +1,37 @@
package com.thebluealliance.androidclient.activities;
-import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.view.View;
-import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import com.thebluealliance.androidclient.R;
-import com.thebluealliance.androidclient.TbaLogger;
-import com.thebluealliance.androidclient.accounts.AccountController;
-import com.thebluealliance.androidclient.auth.AuthProvider;
import com.thebluealliance.androidclient.databinding.ActivityMytbaOnboardingBinding;
+import com.thebluealliance.androidclient.mytba.MyTbaOnboardingController;
import com.thebluealliance.androidclient.views.MyTBAOnboardingViewPager;
import javax.inject.Inject;
-import javax.inject.Named;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class MyTBAOnboardingActivity extends AppCompatActivity
- implements MyTBAOnboardingViewPager.Callbacks{
+ implements MyTBAOnboardingViewPager.Callbacks,
+ MyTbaOnboardingController.MyTbaOnboardingCallbacks {
private static final String MYTBA_LOGIN_COMPLETE = "mytba_login_complete";
- private static final int SIGNIN_CODE = 254;
private ActivityMytbaOnboardingBinding mBinding;
private boolean isMyTBALoginComplete = false;
- @Inject @Named("firebase_auth") AuthProvider mAuthProvider;
- @Inject AccountController mAccountController;
+ @Inject
+ MyTbaOnboardingController mMyTbaOnboardingController;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -67,49 +65,57 @@ public void onPageSelected(int position) {
mBinding.cancelButton.setOnClickListener((View view) -> finish());
mBinding.continueButton.setOnClickListener(this::onContinueClick);
+
+ mMyTbaOnboardingController.registerActivityCallbacks(this, this);
}
private void updateContinueButtonText() {
- if (mBinding.mytbaViewPager.isOnLoginPage()) {
+ if (mBinding.mytbaViewPager.isDone()) {
mBinding.continueButtonLabel.setText(R.string.finish_caps);
+ } else if (mBinding.mytbaViewPager.isOnLoginPage()) {
+ mBinding.continueButtonLabel.setText(R.string.continue_caps);
} else {
mBinding.continueButtonLabel.setText(R.string.skip_intro_caps);
}
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == SIGNIN_CODE) {
- if (resultCode == RESULT_OK) {
- mAuthProvider.userFromSignInResult(requestCode, resultCode, data)
- .subscribe(user -> {
- TbaLogger.d("User logged in: " + user.getEmail());
- mBinding.mytbaViewPager.setUpForLoginSuccess();
- isMyTBALoginComplete = true;
- mAccountController.onAccountConnect(MyTBAOnboardingActivity.this, user);
- }, throwable -> {
- TbaLogger.e("Error logging in");
- throwable.printStackTrace();
- mAccountController.setMyTbaEnabled(false);
- });
- } else if (resultCode == RESULT_CANCELED) {
- Toast.makeText(this, "Sign in canceled", Toast.LENGTH_LONG).show();
- mBinding.mytbaViewPager.setUpForLoginPrompt();
- }
+ public void onLoginSuccess() {
+ mBinding.mytbaViewPager.setUpForLoginSuccess();
+ isMyTBALoginComplete = true;
+
+ if (!mBinding.mytbaViewPager.isDone()) {
+ mBinding.mytbaViewPager.advance();
}
}
@Override
- protected void onSaveInstanceState(Bundle outState) {
+ public void onLoginFailed() {
+ mBinding.mytbaViewPager.setUpForLoginPrompt();
+ }
+
+ @Override
+ public void onPermissionResult(boolean isGranted) {
+ mBinding.mytbaViewPager.setUpForPermissionResult(isGranted);
+
+ if (isGranted) {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(MYTBA_LOGIN_COMPLETE, isMyTBALoginComplete);
}
private void onContinueClick(View view) {
- if (mBinding.mytbaViewPager.isOnLoginPage()) {
+ if (mBinding.mytbaViewPager.isDone()) {
// On the last page, the "continue" button turns into a "finish" button
finish();
+ } else if (mBinding.mytbaViewPager.isOnLoginPage()) {
+ // If there is an additional page after login, go there
+ mBinding.mytbaViewPager.advance();
} else {
// On other pages, the "continue" button becomes a "skip intro" button
mBinding.mytbaViewPager.scrollToLoginPage();
@@ -118,12 +124,12 @@ private void onContinueClick(View view) {
@Override
public void onSignInButtonClicked() {
- Intent signInIntent = mAuthProvider.buildSignInIntent();
- if (signInIntent != null) {
- startActivityForResult(signInIntent, SIGNIN_CODE);
- } else {
- Toast.makeText(this, R.string.mytba_no_signin_intent, Toast.LENGTH_SHORT).show();
- TbaLogger.e("Unable to get login Intent");
- }
+ mMyTbaOnboardingController.launchSignIn(this);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+ @Override
+ public void onEnableNotificationsButtonClicked() {
+ mMyTbaOnboardingController.launchNotificationPermissionRequest(this);
}
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/activities/OnboardingActivity.java b/android/src/main/java/com/thebluealliance/androidclient/activities/OnboardingActivity.java
index c5223b188..92240bc51 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/activities/OnboardingActivity.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/activities/OnboardingActivity.java
@@ -7,11 +7,12 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
-import android.widget.Toast;
+import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -19,12 +20,11 @@
import com.thebluealliance.androidclient.Constants;
import com.thebluealliance.androidclient.R;
import com.thebluealliance.androidclient.TbaLogger;
-import com.thebluealliance.androidclient.accounts.AccountController;
import com.thebluealliance.androidclient.adapters.FirstLaunchPagerAdapter;
-import com.thebluealliance.androidclient.auth.AuthProvider;
import com.thebluealliance.androidclient.background.firstlaunch.LoadTBADataWorker;
import com.thebluealliance.androidclient.databinding.ActivityOnboardingBinding;
import com.thebluealliance.androidclient.helpers.ConnectionDetector;
+import com.thebluealliance.androidclient.mytba.MyTbaOnboardingController;
import com.thebluealliance.androidclient.views.MyTBAOnboardingViewPager;
import org.jetbrains.annotations.NotNull;
@@ -33,14 +33,14 @@
import java.util.UUID;
import javax.inject.Inject;
-import javax.inject.Named;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class OnboardingActivity extends AppCompatActivity
implements LoadTBADataWorker.LoadTBADataCallbacks,
- MyTBAOnboardingViewPager.Callbacks {
+ MyTBAOnboardingViewPager.Callbacks,
+ MyTbaOnboardingController.MyTbaOnboardingCallbacks {
private static final String CURRENT_LOADING_MESSAGE_KEY = "current_loading_message";
private static final String LOADING_COMPLETE = "loading_complete";
@@ -48,19 +48,20 @@ public class OnboardingActivity extends AppCompatActivity
private static final String WELCOME_PAGER_STATE = "welcome_pager_state";
private static final String MYTBA_PAGER_STATE = "mytba_pager_state";
private static final String LOAD_TASK_UUID = "load_task_uuid";
- private static final int SIGNIN_CODE = 254;
private ActivityOnboardingBinding mBinding;
private String currentLoadingMessage = "";
private boolean isDataFinishedLoading = false;
private boolean isMyTBALoginComplete = false;
- private @Nullable UUID dataLoadTask = null;
- @Inject @Named("firebase_auth") AuthProvider mAuthProvider;
- @Inject AccountController mAccountController;
+ private boolean didGrantNotificationPermission = Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU;
+ private @Nullable UUID dataLoadTask = null;
@Inject SharedPreferences mPreferences;
+ @Inject
+ MyTbaOnboardingController mMyTbaOnboardingController;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -121,12 +122,14 @@ protected void onCreate(Bundle savedInstanceState) {
mBinding.mytbaViewPager.setUpForLoginPrompt();
}
- mBinding.continueToEnd.setOnClickListener(this::onContinueToEndClient);
+ mBinding.continueToEnd.setOnClickListener(this::onContinueToEndClick);
mBinding.welcomeNextPage.setOnClickListener((View view) -> beginLoadingIfConnected());
mBinding.finish.setOnClickListener((View view) -> {
startActivity(new Intent(this, HomeActivity.class));
finish();
});
+
+ mMyTbaOnboardingController.registerActivityCallbacks(this, this);
}
@Override
@@ -148,33 +151,35 @@ protected void onSaveInstanceState(@NotNull Bundle outState) {
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == SIGNIN_CODE) {
- if (resultCode == RESULT_OK) {
- mAuthProvider.userFromSignInResult(requestCode, resultCode, data)
- .subscribe(user -> {
- TbaLogger.d("User logged in: " + user.getEmail());
- mBinding.mytbaViewPager.setUpForLoginSuccess();
- isMyTBALoginComplete = true;
- mAccountController.onAccountConnect(OnboardingActivity.this, user);
- }, throwable -> {
- TbaLogger.e("Error logging in");
- throwable.printStackTrace();
- mAccountController.setMyTbaEnabled(false);
- });
- } else if (resultCode == RESULT_CANCELED) {
- Toast.makeText(this, "Sign in canceled", Toast.LENGTH_LONG).show();
- mBinding.mytbaViewPager.setUpForLoginPrompt();
- }
+ public void onLoginSuccess() {
+ mBinding.mytbaViewPager.setUpForLoginSuccess();
+ isMyTBALoginComplete = true;
+
+ if (!mBinding.mytbaViewPager.isDone()) {
+ mBinding.mytbaViewPager.advance();
+ }
+ }
+
+ @Override
+ public void onLoginFailed() {
+ mBinding.mytbaViewPager.setUpForLoginPrompt();
+ }
+
+ @Override
+ public void onPermissionResult(boolean isGranted) {
+ didGrantNotificationPermission = isGranted;
+ mBinding.mytbaViewPager.setUpForPermissionResult(isGranted);
+
+ if (isGranted) {
+ mBinding.viewPager.setCurrentItem(2);
}
}
- private void onContinueToEndClient(View view) {
+ private void onContinueToEndClick(View view) {
// If myTBA hasn't been activated yet, prompt the user one last time to sign in
- if (!mBinding.mytbaViewPager.isOnLoginPage()) {
+ if (mBinding.mytbaViewPager.isBeforeLoginPage()) {
mBinding.mytbaViewPager.scrollToLoginPage();
- } else if (!isMyTBALoginComplete) {
+ } else if (mBinding.mytbaViewPager.isOnLoginPage() && !isMyTBALoginComplete) {
// Only show this dialog if play services are actually available
new AlertDialog.Builder(this)
.setTitle(getString(R.string.mytba_prompt_title))
@@ -189,6 +194,22 @@ private void onContinueToEndClient(View view) {
mBinding.viewPager.setCurrentItem(2);
dialog.dismiss();
}).create().show();
+ } else if (mBinding.mytbaViewPager.isOnNotificationPermissionPage() && !didGrantNotificationPermission) {
+ new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.mytba_prompt_notif_permission))
+ .setMessage(getString(R.string.mytba_prompt_notif_permission_message))
+ .setCancelable(false)
+ .setPositiveButton(R.string.mytba_prompt_yes, (dialog, dialogId) -> {
+ // Do nothing; allow user to enable myTBA
+ dialog.dismiss();
+ })
+ .setNegativeButton(R.string.mytba_prompt_cancel, (dialog, dialogId) -> {
+ // Scroll to the last page
+ mBinding.viewPager.setCurrentItem(2);
+ dialog.dismiss();
+ }).create().show();
+ } else if (!mBinding.mytbaViewPager.isDone()) {
+ mBinding.mytbaViewPager.advance();
} else {
mBinding.viewPager.setCurrentItem(2);
}
@@ -360,12 +381,12 @@ public void onProgressUpdate(LoadTBADataWorker.LoadProgressInfo info) {
@Override
public void onSignInButtonClicked() {
- Intent signInIntent = mAuthProvider.buildSignInIntent();
- if (signInIntent != null) {
- startActivityForResult(signInIntent, SIGNIN_CODE);
- } else {
- Toast.makeText(this, R.string.mytba_no_signin_intent, Toast.LENGTH_SHORT).show();
- TbaLogger.e("Unable to get login Intent");
- }
+ mMyTbaOnboardingController.launchSignIn(this);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+ @Override
+ public void onEnableNotificationsButtonClicked() {
+ mMyTbaOnboardingController.launchNotificationPermissionRequest(this);
}
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/activities/settings/NotificationSettingsActivity.java b/android/src/main/java/com/thebluealliance/androidclient/activities/settings/NotificationSettingsActivity.java
index ff1b324d6..84cfbcbce 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/activities/settings/NotificationSettingsActivity.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/activities/settings/NotificationSettingsActivity.java
@@ -1,12 +1,19 @@
package com.thebluealliance.androidclient.activities.settings;
-import android.app.Fragment;
+import android.Manifest;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
-import android.preference.PreferenceFragment;
import android.view.View;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.SwitchPreference;
import com.thebluealliance.androidclient.R;
@@ -18,24 +25,45 @@ protected void onCreate(Bundle savedInstanceState) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- Fragment existingFragment = getFragmentManager().findFragmentById(android.R.id.content);
+ Fragment existingFragment = getSupportFragmentManager().findFragmentById(android.R.id.content);
if (existingFragment == null || !existingFragment.getClass().equals(NotificationSettingsFragment.class)) {
// Display the fragment as the main content.
- getFragmentManager().beginTransaction()
+ getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new NotificationSettingsFragment())
.commit();
}
}
- public static class NotificationSettingsFragment extends PreferenceFragment {
+ public static class NotificationSettingsFragment extends PreferenceFragmentCompat {
+
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
+ setPreferencesFromResource(R.xml.notification_preferences, rootKey);
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.notification_preferences);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
addPreferencesFromResource(R.xml.notification_preferences_lollipop);
}
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) {
+ addPreferencesFromResource(R.xml.notification_preferences_tiramisu);
+
+ SwitchPreference notifPermissionPref = findPreference("notification_permission_enabled");
+ ActivityResultLauncher notificationPermissionLauncher =
+ registerForActivityResult(new ActivityResultContracts.RequestPermission(), result -> notifPermissionPref.setChecked(result));
+ boolean hasPermission = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
+ notifPermissionPref.setChecked(hasPermission);
+ notifPermissionPref.setOnPreferenceChangeListener((preference, newValue) -> {
+ if (!((boolean) newValue)) {
+ getContext().revokeSelfPermissionOnKill(Manifest.permission.POST_NOTIFICATIONS);
+ } else {
+ notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
+ }
+ return true;
+ });
+ }
}
@Override
diff --git a/android/src/main/java/com/thebluealliance/androidclient/activities/settings/SettingsActivity.java b/android/src/main/java/com/thebluealliance/androidclient/activities/settings/SettingsActivity.java
index 6de2e8342..3db3b85cf 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/activities/settings/SettingsActivity.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/activities/settings/SettingsActivity.java
@@ -121,18 +121,19 @@ public void onCreate(Bundle savedInstanceState) {
Preference tbaLink = findPreference("tba_link");
tbaLink.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.thebluealliance.com")));
- final SwitchPreference mytbaEnabled = (SwitchPreference) findPreference("mytba_enabled");
+ final SwitchPreference mytbaEnabled = findPreference("mytba_enabled");
final Activity activity = getActivity();
if (mytbaEnabled != null) {
mytbaEnabled.setChecked(mAccountController.isMyTbaEnabled());
- mytbaEnabled.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- boolean enabled = mAccountController.isMyTbaEnabled();
- TbaLogger.d("myTBA is: " + enabled);
+ mytbaEnabled.setOnPreferenceChangeListener((preference, newValue) -> {
+ boolean enabled = mAccountController.isMyTbaEnabled();
+ TbaLogger.d("myTBA is: " + enabled);
+ if (!enabled) {
activity.startActivity(new Intent(getActivity(), MyTBAOnboardingActivity.class));
- return true;
+ } else {
+ mAccountController.setMyTbaEnabled(false);
}
+ return true;
});
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/adapters/MyTBAOnboardingPagerAdapter.java b/android/src/main/java/com/thebluealliance/androidclient/adapters/MyTBAOnboardingPagerAdapter.java
index 692ae3d6b..b8a9168d5 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/adapters/MyTBAOnboardingPagerAdapter.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/adapters/MyTBAOnboardingPagerAdapter.java
@@ -1,19 +1,27 @@
package com.thebluealliance.androidclient.adapters;
+import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.RequiresApi;
import androidx.viewpager.widget.PagerAdapter;
import com.thebluealliance.androidclient.R;
public class MyTBAOnboardingPagerAdapter extends PagerAdapter {
- private int mCount = 5;
- private ViewGroup mView;
+ private final int mCount;
+ private final ViewGroup mView;
public MyTBAOnboardingPagerAdapter(ViewGroup view) {
mView = view;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ // Starting with API 33, there is an additional page to request notification permissions
+ mCount = 6;
+ } else {
+ mCount = 5;
+ }
}
@Override
@@ -21,9 +29,21 @@ public int getCount() {
return mCount;
}
+ public int getLoginPageId() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ return mCount - 2;
+ } else {
+ return mCount - 1;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+ public int getNotificationPermissionPageId() {
+ return mCount - 1;
+ }
+
@Override
public Object instantiateItem(ViewGroup collection, int position) {
-
int resId = 0;
switch (position) {
case 0:
@@ -41,6 +61,9 @@ public Object instantiateItem(ViewGroup collection, int position) {
case 4:
resId = R.id.page_five;
break;
+ case 5:
+ resId = R.id.page_six;
+ break;
}
return mView.findViewById(resId);
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/auth/AuthModule.java b/android/src/main/java/com/thebluealliance/androidclient/auth/AuthModule.java
index 7ea29660d..ec56bc16e 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/auth/AuthModule.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/auth/AuthModule.java
@@ -8,6 +8,7 @@
import com.thebluealliance.androidclient.accounts.AccountModule;
import com.thebluealliance.androidclient.auth.firebase.FirebaseAuthProvider;
import com.thebluealliance.androidclient.auth.google.GoogleAuthProvider;
+import com.thebluealliance.androidclient.mytba.MyTbaOnboardingController;
import javax.annotation.Nullable;
import javax.inject.Named;
@@ -46,4 +47,10 @@ public AuthProvider provideFirebaseAuthProvider(@Nullable FirebaseAuth firebaseA
GoogleAuthProvider googleAuthProvider) {
return new FirebaseAuthProvider(firebaseAuth, googleAuthProvider);
}
+
+ @Provides
+ public MyTbaOnboardingController provideMyTbaOnbordingController(@Named("firebase_auth") AuthProvider authProvider,
+ AccountController accountController) {
+ return new MyTbaOnboardingController(authProvider, accountController);
+ }
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/auth/AuthProvider.java b/android/src/main/java/com/thebluealliance/androidclient/auth/AuthProvider.java
index b09feb5ea..44a3a4d37 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/auth/AuthProvider.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/auth/AuthProvider.java
@@ -8,10 +8,6 @@
public interface AuthProvider {
- void onStart();
-
- void onStop();
-
/**
* Check if a user is currently signed in
*/
@@ -32,7 +28,7 @@ public interface AuthProvider {
@Nullable
Intent buildSignInIntent();
- Observable extends User> userFromSignInResult(int requestCode, int resultCode, Intent data);
+ Observable extends User> userFromSignInResult(int resultCode, Intent data);
Observable extends User> signInLegacyUser();
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProvider.java b/android/src/main/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProvider.java
index 81ae46b65..e115514c0 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProvider.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProvider.java
@@ -33,16 +33,6 @@ public FirebaseAuthProvider(
mGoogleAuthProvider = googleProvider;
}
- @Override
- public void onStart() {
- mGoogleAuthProvider.onStart();
- }
-
- @Override
- public void onStop() {
- mGoogleAuthProvider.onStop();
- }
-
@Override
public boolean isUserSignedIn() {
return mFirebaseAuth != null && mFirebaseAuth.getCurrentUser() != null;
@@ -80,8 +70,8 @@ public Intent buildSignInIntent() {
}
@Override
- public Observable userFromSignInResult(int requestCode, int resultCode, Intent data) {
- Observable extends User> googleUser = mGoogleAuthProvider.userFromSignInResult(requestCode, resultCode, data);
+ public Observable userFromSignInResult(int resultCode, Intent data) {
+ Observable extends User> googleUser = mGoogleAuthProvider.userFromSignInResult(resultCode, data);
return googleUser.switchMap(user -> {
if (mFirebaseAuth == null || !(user instanceof GoogleSignInUser)) {
return Observable.empty();
@@ -90,20 +80,15 @@ public Observable userFromSignInResult(int requestCode, int
GoogleSignInUser googleSignInUser = (GoogleSignInUser) user;
AuthCredential credential = mGoogleAuthProvider
.getAuthCredential(googleSignInUser.getIdToken());
- return Observable.create(new Observable.OnSubscribe() {
- @Override
- public void call(Subscriber super FirebaseSignInUser> subscriber) {
- mFirebaseAuth.signInWithCredential(credential)
- .addOnCompleteListener(task -> {
- if (task.isSuccessful()) {
- AuthResult result = task.getResult();
- subscriber.onNext(new FirebaseSignInUser(result.getUser()));
- }
- subscriber.onCompleted();
- })
- .addOnFailureListener(subscriber::onError);
- }
- });
+ return Observable.create(subscriber -> mFirebaseAuth.signInWithCredential(credential)
+ .addOnCompleteListener(task -> {
+ if (task.isSuccessful()) {
+ AuthResult result = task.getResult();
+ subscriber.onNext(new FirebaseSignInUser(result.getUser()));
+ }
+ subscriber.onCompleted();
+ })
+ .addOnFailureListener(subscriber::onError));
});
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/auth/google/GoogleAuthProvider.java b/android/src/main/java/com/thebluealliance/androidclient/auth/google/GoogleAuthProvider.java
index e8ff60a19..6d71644ab 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/auth/google/GoogleAuthProvider.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/auth/google/GoogleAuthProvider.java
@@ -2,18 +2,15 @@
import android.content.Context;
import android.content.Intent;
-import android.os.Bundle;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.auth.api.signin.GoogleSignIn;
+import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.OptionalPendingResult;
import com.google.firebase.auth.AuthCredential;
import com.thebluealliance.androidclient.TbaLogger;
import com.thebluealliance.androidclient.accounts.AccountController;
@@ -25,14 +22,13 @@
import rx.Observable;
@Singleton
-public class GoogleAuthProvider implements AuthProvider,
- GoogleApiClient.OnConnectionFailedListener,
- GoogleApiClient.ConnectionCallbacks
-{
+public class GoogleAuthProvider implements AuthProvider {
private final Context mContext;
private final AccountController mAccountController;
- private @Nullable GoogleApiClient mGoogleApiClient;
+
+ private GoogleSignInClient mSignInClient;
+
private @Nullable GoogleSignInUser mCurrentUser;
@Inject
@@ -40,51 +36,29 @@ public GoogleAuthProvider(Context context, AccountController accountController)
mCurrentUser = null;
mAccountController = accountController;
mContext = context;
-
}
private void loadGoogleApiClient() {
String clientId = mAccountController.getWebClientId();
+ TbaLogger.d("Google client id: " + clientId);
if (clientId.isEmpty()) {
// No client id set in tba.properties, can't continue
TbaLogger.w("Oauth client ID not set, can't enable myTBA. See https://goo.gl/Swp5PC "
+ "for config details");
- mGoogleApiClient = null;
+ mSignInClient = null;
return;
}
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken(clientId)
.build();
- mGoogleApiClient = new GoogleApiClient.Builder(mContext)
- .addConnectionCallbacks(this)
- .addOnConnectionFailedListener(this)
- .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
- .build();
+ mSignInClient = GoogleSignIn.getClient(mContext, gso);
}
public AuthCredential getAuthCredential(String idToken) {
return com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, null);
}
- @Override
- public void onStart() {
- if (mGoogleApiClient != null
- && !mGoogleApiClient.isConnecting()
- && !mGoogleApiClient.isConnected()) {
- mGoogleApiClient.connect();
- }
- }
-
- @Override
- public void onStop() {
- if (mGoogleApiClient != null
- && !mGoogleApiClient.isConnecting()
- && mGoogleApiClient.isConnected()) {
- mGoogleApiClient.disconnect();
- }
- }
-
@Override
public boolean isUserSignedIn() {
return mCurrentUser != null;
@@ -97,13 +71,13 @@ public GoogleSignInUser getCurrentUser() {
@Nullable @Override
public Intent buildSignInIntent() {
- if (mGoogleApiClient == null) {
+ if (mSignInClient == null) {
// Lazy load the API client, if needed
loadGoogleApiClient();
}
- if (mGoogleApiClient != null) {
- return Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
+ if (mSignInClient != null) {
+ return mSignInClient.getSignInIntent();
}
// If we still can't get the API client, just give up
@@ -111,9 +85,9 @@ public Intent buildSignInIntent() {
}
@Override
- public Observable userFromSignInResult(int requestCode, int resultCode, Intent data) {
+ public Observable userFromSignInResult(int resultCode, Intent data) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
- boolean success = result.isSuccess();
+ boolean success = result != null && result.isSuccess();
TbaLogger.d("Google Sign In Result: " + success);
if (success) {
mCurrentUser = new GoogleSignInUser(result.getSignInAccount());
@@ -123,40 +97,7 @@ public Observable userFromSignInResult(int requestCode, int re
@WorkerThread
public Observable signInLegacyUser() {
- if (mGoogleApiClient == null) {
- TbaLogger.i("Lazy loading Google API Client for legacy sign in");
- loadGoogleApiClient();
- }
- if (mGoogleApiClient == null) {
- TbaLogger.i("Unable to get API Client for legacy sign in");
- return Observable.empty();
- }
- onStart();
- OptionalPendingResult optionalResult = Auth.GoogleSignInApi
- .silentSignIn(mGoogleApiClient);
- GoogleSignInResult result = optionalResult.await();
- onStop();
- if (result.isSuccess()) {
- return Observable.just(new GoogleSignInUser(result.getSignInAccount()));
- } else {
- TbaLogger.w("Unable to complete legacy sign in: " + result.getStatus().getStatusMessage());
- }
+ TbaLogger.w("Legacy sign in migration no longer supported");
return Observable.empty();
}
-
- @Override
- public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
- TbaLogger.w("Google API client connection failed");
- TbaLogger.w(connectionResult.getErrorMessage());
- }
-
- @Override
- public void onConnected(@Nullable Bundle bundle) {
- TbaLogger.d("Google API client connected");
- }
-
- @Override
- public void onConnectionSuspended(int i) {
-
- }
}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/gcm/GCMMessageHandler.java b/android/src/main/java/com/thebluealliance/androidclient/gcm/GCMMessageHandler.java
index 5922689ca..af1a67c7c 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/gcm/GCMMessageHandler.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/gcm/GCMMessageHandler.java
@@ -1,8 +1,10 @@
package com.thebluealliance.androidclient.gcm;
+import android.Manifest;
import android.app.Notification;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
@@ -215,6 +217,11 @@ protected void notify(Context c, BaseNotification notification, Notification bui
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(c);
int id = notification.getNotificationId();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(c, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
+ TbaLogger.w("Notification permission not granted! Skipping posting notifications...");
+ return;
+ }
+
setNotificationParams(built, c, notification.getNotificationType(), mPrefs);
TbaLogger.i("Notifying: " + id);
notificationManager.notify(id, built);
diff --git a/android/src/main/java/com/thebluealliance/androidclient/mytba/MyTbaOnboardingController.java b/android/src/main/java/com/thebluealliance/androidclient/mytba/MyTbaOnboardingController.java
new file mode 100644
index 000000000..1e0649665
--- /dev/null
+++ b/android/src/main/java/com/thebluealliance/androidclient/mytba/MyTbaOnboardingController.java
@@ -0,0 +1,97 @@
+package com.thebluealliance.androidclient.mytba;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.app.Activity.RESULT_OK;
+
+import android.Manifest;
+import android.content.Intent;
+import android.os.Build;
+import android.widget.Toast;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;
+import com.google.android.gms.common.api.Status;
+import com.thebluealliance.androidclient.R;
+import com.thebluealliance.androidclient.TbaLogger;
+import com.thebluealliance.androidclient.accounts.AccountController;
+import com.thebluealliance.androidclient.auth.AuthProvider;
+import com.thebluealliance.androidclient.auth.User;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import rx.Observable;
+
+public class MyTbaOnboardingController {
+
+ final AuthProvider mAuthProvider;
+ final AccountController mAccountController;
+
+ ActivityResultLauncher mSignInLauncher;
+ ActivityResultLauncher mNotificationPermissionLauncher;
+
+ @Inject
+ public MyTbaOnboardingController(
+ @Named("firebase_auth") AuthProvider authProvider,
+ AccountController accountController
+ ) {
+ mAuthProvider = authProvider;
+ mAccountController = accountController;
+ }
+
+ public void registerActivityCallbacks(AppCompatActivity activity, MyTbaOnboardingCallbacks callbacks) {
+ mSignInLauncher = activity.registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
+ onSignInResult(activity, callbacks, result.getResultCode(), result.getData());
+ });
+ mNotificationPermissionLauncher =
+ activity.registerForActivityResult(new ActivityResultContracts.RequestPermission(), callbacks::onPermissionResult);
+ }
+
+ private void onSignInResult(AppCompatActivity activity, MyTbaOnboardingCallbacks callbacks, int resultCode, Intent data) {
+ if (resultCode == RESULT_OK) {
+ Observable extends User> observable = mAuthProvider.userFromSignInResult(resultCode, data);
+ observable.subscribe(user -> {
+ TbaLogger.d("User logged in: " + user.getEmail());
+ mAccountController.onAccountConnect(activity, user);
+ callbacks.onLoginSuccess();
+ }, throwable -> {
+ TbaLogger.e("Error logging in", throwable);
+ mAccountController.setMyTbaEnabled(false);
+ callbacks.onLoginFailed();
+ });
+ } else if (resultCode == RESULT_CANCELED) {
+ Status signInStatus = (Status)data.getExtras().get("googleSignInStatus");
+ String errorReason = GoogleSignInStatusCodes.getStatusCodeString(signInStatus.getStatusCode());
+ Toast.makeText(activity, "Google sign in error: " + errorReason, Toast.LENGTH_LONG).show();
+ TbaLogger.w("Google sign in error: " + errorReason);
+ callbacks.onLoginFailed();
+ }
+ }
+
+ public void launchSignIn(AppCompatActivity activity) {
+ Intent signInIntent = mAuthProvider.buildSignInIntent();
+ if (signInIntent == null) {
+ Toast.makeText(activity, R.string.mytba_no_signin_intent, Toast.LENGTH_SHORT).show();
+ TbaLogger.e("Unable to get login Intent");
+ return;
+ }
+
+ mSignInLauncher.launch(signInIntent);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+ public void launchNotificationPermissionRequest(AppCompatActivity activity) {
+ TbaLogger.i("Requesting notification permission");
+ mNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
+ }
+
+ public interface MyTbaOnboardingCallbacks {
+ void onLoginSuccess();
+ void onLoginFailed();
+ void onPermissionResult(boolean isGranted);
+ }
+}
diff --git a/android/src/main/java/com/thebluealliance/androidclient/views/MyTBAOnboardingViewPager.java b/android/src/main/java/com/thebluealliance/androidclient/views/MyTBAOnboardingViewPager.java
index 3042b9af2..8a2887d3c 100644
--- a/android/src/main/java/com/thebluealliance/androidclient/views/MyTBAOnboardingViewPager.java
+++ b/android/src/main/java/com/thebluealliance/androidclient/views/MyTBAOnboardingViewPager.java
@@ -1,48 +1,37 @@
package com.thebluealliance.androidclient.views;
import android.content.Context;
+import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
-import android.widget.TextView;
+import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.viewpager.widget.ViewPager;
import com.google.android.gms.common.SignInButton;
import com.thebluealliance.androidclient.R;
import com.thebluealliance.androidclient.adapters.MyTBAOnboardingPagerAdapter;
-
-import me.relex.circleindicator.CircleIndicator;
+import com.thebluealliance.androidclient.databinding.MytbaOnboardingViewPagerBinding;
public class MyTBAOnboardingViewPager extends RelativeLayout implements View.OnClickListener {
-
- private final ViewPager mViewPager;
- private final SignInButton mSignInButton;
- private final TextView myTBATitle;
- private final TextView myTBASubtitle;
+ final private MytbaOnboardingViewPagerBinding mBinding;
+ final private MyTBAOnboardingPagerAdapter mAdapter;
private Callbacks mCallbacks;
public MyTBAOnboardingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
-
- LayoutInflater.from(context).inflate(R.layout.mytba_onboarding_view_pager, this, true);
-
- myTBATitle = (TextView) findViewById(R.id.mytba_title);
- myTBASubtitle = (TextView) findViewById(R.id.mytba_subtitle);
-
- mViewPager = (ViewPager) findViewById(R.id.view_pager);
- mViewPager.setAdapter(new MyTBAOnboardingPagerAdapter(mViewPager));
- mViewPager.setOffscreenPageLimit(10);
-
- CircleIndicator indicator = (CircleIndicator) findViewById(R.id.mytba_pager_indicator);
- indicator.setViewPager(mViewPager);
-
- mSignInButton = findViewById(R.id.google_sign_in_button);
- mSignInButton.setSize(SignInButton.SIZE_WIDE);
- mSignInButton.setOnClickListener(this);
+ mBinding = MytbaOnboardingViewPagerBinding.inflate(LayoutInflater.from(context), this);
+ mAdapter = new MyTBAOnboardingPagerAdapter(mBinding.viewPager);
+ mBinding.viewPager.setAdapter(mAdapter);
+ mBinding.viewPager.setOffscreenPageLimit(10);
+ mBinding.mytbaPagerIndicator.setViewPager(mBinding.viewPager);
+ mBinding.googleSignInButton.setSize(SignInButton.SIZE_WIDE);
+ mBinding.googleSignInButton.setOnClickListener(this);
+ mBinding.enableNotificationsButton.setOnClickListener(this);
}
public void setCallbacks(Callbacks callbacks) {
@@ -52,60 +41,76 @@ public void setCallbacks(Callbacks callbacks) {
@Override
public void onClick(View v) {
int id = v.getId();
- switch (id) {
- case R.id.google_sign_in_button:
- if (mCallbacks != null) {
- mCallbacks.onSignInButtonClicked();
- }
- break;
+ if (id == R.id.google_sign_in_button && mCallbacks != null) {
+ mCallbacks.onSignInButtonClicked();
+ } else if (id == R.id.enable_notifications_button && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ mCallbacks.onEnableNotificationsButtonClicked();
}
-
}
public void scrollToLoginPage() {
// Login page should always be the last page
- mViewPager.setCurrentItem(mViewPager.getAdapter().getCount() - 1);
+ mBinding.viewPager.setCurrentItem(mAdapter.getLoginPageId());
+ }
+
+ public boolean isBeforeLoginPage() {
+ return mBinding.viewPager.getCurrentItem() < mAdapter.getLoginPageId();
}
public boolean isOnLoginPage() {
- return mViewPager.getCurrentItem() == (mViewPager.getAdapter().getCount() - 1);
+ return mBinding.viewPager.getCurrentItem() == mAdapter.getLoginPageId();
}
- public ViewPager getViewPager() {
- return mViewPager;
+ public boolean isOnNotificationPermissionPage() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && mBinding.viewPager.getCurrentItem() == mAdapter.getNotificationPermissionPageId();
}
- public void setTitleText(@StringRes int resId) {
- myTBATitle.setText(resId);
+ public boolean isDone() {
+ return mBinding.viewPager.getCurrentItem() == mAdapter.getCount() - 1;
}
- public void setUpForNoPlayServices() {
- myTBATitle.setVisibility(View.VISIBLE);
- myTBATitle.setText(R.string.mytba_no_play_services);
+ public void advance() {
+ mBinding.viewPager.setCurrentItem(mBinding.viewPager.getCurrentItem() + 1);
+ }
+
+ public ViewPager getViewPager() {
+ return mBinding.viewPager;
+ }
- myTBASubtitle.setVisibility(View.VISIBLE);
- myTBASubtitle.setText(R.string.mytba_no_play_services_subtitle);
+ public void setTitleText(@StringRes int resId) {
+ mBinding.mytbaTitle.setText(resId);
}
public void setUpForLoginPrompt() {
- myTBATitle.setVisibility(View.VISIBLE);
- myTBATitle.setText(R.string.mytba_get_started_title);
+ mBinding.mytbaTitle.setVisibility(View.VISIBLE);
+ mBinding.mytbaTitle.setText(R.string.mytba_get_started_title);
- myTBASubtitle.setVisibility(View.VISIBLE);
- myTBASubtitle.setText(R.string.mytba_login_prompt);
+ mBinding.mytbaSubtitle.setVisibility(View.VISIBLE);
+ mBinding.mytbaSubtitle.setText(R.string.mytba_login_prompt);
}
public void setUpForLoginSuccess() {
- myTBATitle.setVisibility(View.VISIBLE);
- myTBATitle.setText(R.string.mytba_login_success);
+ mBinding.mytbaTitle.setVisibility(View.VISIBLE);
+ mBinding.mytbaTitle.setText(R.string.mytba_login_success);
- myTBASubtitle.setVisibility(View.VISIBLE);
- myTBASubtitle.setText(R.string.mytba_login_success_subtitle);
+ mBinding.mytbaSubtitle.setVisibility(View.VISIBLE);
+ mBinding.mytbaSubtitle.setText(R.string.mytba_login_success_subtitle);
- mSignInButton.setVisibility(View.GONE);
+ mBinding.googleSignInButton.setVisibility(View.GONE);
+ }
+
+ public void setUpForPermissionResult(boolean permissionGranted) {
+ if (permissionGranted) {
+ mBinding.enableNotificationsButton.setVisibility(GONE);
+ } else {
+ mBinding.enableNotificationsClarification.setVisibility(VISIBLE);
+ }
}
public interface Callbacks {
void onSignInButtonClicked();
+ @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+ void onEnableNotificationsButtonClicked();
}
}
diff --git a/android/src/main/res/layout/mytba_onboarding_view_pager.xml b/android/src/main/res/layout/mytba_onboarding_view_pager.xml
index 2a5900925..7d89e9206 100644
--- a/android/src/main/res/layout/mytba_onboarding_view_pager.xml
+++ b/android/src/main/res/layout/mytba_onboarding_view_pager.xml
@@ -168,6 +168,71 @@
android:layout_margin="16dp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
You can use myTBA anywhere you see this yellow button.
Ready to get started with myTBA?
Sign in with your Google account so we can save and sync your data.
+ Enable Notifications
+ Enable Push Notifications
+ In order to receive updates about your favorite teams and events, you\'ll need to allow The Blue Alliance to trigger notifications on your device
+ Without granting the notification permission, you will not receive updates about ongoing events. You can always enable this later in settings.
Success!
Enjoy using myTBA.
Unable to complete login
@@ -49,6 +53,10 @@
Enable Now
Maybe Later
+
+ Do you want to enable notifications?
+ You will not receive realtime updates until you do, but you can always enable it later in settings
+
Loading app configuration
Loading teams from %1$d to %2$d
diff --git a/android/src/main/res/values/strings_settings.xml b/android/src/main/res/values/strings_settings.xml
index cfc646434..636d08204 100644
--- a/android/src/main/res/values/strings_settings.xml
+++ b/android/src/main/res/values/strings_settings.xml
@@ -5,6 +5,7 @@
Notifications
General
Privacy
+ Permissions
Account
@@ -65,6 +66,7 @@
Select LED Color
Heads Up Notifications
+ Notification Permission Granted
App Theme
diff --git a/android/src/main/res/xml/notification_preferences.xml b/android/src/main/res/xml/notification_preferences.xml
index 2ce7a89f2..0bd64c232 100644
--- a/android/src/main/res/xml/notification_preferences.xml
+++ b/android/src/main/res/xml/notification_preferences.xml
@@ -23,12 +23,5 @@
android:dependency="enable_notifications"
android:summary="@string/pref_notification_led_enabled_summary"
android:title="@string/pref_notification_led_enabled" />
-
\ No newline at end of file
diff --git a/android/src/main/res/xml/notification_preferences_tiramisu.xml b/android/src/main/res/xml/notification_preferences_tiramisu.xml
new file mode 100644
index 000000000..9aea34d39
--- /dev/null
+++ b/android/src/main/res/xml/notification_preferences_tiramisu.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/src/test/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProviderTest.java b/android/src/test/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProviderTest.java
index eb6f6562f..1c616d763 100644
--- a/android/src/test/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProviderTest.java
+++ b/android/src/test/java/com/thebluealliance/androidclient/auth/firebase/FirebaseAuthProviderTest.java
@@ -35,18 +35,6 @@ public void setUp() {
mFirebaseAuthProvider = new FirebaseAuthProvider(mFirebaseAuth, mGoogleAuthProvider);
}
- @Test
- public void onStart() {
- mFirebaseAuthProvider.onStart();
- verify(mGoogleAuthProvider).onStart();
- }
-
- @Test
- public void onStop() {
- mFirebaseAuthProvider.onStop();
- verify(mGoogleAuthProvider).onStop();
- }
-
@Test
public void isUserSignedIn() {
assertFalse(mFirebaseAuthProvider.isUserSignedIn());
diff --git a/android/src/test/java/com/thebluealliance/androidclient/gcm/GCMMessageHandlerTest.java b/android/src/test/java/com/thebluealliance/androidclient/gcm/GCMMessageHandlerTest.java
index ffd31628d..84d9116f7 100644
--- a/android/src/test/java/com/thebluealliance/androidclient/gcm/GCMMessageHandlerTest.java
+++ b/android/src/test/java/com/thebluealliance/androidclient/gcm/GCMMessageHandlerTest.java
@@ -3,6 +3,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import android.Manifest;
+import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
@@ -30,6 +32,7 @@
import org.robolectric.Shadows;
import org.robolectric.android.controller.ServiceController;
import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
import java.util.Arrays;
import java.util.Collection;
@@ -62,9 +65,12 @@ public static class TestRenderSingleNotifications {
@Before
public void setUp() {
- Context applicationContext = ApplicationProvider.getApplicationContext();
+ Application applicationContext = ApplicationProvider.getApplicationContext();
mNotificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
mService = buildAndStartService();
+
+ ShadowApplication shadowApplication = Shadows.shadowOf(applicationContext);
+ shadowApplication.grantPermissions(Manifest.permission.POST_NOTIFICATIONS);
}
@ParameterizedRobolectricTestRunner.Parameters(name = "NotificationType = {0}")
@@ -129,9 +135,12 @@ public static class TestNotificationSummary {
@Before
public void setUp() {
- Context applicationContext = ApplicationProvider.getApplicationContext();
+ Application applicationContext = ApplicationProvider.getApplicationContext();
mNotificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
mService = buildAndStartService();
+
+ ShadowApplication shadowApplication = Shadows.shadowOf(applicationContext);
+ shadowApplication.grantPermissions(Manifest.permission.POST_NOTIFICATIONS);
}
@Test