Skip to content

Commit

Permalink
Changes for API 33 & 34 (#978)
Browse files Browse the repository at this point in the history
**Summary:** 
Breaking Changes:
 - https://developer.android.com/about/versions/13/behavior-changes-13
 - https://developer.android.com/about/versions/14/behavior-changes-14

The biggest thing is notification permissions being moved to a runtime thing. This means we need to extend the onboarding flow to include requesting the permission. Some other things:

 - centralize the logic for login/etc in `MyTbaOnboardingController`, since there's two entry points for this
 - stop using `startActivityForResult` in favor of the new [`ActivityResultLauncher`](https://developer.android.com/training/basics/intents/result)
 - skip posting notifications if we don't have the permission
 - Add a toggle in settings to re-request the permission if needed
  • Loading branch information
phil-lopreiato authored Jan 14, 2024
1 parent c999cd1 commit a7f1a68
Show file tree
Hide file tree
Showing 22 changed files with 497 additions and 278 deletions.
15 changes: 12 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />

<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

<!-- This is not needed with android 6.0 (API 23) anymore, since we only need this to
interact with our own Authenticator to create a TBA system account
https://developer.android.com/reference/android/Manifest.permission.html#GET_ACCOUNTS -->
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -28,13 +34,20 @@ public class LaunchActivity extends AppCompatActivity {
@Inject Cache mDatafeedCache;
@Inject SharedPreferences mSharedPreferences;

@Inject
AccountController mAccountController;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(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();
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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();
Expand All @@ -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);
}
}
Loading

0 comments on commit a7f1a68

Please sign in to comment.