adds stub for notifications

This commit is contained in:
Raphael Maenle 2021-12-29 23:22:12 +01:00
parent 734b64ce3b
commit 6e3f3c44af
25 changed files with 267 additions and 43 deletions

View File

@ -1,8 +1,9 @@
package com.example.bump
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.internal.runner.InstrumentationConnection
import androidx.test.platform.app.InstrumentationRegistry
import com.example.bump.util.LocalData
import com.example.bump.util.MessageProcessor
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
@ -15,8 +16,9 @@ class MessageProcessorTest {
val code = "dydoes-unknowledgeable-indiscretion-househusbands-pot-walloper-indiscretion-discophorous-transcriptions-dydoes-poodle-faker-transcriptions-budlike"
// val messageRaw = "M1dEAxKZ5HUHCJoRkgGOvAABhqCAAAAAAGG2eKTSlKXWLDQx5B_wssZsNwsanzQID2UyUm4KKuKYKgfwH5MG2N-qzt6K4mg3pfZmWPaiDB9PiqlX236k6zo9Yvvq"
val messageRaw = "M1dEAxKZ5HUHCJoRkgGOvAABhqCAAAAAAGG8afPPk380EzwcbGzNoTr_I4y6YT8hnUYcToinlgsVkaUx5K-JicdS5epZenOX4u8vVhhMvR0ebeWm_mgp6LZvTw8S"
val data = decryptMessage(code, messageRaw)
Assert.assertEquals(data, "hello")
val processor = MessageProcessor(code)
val decrypted = processor.decrypt(messageRaw)
Assert.assertEquals(decrypted, "hello")
}
@Test
@ -26,7 +28,9 @@ class MessageProcessorTest {
val test = getRandomString(32)
val encrypted = message.encrypt(test)
val decrypted = decryptMessage(code, encrypted)
val processor = MessageProcessor(code)
val decrypted = processor.decrypt(encrypted)
Assert.assertEquals(test, decrypted)
}

View File

@ -2,6 +2,9 @@ package com.example.bump
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.bump.util.Message
import com.example.bump.util.MessageProcessor
import com.example.bump.util.RestSingleton
import org.json.JSONArray
import org.json.JSONObject
@ -27,6 +30,13 @@ class ExampleInstrumentedTest {
}
}
class NotificationTest {
@Test
fun showNotification() {
}
}
class RestCryptTest{
@Test
@ -46,6 +56,8 @@ class RestCryptTest{
assertEquals(messageData, encrypted)
val data = message.decrypt(messageData)
assertEquals(data, testMessage)
val m = Message(code, data, messageEncrypted.get("timestamp").toString())
lock.countDown()
}

View File

@ -8,7 +8,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.example.bump.MainApplication"
android:name="com.example.bump.ui.MainApplication"
android:allowBackup="true"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
@ -17,7 +17,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.BumpForAndroid">
<activity
android:name=".MainActivity"
android:name="com.example.bump.ui.MainActivity"
tools:node="merge"
android:exported="true"
android:label="@string/app_name"

View File

@ -1,5 +0,0 @@
package com.example.bump
class Message {
}

View File

@ -0,0 +1,28 @@
package com.example.bump.receiver
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.example.bump.util.sendNotification
import com.maenle.bump.R
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(
"Hello",
context
)
}
}

View File

