-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introducing Notifications #637
base: main
Are you sure you want to change the base?
Changes from all commits
8281125
5791015
7e05cf8
6ea307f
2f35e44
37ad439
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package com.pennapps.labs.pennmobile | ||
|
||
import StudentLifeRf2 | ||
import android.Manifest | ||
import android.content.Context | ||
import android.content.SharedPreferences | ||
import android.content.pm.PackageManager | ||
|
@@ -20,8 +21,10 @@ import android.view.inputmethod.InputMethodManager | |
import android.webkit.CookieManager | ||
import android.widget.TextView | ||
import android.widget.Toast | ||
import androidx.activity.result.contract.ActivityResultContracts | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.coordinatorlayout.widget.CoordinatorLayout | ||
import androidx.core.content.ContextCompat | ||
import androidx.core.graphics.ColorUtils | ||
import androidx.fragment.app.Fragment | ||
import androidx.fragment.app.FragmentManager | ||
|
@@ -35,6 +38,7 @@ import com.google.firebase.analytics.FirebaseAnalytics | |
import com.google.gson.GsonBuilder | ||
import com.google.gson.reflect.TypeToken | ||
import com.pennapps.labs.pennmobile.api.CampusExpress | ||
import com.pennapps.labs.pennmobile.api.NotificationAPI | ||
import com.pennapps.labs.pennmobile.api.OAuth2NetworkManager | ||
import com.pennapps.labs.pennmobile.api.Platform | ||
import com.pennapps.labs.pennmobile.api.Serializer | ||
|
@@ -77,6 +81,18 @@ class MainActivity : AppCompatActivity() { | |
private lateinit var mFirebaseAnalytics: FirebaseAnalytics | ||
val mNetworkManager by lazy { OAuth2NetworkManager(this) } | ||
|
||
// Declare the launcher at the top of your Activity/Fragment: | ||
private val requestPermissionLauncher = | ||
registerForActivityResult( | ||
ActivityResultContracts.RequestPermission(), | ||
) { isGranted: Boolean -> | ||
if (isGranted) { | ||
// FCM SDK (and your app) can post notifications. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If isGranted is true, does that just mean you can send notifications even though nothing is written here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doubling this question Is this something where we can choose to do something (ex: sending a "success!" message to the user), but it's also okay not to do anything (since notifications will be shown as enabled in the background?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we have to do anything, but having TODOs in main is generally considered bad practice. What behavior should we expect if notifications aren't enabled? Is this information generally available or should it be stored somewhere? (oops, answered by scrolling down LOL) I don't think we have to do anything then. How do other ppl generally use this function? |
||
} else { | ||
// TODO: Inform user that that your app will not show notifications. | ||
} | ||
} | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
if (Build.VERSION.SDK_INT > 28) { | ||
setTheme(R.style.DarkModeApi29) | ||
|
@@ -91,6 +107,7 @@ class MainActivity : AppCompatActivity() { | |
setTheme(R.style.DarkBackground) | ||
} | ||
Utils.getCurrentSystemTime() | ||
askNotificationPermission() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why are we asking for notification permission every time the activity starts? It is probably annoying if they don't want notifications and the app keeps asking... I feel like the app should just ask once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. udp: I didn't realize that android had a "never ask again" feature. Someone correct me if I'm wrong, but this case should be handled right. |
||
|
||
setSupportActionBar(binding.include.toolbar) | ||
fragmentManager = supportFragmentManager | ||
|
@@ -132,6 +149,25 @@ class MainActivity : AppCompatActivity() { | |
} | ||
} | ||
|
||
private fun askNotificationPermission() { | ||
// This is only necessary for API level >= 33 (TIRAMISU) | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { | ||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == | ||
PackageManager.PERMISSION_GRANTED | ||
) { | ||
// FCM SDK (and your app) can post notifications. | ||
} else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { | ||
// TODO: display an educational UI explaining to the user the features that will be enabled | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some method to keep track of the user already rejecting notifications? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems the "shouldShowRequestPermissionRationale" function actually checks the Manifest.permission.POST_NOTIFICATIONS to see whether these permissions was just rejected (meaning ask again on-create), or if it was rejected + "don't show again" box was checked (meaning don't ask again on-create) When a user rejects, the specific type of rejection is stored within Manifest.permission.POST_NOTIFICATIONS. Pretty Cool There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again, try to clean up TODOs. Personally, I don't believe we really need an explanation for this and am comfortable with doing nothing. |
||
// by them granting the POST_NOTIFICATION permission. This UI should provide the user | ||
// "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission. | ||
// If the user selects "No thanks," allow the user to continue without notifications. | ||
} else { | ||
// Directly ask for the permission | ||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) | ||
} | ||
} | ||
} | ||
|
||
private fun onExpandableBottomNavigationItemSelected() { | ||
binding.include.expandableBottomBar.setOnNavigationItemSelectedListener { item -> | ||
val position = | ||
|
@@ -319,6 +355,7 @@ class MainActivity : AppCompatActivity() { | |
private var mStudentLifeRf2: StudentLifeRf2? = null | ||
private var mPlatform: Platform? = null | ||
private var mCampusExpress: CampusExpress? = null | ||
private var mNotificationAPI: NotificationAPI? = null | ||
|
||
@JvmStatic | ||
val campusExpressInstance: CampusExpress | ||
|
@@ -383,6 +420,31 @@ class MainActivity : AppCompatActivity() { | |
return mStudentLifeRf2!! | ||
} | ||
|
||
val notificationAPIInstance: NotificationAPI | ||
get() { | ||
if (mNotificationAPI == null) { | ||
val okHttpClient = | ||
OkHttpClient | ||
.Builder() | ||
.connectTimeout(35, TimeUnit.SECONDS) | ||
.readTimeout(35, TimeUnit.SECONDS) | ||
.writeTimeout(35, TimeUnit.SECONDS) | ||
.build() | ||
|
||
val retrofit = | ||
Retrofit | ||
.Builder() | ||
.baseUrl("https://pennmobile.org/api/") | ||
.client(okHttpClient) | ||
.addConverterFactory(ScalarsConverterFactory.create()) | ||
.addConverterFactory(GsonConverterFactory.create()) | ||
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) | ||
.build() | ||
mNotificationAPI = retrofit.create(NotificationAPI::class.java) | ||
} | ||
return mNotificationAPI!! | ||
} | ||
|
||
@JvmStatic | ||
val studentLifeInstance: StudentLife | ||
get() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.pennapps.labs.pennmobile.api | ||
|
||
import okhttp3.ResponseBody | ||
import retrofit2.Response | ||
import retrofit2.http.DELETE | ||
import retrofit2.http.Header | ||
import retrofit2.http.POST | ||
import retrofit2.http.Path | ||
|
||
interface NotificationAPI { | ||
@POST("user/notifications/tokens/android/{token}/") | ||
suspend fun sendNotificationToken( | ||
@Header("Authorization") bearerToken: String, | ||
@Path("token") token: String, | ||
): Response<ResponseBody> | ||
|
||
@DELETE("user/notifications/tokens/android/{token}/") | ||
suspend fun deleteNotificationToken( | ||
@Path("token") token: String, | ||
): Response<ResponseBody> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.pennapps.labs.pennmobile.api.viewmodels | ||
|
||
import android.util.Log | ||
import androidx.lifecycle.ViewModel | ||
import com.pennapps.labs.pennmobile.api.NotificationAPI | ||
|
||
// Currently only include logic for notifications, would add more network handling afterwards (TBD) | ||
|
||
class LoginWebviewViewmodel : ViewModel() { | ||
suspend fun sendToken( | ||
mNotificationAPI: NotificationAPI, | ||
notGuest: Boolean, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we negate this here. (I know our guest mode stuff is kinda fucked up but notGuest is just an extremely cursed variable name) |
||
bearerToken: String, | ||
notifToken: String, | ||
) { | ||
try { | ||
if (notGuest) { | ||
val response = mNotificationAPI.sendNotificationToken(bearerToken, notifToken) | ||
if (response.isSuccessful) { | ||
Log.i("Notification Token", "Successfully updated token") | ||
} else { | ||
Log.i("Notification Token", "Error updating token: ${response.code()} ${response.message()}") | ||
} | ||
} | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this have parens but the other imports don't? Can we standardize formatting?