Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
d102004e6b | |||
d5ceace59f | |||
625d5496e3 | |||
67fe7381aa | |||
22b2ac96b9 | |||
0e307c947a | |||
1f5fc5288b | |||
6df001ed4c | |||
c213e51cdd | |||
538974c6f6 |
1
.gitignore
vendored
@ -28,3 +28,4 @@ google-services.json
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
*.aab
|
||||
|
1
app/.gitignore
vendored
@ -1 +1,2 @@
|
||||
/build
|
||||
/release
|
||||
|
@ -8,10 +8,10 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.maenle.bump"
|
||||
minSdk 29
|
||||
minSdk 26
|
||||
targetSdk 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
versionCode 4
|
||||
versionName "0.1.2"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.maenle.bump">
|
||||
package="com.maenle.bump"
|
||||
android:versionName="0.1_alpha">
|
||||
|
||||
<uses-feature android:name="android.hardware.camera.any" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
@ -22,13 +23,13 @@
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
|
||||
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/ic_launcher_foreground"/>
|
||||
|
||||
android:resource="@drawable/ic_stat_name"/>
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@color/black"/> <!-- [END fcm_default_icon] -->
|
||||
@ -49,6 +50,10 @@
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name=".ui.MainActivity" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
@ -3,12 +3,12 @@ package com.maenle.bump
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.maenle.bump.util.sendNotification
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
import com.maenle.bump.util.BumpProcessor
|
||||
import com.maenle.bump.util.RestSingleton
|
||||
|
||||
class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
|
||||
@ -17,27 +17,32 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
*
|
||||
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
|
||||
*/
|
||||
// [START receive_message]
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
|
||||
Log.d(TAG, "From: ${remoteMessage.from}")
|
||||
// Log.d(TAG, "From: ${remoteMessage.from}")
|
||||
|
||||
// TODO Step 3.5 check messages for data
|
||||
// Check if message contains a data payload.
|
||||
var body: String? = null;
|
||||
var header: String? = null;
|
||||
remoteMessage.data.let {
|
||||
val bump = BumpProcessor.getInstance(applicationContext)
|
||||
Log.d(TAG, "Message data payload: " + remoteMessage.data)
|
||||
val a = remoteMessage.data.keys
|
||||
val value = remoteMessage.data["data"]
|
||||
value?.let {
|
||||
body = bump.decryptMessage(it)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Step 3.6 check messages for notification and call sendNotification
|
||||
// Check if message contains a notification payload.
|
||||
remoteMessage.notification?.let {
|
||||
Log.d(TAG, "Message Notification Body: ${it.body}")
|
||||
sendNotification(it.body!!)
|
||||
header = it.body
|
||||
}
|
||||
}
|
||||
// [END receive_message]
|
||||
|
||||
//TODO Step 3.2 log registration token
|
||||
sendNotification(header, body)
|
||||
|
||||
}
|
||||
|
||||
// log registration token
|
||||
// [START on_new_token]
|
||||
/**
|
||||
* Called if InstanceID token is updated. This may occur if the security of
|
||||
@ -45,12 +50,11 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
* is initially generated so this is where you would retrieve the token.
|
||||
*/
|
||||
override fun onNewToken(token: String) {
|
||||
Log.d(TAG, "Refreshed token: $token")
|
||||
|
||||
// If you want to send messages to this application instance or
|
||||
// manage this apps subscriptions on the server side, send the
|
||||
// Instance ID token to your app server.
|
||||
sendRegistrationToServer(token)
|
||||
sendRegistrationToServer()
|
||||
}
|
||||
// [END on_new_token]
|
||||
|
||||
@ -60,17 +64,16 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
*
|
||||
* @param token The new token.
|
||||
*/
|
||||
private fun sendRegistrationToServer(token: String?) {
|
||||
private fun sendRegistrationToServer() {
|
||||
val bump = BumpProcessor.getInstance(baseContext)
|
||||
bump.setFirebaseToken(baseContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and show a simple notification containing the received FCM message.
|
||||
*
|
||||
* @param messageBody FCM message body received.
|
||||
*/
|
||||
private fun sendNotification(messageBody: String) {
|
||||
private fun sendNotification(messageHeader: String?, messageBody: String?) {
|
||||
|
||||
val notificationManager = ContextCompat.getSystemService(applicationContext, NotificationManager::class.java) as NotificationManager
|
||||
notificationManager.sendNotification(messageBody, applicationContext)
|
||||
notificationManager.sendNotification(messageHeader, messageBody, applicationContext)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -110,9 +110,9 @@ class CameraFragment: Fragment() {
|
||||
previewUseCase
|
||||
)
|
||||
} catch (illegalStateException: IllegalStateException) {
|
||||
Log.e(TAG, illegalStateException.message ?: "IllegalStateException")
|
||||
// Log.e(TAG, illegalStateException.message ?: "IllegalStateException")
|
||||
} catch (illegalArgumentException: IllegalArgumentException) {
|
||||
Log.e(TAG, illegalArgumentException.message ?: "IllegalArgumentException")
|
||||
// Log.e(TAG, illegalArgumentException.message ?: "IllegalArgumentException")
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,9 +153,9 @@ class CameraFragment: Fragment() {
|
||||
analysisUseCase
|
||||
)
|
||||
} catch (illegalStateException: IllegalStateException) {
|
||||
Log.e(TAG, illegalStateException.message ?: "IllegalStateException")
|
||||
// Log.e(TAG, illegalStateException.message ?: "IllegalStateException")
|
||||
} catch (illegalArgumentException: IllegalArgumentException) {
|
||||
Log.e(TAG, illegalArgumentException.message ?: "IllegalArgumentException")
|
||||
// Log.e(TAG, illegalArgumentException.message ?: "IllegalArgumentException")
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ class CameraFragment: Fragment() {
|
||||
blockingScanned = false
|
||||
}
|
||||
.addOnFailureListener {
|
||||
Log.e(TAG, it.message ?: it.toString())
|
||||
// Log.e(TAG, it.message ?: it.toString())
|
||||
}.addOnCompleteListener {
|
||||
// When the image is from CameraX analysis use case, must call image.close() on received
|
||||
// images when finished using them. Otherwise, new images may not be received or the camera
|
||||
@ -207,7 +207,7 @@ class CameraFragment: Fragment() {
|
||||
if (isCameraPermissionGranted()) {
|
||||
bindCameraUseCases()
|
||||
} else {
|
||||
Log.e(TAG, "no camera permission")
|
||||
// Log.e(TAG, "no camera permission")
|
||||
}
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
|
@ -2,29 +2,27 @@ package com.maenle.bump.ui
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import com.maenle.bump.R
|
||||
import com.maenle.bump.databinding.FragmentFirstBinding
|
||||
import com.maenle.bump.util.*
|
||||
import com.google.zxing.WriterException
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.util.Log
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidmads.library.qrgenearator.QRGContents
|
||||
import androidmads.library.qrgenearator.QRGEncoder
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import com.google.zxing.WriterException
|
||||
import com.maenle.bump.R
|
||||
import com.maenle.bump.databinding.FragmentFirstBinding
|
||||
import com.maenle.bump.util.*
|
||||
import com.maenle.bump.util.Display
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
|
||||
/**
|
||||
* A simple [Fragment] subclass as the default destination in the navigation.
|
||||
*/
|
||||
class FirstFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentFirstBinding? = null
|
||||
@ -53,16 +51,10 @@ class FirstFragment : Fragment() {
|
||||
getString(R.string.bump_notification_channel_name)
|
||||
)
|
||||
|
||||
/*
|
||||
val notificationManager = ContextCompat.getSystemService(
|
||||
requireContext(),
|
||||
NotificationManager::class.java
|
||||
) as NotificationManager
|
||||
notificationManager.sendNotification(getString(R.string.notification), requireContext())
|
||||
*/
|
||||
|
||||
subscribeTopic()
|
||||
|
||||
bump.logUpdateCallback = { log -> updateMessageList(log) }
|
||||
updateMessageList(bump.log)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@ -76,7 +68,7 @@ class FirstFragment : Fragment() {
|
||||
binding.textviewEncryption.visibility = View.VISIBLE
|
||||
binding.idIVQrcode.visibility = View.VISIBLE
|
||||
binding.buttonView.text = getString(R.string.hide_sender)
|
||||
message?.let { binding.textviewScan.text = it.sender.replace("-", "-\u200b")+"-"}
|
||||
message?.let { binding.textviewScan.text = it.sender.replace("-", "-\u200b")}
|
||||
} else {
|
||||
binding.textviewEncryption.visibility = View.GONE
|
||||
binding.idIVQrcode.visibility = View.GONE
|
||||
@ -84,8 +76,9 @@ class FirstFragment : Fragment() {
|
||||
message?.let { binding.textviewScan.text = it.sender.replace("-", "-\u200b")}
|
||||
}
|
||||
}
|
||||
|
||||
message?.let { binding.textviewScan.text = it.sender.replace("-", "-\u200b")}
|
||||
message?.let { binding.textviewEncryption.text = it.password.replace("-", "-\u200b")}
|
||||
message?.let { binding.textviewEncryption.text = "-" + it.password.replace("-", "-\u200b")}
|
||||
message?.let {binding.idIVQrcode.setImageBitmap(generateQrCode(it.code))}
|
||||
}
|
||||
|
||||
@ -125,7 +118,7 @@ class FirstFragment : Fragment() {
|
||||
try {
|
||||
bitmap = qrgEncoder.encodeAsBitmap()
|
||||
} catch (e: WriterException) {
|
||||
Log.d("Tag", e.toString())
|
||||
// Log.d("Tag", e.toString())
|
||||
}
|
||||
|
||||
if(nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
|
||||
@ -152,15 +145,49 @@ class FirstFragment : Fragment() {
|
||||
return newBitmap
|
||||
}
|
||||
|
||||
private fun updateMessageList(log: JSONArray) {
|
||||
val messages: MutableList<MessageItem> = mutableListOf()
|
||||
val adapter = NotificationAdapter(requireContext(), messages)
|
||||
binding.nrMessages.text = log.length().toString()
|
||||
for (l in log.length() - 1 downTo 0) {
|
||||
val m = log[l] as JSONObject
|
||||
val msg = MessageItem(m.get("title") as String, message = "...")
|
||||
messages.add(msg)
|
||||
bump.asyncDecryptMessage(m.get("data") as String, messages.size-1) { data, location ->
|
||||
messages[location].message = data
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
binding.notificationList.adapter = adapter
|
||||
adapter.notifyDataSetChanged()
|
||||
binding.notificationList.setOnItemClickListener { adapterView, view, i, l ->
|
||||
run {
|
||||
if (messages[i].header.startsWith("http")) {
|
||||
val browserIntent =
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(messages[i].header))
|
||||
startActivity(browserIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.notificationList.setOnItemLongClickListener { adapterView, view, i, l ->
|
||||
messages[i].encrypted?.let {
|
||||
messages[i].message = bump.decryptMessage(messages[i].encrypted!!)
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun subscribeTopic() {
|
||||
// [START subscribe_topic]
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)
|
||||
.addOnCompleteListener { task ->
|
||||
var curmessage = getString(R.string.message_subscribed)
|
||||
if (!task.isSuccessful) {
|
||||
curmessage = getString(R.string.message_subscribe_failed)
|
||||
}
|
||||
Log.d(TAG, curmessage.toString())
|
||||
// Log.d(TAG, curmessage.toString())
|
||||
}
|
||||
// [END subscribe_topics]
|
||||
}
|
||||
@ -177,3 +204,7 @@ class FirstFragment : Fragment() {
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
|
||||
class MessageItem(public val title: String, var message: String = "...", var encrypted: String? = null) {
|
||||
public var header: String = title
|
||||
}
|
@ -1,16 +1,23 @@
|
||||
package com.maenle.bump.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
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
|
||||
import com.maenle.bump.util.BumpProcessor
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
@ -29,11 +36,31 @@ class MainActivity : AppCompatActivity() {
|
||||
appBarConfiguration = AppBarConfiguration(navController.graph)
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
|
||||
val bundle = intent.extras
|
||||
if (bundle != null) {
|
||||
Log.d("MainActivity", bundle.toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun showHelp() {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://raphael.maenle.net/post/projects/bump/"));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
for (i in 0 until menu.size()) {
|
||||
val item = menu.getItem(i)
|
||||
val spanString = SpannableString(menu.getItem(i).title.toString())
|
||||
spanString.setSpan(
|
||||
ForegroundColorSpan(Color.WHITE),
|
||||
0,
|
||||
spanString.length,
|
||||
0
|
||||
) //fix the color to white
|
||||
item.title = spanString
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -42,9 +69,18 @@ class MainActivity : AppCompatActivity() {
|
||||
// automatically handle clicks on the Home/Up button, so long
|
||||
// as you specify a parent activity in AndroidManifest.xml.
|
||||
return when (item.itemId) {
|
||||
R.id.action_settings -> true
|
||||
R.id.action_settings -> {true}
|
||||
R.id.action_clear_log -> {
|
||||
BumpProcessor.clearLog(applicationContext)
|
||||
true
|
||||
}
|
||||
R.id.action_info -> {
|
||||
showHelp()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
|
@ -4,20 +4,24 @@ import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class BumpProcessor constructor(context: Context) {
|
||||
|
||||
private var rest: RestSingleton = RestSingleton.getInstance(context)
|
||||
private lateinit var log: JSONArray
|
||||
private val rest: RestSingleton = RestSingleton.getInstance(context)
|
||||
private val local = LocalData(context)
|
||||
private var _log: JSONArray
|
||||
private val messenger = local.code?.let { MessageProcessor(it) }
|
||||
|
||||
var logUpdateCallback: ((JSONArray)->Unit)? = null
|
||||
|
||||
init {
|
||||
log = getLog(context)
|
||||
_log = getLogFromFile(context)
|
||||
startUpdateHandler(context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -29,22 +33,50 @@ class BumpProcessor constructor(context: Context) {
|
||||
INSTANCE = it
|
||||
}
|
||||
}
|
||||
fun clearLog(context: Context) {
|
||||
val logFile = File(context.filesDir, ".bump_log")
|
||||
logFile.writeText("")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val log: JSONArray
|
||||
get() {
|
||||
return _log
|
||||
}
|
||||
|
||||
fun asyncDecryptMessage(message: String, location: Int, callback: (String, Int) -> Unit) {
|
||||
thread(start = true) {
|
||||
val decrypt = decryptMessage(message)
|
||||
Handler(Looper.getMainLooper()).post {callback( decrypt, location)}
|
||||
}
|
||||
}
|
||||
|
||||
fun decryptMessage(message: String): String {
|
||||
return messenger!!.decrypt(message)
|
||||
}
|
||||
|
||||
private fun updateLog(context: Context, list: JSONArray) {
|
||||
_log = getLogFromFile(context)
|
||||
var change = false
|
||||
for(i in 0 until list.length()) {
|
||||
var exists = false
|
||||
for(j in 0 until log.length()) {
|
||||
if (list[i] == log[j]) {
|
||||
for(j in 0 until _log.length()) {
|
||||
if (list[i].toString() == _log[j].toString()) {
|
||||
exists = true
|
||||
continue
|
||||
break
|
||||
}
|
||||
}
|
||||
exists.let {
|
||||
log.put(list[i])
|
||||
if(!exists) {
|
||||
_log.put(list[i])
|
||||
addToLog(context, list[i] as JSONObject)
|
||||
change = true
|
||||
}
|
||||
}
|
||||
|
||||
if(change) {
|
||||
logUpdateCallback?.invoke(_log)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSecret(context: Context):String? {
|
||||
@ -52,11 +84,11 @@ class BumpProcessor constructor(context: Context) {
|
||||
return local.code
|
||||
}
|
||||
|
||||
fun startUpdateHandler(context: Context) {
|
||||
|
||||
val delay: Long = 30_000 //milliseconds
|
||||
private fun startUpdateHandler(context: Context) {
|
||||
val delay: Long = 60_000 //milliseconds
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
updateFromServer(context)
|
||||
handler.postDelayed(object : Runnable {
|
||||
override fun run() {
|
||||
updateFromServer(context)
|
||||
@ -65,7 +97,7 @@ class BumpProcessor constructor(context: Context) {
|
||||
}, delay)
|
||||
}
|
||||
|
||||
private fun updateFromServer(context: Context) {
|
||||
fun updateFromServer(context: Context) {
|
||||
LocalData(context).code?.let {
|
||||
val messenger = MessageProcessor(it)
|
||||
rest.list(messenger.sender) { list -> updateLog(context, list) }
|
||||
@ -74,7 +106,7 @@ class BumpProcessor constructor(context: Context) {
|
||||
|
||||
private fun addToLog(context: Context, line: JSONObject) {
|
||||
val logFile = File(context.filesDir, ".bump_log")
|
||||
logFile.appendText(line.toString())
|
||||
logFile.appendText(line.toString() + "\n")
|
||||
}
|
||||
|
||||
fun addSecret(context: Context, newCode: String): Boolean {
|
||||
@ -94,7 +126,7 @@ class BumpProcessor constructor(context: Context) {
|
||||
return newCode.length == validChars && newCode.length >= 32
|
||||
}
|
||||
|
||||
private fun getLog(context: Context): JSONArray {
|
||||
private fun getLogFromFile(context: Context): JSONArray {
|
||||
val logFile = File(context.filesDir, ".bump_log")
|
||||
val log = JSONArray()
|
||||
!logFile.exists().let {
|
||||
@ -106,7 +138,7 @@ class BumpProcessor constructor(context: Context) {
|
||||
return log
|
||||
}
|
||||
|
||||
private fun setFirebaseToken(context: Context) {
|
||||
fun setFirebaseToken(context: Context) {
|
||||
val tokenTask = FirebaseMessaging.getInstance().token
|
||||
val secret = LocalData(context).code
|
||||
val messenger = secret?.let { MessageProcessor(it) }
|
||||
|
@ -29,9 +29,9 @@ class CameraXViewModel(application: Application) : AndroidViewModel(application)
|
||||
cameraProviderLiveData!!.setValue(cameraProviderFuture.get())
|
||||
} catch (e: ExecutionException) {
|
||||
// Handle any errors (including cancellation) here.
|
||||
Log.e(TAG, "Unhandled exception", e)
|
||||
// Log.e(TAG, "Unhandled exception", e)
|
||||
} catch (e: InterruptedException) {
|
||||
Log.e(TAG, "Unhandled exception", e)
|
||||
// Log.e(TAG, "Unhandled exception", e)
|
||||
}
|
||||
},
|
||||
ContextCompat.getMainExecutor(getApplication())
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.maenle.bump.util
|
||||
|
||||
import android.util.Log
|
||||
import java.util.Base64
|
||||
import javax.crypto.spec.PBEKeySpec
|
||||
import javax.crypto.SecretKeyFactory
|
||||
@ -77,6 +78,7 @@ class MessageProcessor(code: String, private val salt: ByteArray? = null) {
|
||||
super.splitMessage(messageRaw)
|
||||
}
|
||||
fun validateAndDecryptWith(password: String, timeToLive: Long = TIME_TO_LIVE): String {
|
||||
try {
|
||||
val fernetKey = deriveMessageKeyFromPassword(password)
|
||||
val validator: Validator<String> = object : StringValidator {
|
||||
override fun getTimeToLive(): TemporalAmount {
|
||||
@ -84,6 +86,11 @@ class MessageProcessor(code: String, private val salt: ByteArray? = null) {
|
||||
}
|
||||
}
|
||||
return token.validateAndDecrypt(fernetKey, validator)
|
||||
} catch (e: Throwable) {
|
||||
e.message?.let { Log.d(TAG, it) }
|
||||
}
|
||||
return ""
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
package com.maenle.bump.util
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import com.maenle.bump.R
|
||||
import com.maenle.bump.ui.MessageItem
|
||||
|
||||
|
||||
class NotificationAdapter(context: Context, messages: MutableList<MessageItem>) :
|
||||
ArrayAdapter<MessageItem?>(context, 0, messages as List<MessageItem?>) {
|
||||
val bump = BumpProcessor(context)
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
|
||||
// Get the data item for this position
|
||||
var view: View? = convertView
|
||||
val message: MessageItem? = getItem(position)
|
||||
|
||||
// Check if an existing view is being reused, otherwise inflate the view
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(R.layout.notification_item, parent, false)
|
||||
}
|
||||
|
||||
// Lookup view for data population
|
||||
val title = view?.findViewById(R.id.title) as TextView
|
||||
val data = view.findViewById(R.id.data) as TextView
|
||||
|
||||
// Populate the data into the template view using the data object
|
||||
if (message != null) {
|
||||
title.text = message.title
|
||||
data.text = message.message
|
||||
if(data.text == "") {
|
||||
data.text = "-"
|
||||
}
|
||||
}
|
||||
|
||||
// Return the completed view to render on screen
|
||||
return view
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package com.maenle.bump.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
@ -10,16 +9,32 @@ import androidx.core.app.NotificationCompat
|
||||
import com.maenle.bump.ui.MainActivity
|
||||
import com.maenle.bump.R
|
||||
|
||||
// Notification ID.
|
||||
private val NOTIFICATION_ID = 0
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
|
||||
|
||||
|
||||
// Notification ID.
|
||||
private const val NOTIFICATION_ID = 0
|
||||
|
||||
fun NotificationManager.sendNotification(messageHeader: String?, messageBody: String?, applicationContext: Context) {
|
||||
var title = applicationContext.getString(R.string.notification_title)
|
||||
var body = ""
|
||||
|
||||
if(messageBody != null) {
|
||||
if(messageHeader != null) {
|
||||
title = messageHeader
|
||||
}
|
||||
body = messageBody
|
||||
} else if(messageHeader != null) {
|
||||
body = messageHeader
|
||||
}
|
||||
|
||||
sendParsedNotification(title, body, applicationContext)
|
||||
}
|
||||
|
||||
fun NotificationManager.sendParsedNotification(messageHeader: String, 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,
|
||||
@ -27,43 +42,39 @@ fun NotificationManager.sendNotification(messageBody: String, applicationContext
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
// TODO: Step 2.0 add style
|
||||
val eggImage = BitmapFactory.decodeResource(
|
||||
applicationContext.resources,
|
||||
R.drawable.ic_launcher_foreground
|
||||
R.drawable.ic_stat_name
|
||||
)
|
||||
|
||||
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
|
||||
val intent = Intent(applicationContext, this.javaClass)
|
||||
val action =
|
||||
PendingIntent.getBroadcast(applicationContext, 0, intent, 0)
|
||||
// Build the notification
|
||||
val builder = NotificationCompat.Builder(
|
||||
applicationContext,
|
||||
applicationContext.getString(R.string.bump_notification_channel_id)
|
||||
)
|
||||
|
||||
// TODO: Step 1.3 set title, text and icon to builder
|
||||
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||
.setContentTitle(applicationContext
|
||||
.getString(R.string.notification_title))
|
||||
.setSmallIcon(R.drawable.ic_stat_name)
|
||||
.setContentTitle(messageHeader)
|
||||
.setContentText(messageBody)
|
||||
|
||||
// TODO: Step 1.13 set content intent
|
||||
// set content intent
|
||||
.setContentIntent(contentPendingIntent)
|
||||
.setAutoCancel(true)
|
||||
|
||||
// TODO: Step 2.1 add style to builder
|
||||
// add style to builder
|
||||
.setLargeIcon(eggImage)
|
||||
|
||||
// TODO: Step 2.5 set priority
|
||||
// set priority
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
// TODO: Step 1.4 call notify
|
||||
.addAction(0, "Action", action)
|
||||
|
||||
// notify
|
||||
notify(NOTIFICATION_ID, builder.build())
|
||||
}
|
||||
|
||||
// TODO: Step 1.14 Cancel all notifications
|
||||
/**
|
||||
* Cancels all notifications.
|
||||
*
|
||||
*/
|
||||
fun NotificationManager.cancelNotifications() {
|
||||
cancelAll()
|
||||
}
|
@ -26,14 +26,14 @@ class RestSingleton constructor(context: Context){
|
||||
}
|
||||
|
||||
private val TAG = MainActivity::class.java.simpleName
|
||||
private const val URL = "http://192.168.68.127:4000/api/"
|
||||
private const val URL = "https://bump.maenle.net/api/"
|
||||
}
|
||||
|
||||
fun list(sender: String, callback: (JSONArray) -> Unit){
|
||||
val url = URL + "list/"
|
||||
val data = JSONObject()
|
||||
data.put("sender", sender)
|
||||
data.put("minutes", (60*24*365*100).toString())
|
||||
data.put("minutes", (60*24).toString())
|
||||
val jsonRequest = JsonObjectRequest(Request.Method.POST, url,
|
||||
data,
|
||||
{ response ->
|
||||
@ -100,7 +100,6 @@ class RestSingleton constructor(context: Context){
|
||||
|
||||
}
|
||||
|
||||
|
||||
private val requestQueue: RequestQueue by lazy {
|
||||
// applicationContext is key, it keeps you from leaking the
|
||||
// Activity or BroadcastReceiver if someone passes one in.
|
||||
|
15
app/src/main/res/drawable-anydpi/ic_stat_name.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="0.1725"
|
||||
android:scaleY="0.1725"
|
||||
android:translateX="0.96"
|
||||
android:translateY="0.96">
|
||||
<path
|
||||
android:pathData="M53.38,18a12,12 0,0 1,12 12v24.3A9.7,9.7 0,0 0,75 64h0a9.69,9.69 0,0 0,9.66 -9.67L84.8,30a12,12 0,0 1,24 0l-0.94,66.3a12,12 0,0 1,-12 12H31.2a12,12 0,0 1,-12 -12c0,-9.33 8,-7.41 8,-17.58 0,-8.31 -8,-7.87 -8,-16.57 0,-8 8,-8.12 8,-15.81 0.05,-8.12 -8,-9.81 -8,-16.34a12,12 0,0 1,12 -12Z"
|
||||
android:fillColor="#f7941d"/>
|
||||
</group>
|
||||
</vector>
|
BIN
app/src/main/res/drawable-hdpi/ic_stat_name.png
Normal file
After Width: | Height: | Size: 384 B |
BIN
app/src/main/res/drawable-mdpi/ic_stat_name.png
Normal file
After Width: | Height: | Size: 279 B |
@ -1,30 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
BIN
app/src/main/res/drawable-xhdpi/ic_stat_name.png
Normal file
After Width: | Height: | Size: 487 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_stat_name.png
Normal file
After Width: | Height: | Size: 686 B |
@ -1,170 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
14
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group android:scaleX="0.3375"
|
||||
android:scaleY="0.3375"
|
||||
android:translateX="32.4"
|
||||
android:translateY="32.4">
|
||||
<path
|
||||
android:pathData="M53.38,18a12,12 0,0 1,12 12v24.3A9.7,9.7 0,0 0,75 64h0a9.69,9.69 0,0 0,9.66 -9.67L84.8,30a12,12 0,0 1,24 0l-0.94,66.3a12,12 0,0 1,-12 12H31.2a12,12 0,0 1,-12 -12c0,-9.33 8,-7.41 8,-17.58 0,-8.31 -8,-7.87 -8,-16.57 0,-8 8,-8.12 8,-15.81 0.05,-8.12 -8,-9.81 -8,-16.34a12,12 0,0 1,12 -12Z"
|
||||
android:fillColor="#f7941d"/>
|
||||
</group>
|
||||
</vector>
|
7
app/src/main/res/drawable/rounded_card.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/cardview_dark_background"/>
|
||||
<stroke android:width="8dp" android:color="#000000FF" />
|
||||
<corners android:radius="10dp"/>
|
||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||
</shape>
|
@ -6,12 +6,20 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.maenle.bump.ui.FirstFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_scan"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/current_sender"
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:textSize="20sp"
|
||||
android:layout_gravity="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
@ -25,6 +33,7 @@
|
||||
android:breakStrategy="balanced"
|
||||
android:maxEms="16"
|
||||
android:visibility="gone"
|
||||
android:textSize="18sp"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -34,7 +43,8 @@
|
||||
android:id="@+id/idIVQrcode"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="300dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/qr_code"
|
||||
android:layout_marginTop="20dp"
|
||||
android:visibility="gone"
|
||||
@ -43,10 +53,12 @@
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/buttons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_gravity="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/idIVQrcode"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
@ -56,12 +68,50 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:textColor="@color/white"
|
||||
android:text="@string/scan_sender" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:text="@string/view_sender" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_gravity="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/buttons"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
<TextView
|
||||
android:id="@+id/nr_messages"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/default_nr_messages"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:textSize="20sp"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_header"
|
||||
android:textSize="20sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/notification_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:animateLayoutChanges="true"
|
||||
android:layout_weight="1"
|
||||
android:layout_margin="20sp"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/header">
|
||||
</ListView>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
30
app/src/main/res/layout/notification_item.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="10dp"
|
||||
android:paddingStart="20dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:autoLink="web"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="title"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/data"
|
||||
android:autoLink="web"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="no data\nfor\nme"
|
||||
android:textSize="12sp"
|
||||
android:ellipsize="marquee"/>
|
||||
|
||||
</LinearLayout>
|
@ -3,8 +3,18 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="com.maenle.bump.ui.MainActivity">
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:id="@+id/action_clear_log"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/action_clear_log"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_info"
|
||||
android:orderInCategory="101"
|
||||
android:title="@string/action_info"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:orderInCategory="102"
|
||||
android:title="@string/action_settings"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 902 B |
Before Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 7.6 KiB |
@ -2,7 +2,7 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.BumpForAndroid" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimary">@color/purple_700</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="orange_200">#f7941d</color>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
|
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
@ -1,6 +1,8 @@
|
||||
<resources>
|
||||
<string name="app_name">Bump</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
<string name="action_info">Help</string>
|
||||
<string name="action_clear_log">Clear Log</string>
|
||||
<!-- Strings used for fragments for navigation -->
|
||||
<string name="main_fragment_label">Bump</string>
|
||||
<string name="second_fragment_label">Second Fragment</string>
|
||||
@ -11,8 +13,10 @@
|
||||
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
|
||||
<string name="current_sender">No Sender Added</string>
|
||||
<string name="scan_sender">Scan</string>
|
||||
<string name="camera_fragment_label">Scan new Fragment</string>
|
||||
<string name="camera_fragment_label">Scan Sender QR</string>
|
||||
<string name="preference_file_key">code_file_key</string>
|
||||
<string name="message_header">messages received:</string>
|
||||
<string name="default_nr_messages">0</string>
|
||||
<string name="code_key">code_key</string>
|
||||
<string name="notification_title">Bump</string>
|
||||
<string name="bump_notification_channel_id">bump_id</string>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="actionMenuTextColor">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.BumpForAndroid.NoActionBar">
|
||||
|
@ -5,7 +5,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.0.4"
|
||||
classpath 'com.android.tools.build:gradle:7.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0"
|
||||
classpath 'com.google.gms:google-services:4.3.2' // Google Services plugin
|
||||
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Sat Dec 11 13:26:50 CET 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|