@ -0,0 +1,42 @@
package com.example.bump.receiver
import android.app.AlarmManager
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.SystemClock
import android.text.format.DateUtils
import androidx.core.app.AlarmManagerCompat
import androidx.core.content.ContextCompat
class SnoozeReceiver: BroadcastReceiver() {
private val REQUEST_CODE = 0
override fun onReceive(context: Context, intent: Intent) {
val triggerTime = SystemClock.elapsedRealtime() + DateUtils.MINUTE_IN_MILLIS
val notifyIntent = Intent(context, AlarmReceiver::class.java)
val notifyPendingIntent = PendingIntent.getBroadcast(
context,
REQUEST_CODE,
notifyIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
AlarmManagerCompat.setExactAndAllowWhileIdle(
alarmManager,
AlarmManager.ELAPSED_REALTIME_WAKEUP,
triggerTime,
notifyPendingIntent
)
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelAll()
}
}

View File

@ -1,4 +1,4 @@
package com.maenle.bump
package com.example.bump.ui
import android.Manifest
import android.annotation.SuppressLint
@ -31,7 +31,8 @@ import kotlin.math.min
import android.view.WindowInsets
import android.graphics.Insets
import com.example.bump.BumpProcessor
import com.example.bump.util.BumpProcessor
import com.example.bump.util.CameraXViewModel
class CameraFragment: Fragment() {
@ -181,7 +182,7 @@ class CameraFragment: Fragment() {
barcodes.forEach {
Log.d(TAG, it.rawValue!!)
val bump = BumpProcessor.getInstance(requireContext())
bump.addSecret(requireContext(), it.rawValue!!)
bump.addSecret(requireContext(), it.rawValue!!)
fragmentManager?.popBackStack()
}
}

View File

@ -1,18 +1,20 @@
package com.maenle.bump
package com.example.bump.ui
import android.app.NotificationChannel
import android.app.NotificationManager
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.example.bump.BumpProcessor
import com.example.bump.MessageProcessor
import com.example.bump.RestSingleton
import com.example.bump.util.BumpProcessor
import com.example.bump.util.RestSingleton
import com.maenle.bump.R
import com.maenle.bump.databinding.FragmentFirstBinding
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
/**
* A simple [Fragment] subclass as the default destination in the navigation.
@ -36,6 +38,13 @@ class FirstFragment : Fragment() {
): View? {
_binding = FragmentFirstBinding.inflate(inflater, container, false)
// TODO: Step 1.7 call create channel
createChannel(
getString(R.string.bump_notification_channel_id),
getString(R.string.bump_notification_channel_name)
)
return binding.root
}
@ -51,6 +60,35 @@ class FirstFragment : Fragment() {
}
private fun createChannel(channelId: String, channelName: String) {
// TODO: Step 1.6 START create a channel
val notificationChannel = NotificationChannel(
channelId,
channelName,
// TODO: Step 2.4 change importance
NotificationManager.IMPORTANCE_HIGH
)// TODO: Step 2.6 disable badges for this channel
.apply {
setShowBadge(false)
}
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
notificationChannel.enableVibration(true)
notificationChannel.description = getString(R.string.bump_notification_channel_id)
val notificationManager = requireActivity().getSystemService(
NotificationManager::class.java
)
notificationManager.createNotificationChannel(notificationChannel)
// TODO: Step 1.6 END create a channel
}
companion object {
fun newInstance() = FirstFragment()
}
override fun onDestroyView() {
super.onDestroyView()

View File

@ -1,4 +1,4 @@
package com.maenle.bump
package com.example.bump.ui
import android.os.Bundle
import com.google.android.material.snackbar.Snackbar
@ -9,6 +9,7 @@ import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import android.view.Menu
import android.view.MenuItem
import com.maenle.bump.R
import com.maenle.bump.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {

View File

@ -1,7 +1,9 @@
package com.example.bump
package com.example.bump.ui
import android.app.Application
import android.content.Context
import com.example.bump.util.BumpProcessor
import com.example.bump.util.RestSingleton
// Not object class. AndroidManifest.xml error happen.
class MainApplication : Application() {

View File

@ -1,4 +1,4 @@
package com.maenle.bump
package com.example.bump.ui
import android.os.Bundle
import androidx.fragment.app.Fragment

View File

@ -1,15 +1,11 @@
package com.example.bump
package com.example.bump.util
import android.content.Context
import android.os.Handler
import android.os.Looper
import com.android.volley.toolbox.JsonObjectRequest
import com.maenle.bump.MainActivity
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import javax.inject.Inject
class BumpProcessor constructor(context: Context) {

View File

@ -1,4 +1,4 @@
package com.maenle.bump
package com.example.bump.util
import android.app.Application
import android.util.Log

View File

@ -1,4 +1,4 @@
package com.example.bump
package com.example.bump.util
import android.content.Context
import android.content.Context.MODE_PRIVATE

View File

@ -0,0 +1,10 @@
package com.example.bump.util
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.*
class Message(var sender: String, var data: String, timestamp: String) {
val timestamp = LocalDate.parse(timestamp, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"))
}

View File

@ -1,4 +1,4 @@
package com.example.bump
package com.example.bump.util
import java.util.Base64
import javax.crypto.spec.PBEKeySpec
@ -7,7 +7,7 @@ import com.macasaet.fernet.Key
import com.macasaet.fernet.Token
import com.macasaet.fernet.StringValidator
import com.macasaet.fernet.Validator
import com.maenle.bump.MainActivity
import com.example.bump.ui.MainActivity
import java.math.BigInteger
import java.security.SecureRandom
import java.time.Duration
@ -80,7 +80,7 @@ class MessageProcessor(code: String, private val salt: ByteArray? = null) {
val decodedMessage : ByteArray = Base64.getUrlDecoder().decode(message)
salt = decodedMessage.copyOfRange(0, SALT_LENGTH)
val iterationsDecoded = decodedMessage.copyOfRange(SALT_LENGTH, SALT_LENGTH+ ITERATIONS_LENGTH)
val iterationsDecoded = decodedMessage.copyOfRange(SALT_LENGTH, SALT_LENGTH + ITERATIONS_LENGTH)
val tokenString = String(Base64.getUrlEncoder().encode(decodedMessage.copyOfRange(20, decodedMessage.size)))
iterations = BigInteger(iterationsDecoded).toInt()

View File

@ -0,0 +1,91 @@
package com.example.bump.util
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat
import com.example.bump.receiver.SnoozeReceiver
import com.example.bump.ui.MainActivity
import com.maenle.bump.R
// Notification ID.
private val NOTIFICATION_ID = 0
private val REQUEST_CODE = 0
private val FLAGS = 0
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
// Create the content intent for the notification, which launches
// this activity
// TODO: Step 1.11 create intent
val contentIntent = Intent(applicationContext, MainActivity::class.java)
// TODO: Step 1.12 create PendingIntent
val contentPendingIntent = PendingIntent.getActivity(
applicationContext,
NOTIFICATION_ID,
contentIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.ic_launcher_foreground
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
.bigPicture(eggImage)
.bigLargeIcon(null)
// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
applicationContext,
REQUEST_CODE,
snoozeIntent,
FLAGS)
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
// Build the notification
val builder = NotificationCompat.Builder(
applicationContext,
applicationContext.getString(R.string.bump_notification_channel_id)
)
// TODO: Step 1.8 use the new 'breakfast' notification channel
// TODO: Step 1.3 set title, text and icon to builder
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(applicationContext
.getString(R.string.notification_title))
.setContentText(messageBody)
// TODO: Step 1.13 set content intent
.setContentIntent(contentPendingIntent)
.setAutoCancel(true)
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
// TODO: Step 2.3 add snooze action
.addAction(
R.drawable.ic_launcher_foreground,
applicationContext.getString(R.string.snooze),
snoozePendingIntent
)
// TODO: Step 2.5 set priority
.setPriority(NotificationCompat.PRIORITY_HIGH)
// TODO: Step 1.4 call notify
notify(NOTIFICATION_ID, builder.build())
}
// TODO: Step 1.14 Cancel all notifications
/**
* Cancels all notifications.
*
*/
fun NotificationManager.cancelNotifications() {
cancelAll()
}

View File

@ -1,11 +1,11 @@
package com.example.bump
package com.example.bump.util
import android.content.Context
import android.util.Log
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.toolbox.*
import com.maenle.bump.MainActivity
import com.example.bump.ui.MainActivity
import org.json.JSONArray
import org.json.JSONObject

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
tools:context="com.example.bump.ui.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondFragment">
tools:context="com.example.bump.ui.SecondFragment">
<!--androidx.camera.view.PreviewView
android:id="@+id/viewFinder"

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
tools:context="com.example.bump.ui.FirstFragment">
<TextView
android:id="@+id/textview_first"

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondFragment">
tools:context="com.example.bump.ui.SecondFragment">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"

View File

@ -1,7 +1,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.maenle.bump.MainActivity">
tools:context="com.maenle.bump.com.example.bump.ui.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"

View File

@ -7,7 +7,7 @@
<fragment
android:id="@+id/FirstFragment"
android:name="com.maenle.bump.FirstFragment"
android:name="com.example.bump.ui.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first">
@ -17,7 +17,7 @@
</fragment>
<fragment
android:id="@+id/SecondFragment"
android:name="com.maenle.bump.SecondFragment"
android:name="com.example.bump.ui.SecondFragment"
android:label="@string/second_fragment_label"
tools:layout="@layout/fragment_second">
@ -27,7 +27,7 @@
</fragment>
<fragment
android:id="@+id/CameraFragment"
android:name="com.maenle.bump.CameraFragment"
android:name="com.example.bump.ui.CameraFragment"
android:label="@string/camera_fragment_label"
tools:layout="@layout/fragment_camera">

View File

@ -14,4 +14,8 @@
<string name="camera_fragment_label">Camera Fragment</string>
<string name="preference_file_key">code_file_key</string>
<string name="code_key">code_key</string>
<string name="notification_title">Bump</string>
<string name="bump_notification_channel_id">bump_id</string>
<string name="snooze">Snooze</string>
<string name="bump_notification_channel_name">Bump</string>
</resources>