Skip to content

Commit

Permalink
v1.2.0
Browse files Browse the repository at this point in the history
- app updater
- dynamic color
- unlock on lockscreen
- other minor changes
  • Loading branch information
Yanndroid committed Mar 12, 2022
1 parent 062d76e commit 58cda08
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 29 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# <img align="left" loading="lazy" src="readme-res/icon.png" height="50"/> NotiNotes
The Notes app for your notification panel. A simple Notes app for Android which only lives in your QS and notification panel. You can download and install the latest apk [here](https://github.com/Yanndroid/NotiNotes/raw/master/app/release/app-release.apk).
The Notes app for your notification panel. A simple Notes app for Android which only lives in your QS and notification panel. You can download and install the latest apk [here](https://github.com/Yanndroid/NotiNotes/raw/master/app/release/app-release.apk), future updates are available directly via the app.

<img loading="lazy" src="readme-res/notifications.png" width="200"/> <img loading="lazy" src="readme-res/edit_dialog.png" width="200"/> <img loading="lazy" src="readme-res/delete_dialog.png" width="200"/>

Expand Down
6 changes: 4 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.gms.google-services'
}

android {
Expand All @@ -10,8 +11,8 @@ android {
applicationId "de.dlyt.yanndroid.notinotes"
minSdk 26
targetSdk 31
versionCode 2
versionName "1.0.1"
versionCode 3
versionName "1.2.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand All @@ -36,6 +37,7 @@ dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.google.firebase:firebase-database-ktx:20.0.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Expand Down
Binary file modified app/release/app-release.apk
Binary file not shown.
4 changes: 2 additions & 2 deletions app/release/output-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "1.0.1",
"versionCode": 3,
"versionName": "1.2.0",
"outputFile": "app-release.apk"
}
],
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

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

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
Expand All @@ -22,6 +25,16 @@
</intent-filter>
</service>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>

</application>

</manifest>
176 changes: 154 additions & 22 deletions app/src/main/java/de/dlyt/yanndroid/notinotes/QSTile.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package de.dlyt.yanndroid.notinotes

import android.annotation.SuppressLint
import android.app.DownloadManager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.PixelFormat
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import android.service.quicksettings.TileService
import android.util.Log
Expand All @@ -21,20 +24,31 @@ import android.view.WindowManager
import android.widget.*
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.FileProvider
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.File
import java.io.Serializable
import java.util.stream.Collectors

class QSTile : TileService() {

private val INTENT_EXTRA = "intent_note"
private val NOTIFICATION_GROUP = "de.dlyt.yanndroid.notinotes.NOTES_GROUP"
private val NOTIFICATION_GROUP_HOLDER = -1
private val NOTIFICATION_CHANNEL = "1234"

private val ACTION_EDIT_NOTE = "de.dlyt.yanndroid.notinotes.EDIT_NOTE"
private val ACTION_DELETE_NOTE = "de.dlyt.yanndroid.notinotes.DELETE_NOTE"
private val ACTION_SHOW_NOTE = "de.dlyt.yanndroid.notinotes.SHOW_NOTE"
private val EXTRA_NOTE = "intent_note"

private val ACTION_APP_UPDATE = "de.dlyt.yanndroid.notinotes.UPDATE"
private val EXTRA_UPDATE_URL = "UPDATE_URL"
private val EXTRA_UPDATE_VNM = "UPDATE_VNM"

var COLORS = intArrayOf(
R.color.color_1,
Expand All @@ -61,7 +75,15 @@ class QSTile : TileService() {
private var notes: ArrayList<Note> = ArrayList()
private val mBroadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val note: Note = (intent.getSerializableExtra(INTENT_EXTRA) ?: return) as Note
if (intent.action == ACTION_APP_UPDATE) {
installUpdate(
intent.getStringExtra(EXTRA_UPDATE_URL),
intent.getStringExtra(EXTRA_UPDATE_VNM)
)
return
}

val note: Note = (intent.getSerializableExtra(EXTRA_NOTE) ?: return) as Note
when (intent.action) {
ACTION_EDIT_NOTE -> editNotePopup(note)
ACTION_DELETE_NOTE -> deleteNoteDialog(note)
Expand Down Expand Up @@ -98,7 +120,10 @@ class QSTile : TileService() {
it.addAction(ACTION_EDIT_NOTE)
it.addAction(ACTION_DELETE_NOTE)
it.addAction(ACTION_SHOW_NOTE)
it.addAction(ACTION_APP_UPDATE)
})

checkForUpdate()
}

override fun onTileRemoved() {
Expand Down Expand Up @@ -152,6 +177,9 @@ class QSTile : TileService() {
}
}


/** #### Notifications #### **/

private fun showNotification(note: Note) {
val nmc = NotificationManagerCompat.from(this)
nmc.notify( //for some reason this in needed to make grouping work
Expand Down Expand Up @@ -192,25 +220,28 @@ class QSTile : TileService() {
if (notes.size == 0) nmc.cancel(NOTIFICATION_GROUP_HOLDER)
}


/** #### Dialogs #### **/

private fun editNotePopup(note: Note) {
if (!Settings.canDrawOverlays(context)) return
val deleteDialog = Dialog(R.layout.popup_edit_layout, note, note.title)
val pColorPicker = deleteDialog.pView.findViewById<RadioGroup>(R.id.color_picker).also {
val editDialog = Dialog(R.layout.popup_edit_layout, note, note.title)
val pColorPicker = editDialog.pView.findViewById<RadioGroup>(R.id.color_picker).also {
it.setOnCheckedChangeListener { _, i ->
deleteDialog.pPos.setTextColor(getColor(COLORS[RBIDS.indexOf(i)]))
deleteDialog.pNeg.setTextColor(getColor(COLORS[RBIDS.indexOf(i)]))
deleteDialog.pIcon.setColorFilter(getColor(COLORS[RBIDS.indexOf(i)]))
editDialog.pPos.setTextColor(getColor(COLORS[RBIDS.indexOf(i)]))
editDialog.pNeg.setTextColor(getColor(COLORS[RBIDS.indexOf(i)]))
editDialog.pIcon.setColorFilter(getColor(COLORS[RBIDS.indexOf(i)]))
}
it.check(RBIDS[note.colorIndex])
}

deleteDialog.pNeg.setOnClickListener { deleteDialog.dismiss() }
deleteDialog.pPos.setOnClickListener {
note.title = deleteDialog.pTitle.text.toString()
note.content = deleteDialog.pNote.text.toString()
editDialog.pNeg.setOnClickListener { editDialog.dismiss() }
editDialog.pPos.setOnClickListener {
note.title = editDialog.pTitle.text.toString()
note.content = editDialog.pNote.text.toString()
note.colorIndex = RBIDS.indexOf(pColorPicker.checkedRadioButtonId)
saveNote(note)
deleteDialog.dismiss()
editDialog.dismiss()
}
}

Expand All @@ -228,14 +259,14 @@ class QSTile : TileService() {

private fun showNoteDialog(note: Note) {
if (!Settings.canDrawOverlays(context)) return
val deleteDialog = Dialog(R.layout.popup_show_layout, note, note.title)
deleteDialog.pNeg.setOnClickListener {
val showNoteDialog = Dialog(R.layout.popup_show_layout, note, note.title)
showNoteDialog.pNeg.setOnClickListener {
editNotePopup(note)
deleteDialog.dismiss()
showNoteDialog.dismiss()
}
deleteDialog.pPos.setOnClickListener {
showNoteDialog.pPos.setOnClickListener {
deleteNoteDialog(note)
deleteDialog.dismiss()
showNoteDialog.dismiss()
}
}

Expand All @@ -249,7 +280,7 @@ class QSTile : TileService() {
val pIcon: ImageView

init {
closePanel()
closePanelAndUnlock()
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val params = WindowManager.LayoutParams(
(resources.displayMetrics.widthPixels * 0.90).toInt(),
Expand All @@ -265,6 +296,7 @@ class QSTile : TileService() {

pView = LayoutInflater.from(context).inflate(layoutRes, null)
windowManager.addView(pView, params)

pView.setOnClickListener { dismiss() }

pTitle = pView.findViewById<TextView>(R.id.pTitle).also { it.text = title }
Expand All @@ -280,9 +312,12 @@ class QSTile : TileService() {
fun dismiss() = windowManager.removeView(pView)
}


/** #### Utils #### **/

private fun requestPermission() {
if (!Settings.canDrawOverlays(this)) {
closePanel()
closePanelAndUnlock()
Toast.makeText(context, R.string.permission_toast, Toast.LENGTH_SHORT).show()
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Expand Down Expand Up @@ -312,7 +347,7 @@ class QSTile : TileService() {

private fun getPendingIntent(note: Note, action: String): PendingIntent {
val intent = Intent(action)
intent.putExtra(INTENT_EXTRA, note)
intent.putExtra(EXTRA_NOTE, note)
return PendingIntent.getBroadcast(
context,
note.id,
Expand All @@ -322,7 +357,11 @@ class QSTile : TileService() {
}

@SuppressLint("WrongConstant")
private fun closePanel() { //won't work for a12+
private fun closePanelAndUnlock() {
//unlock phone if locked
if (isLocked) unlockAndRun(null)

//close panel, won't work for A12+
try {
Class.forName("android.app.StatusBarManager").getMethod("collapsePanels")
.invoke(context.getSystemService("statusbar"))
Expand All @@ -336,7 +375,100 @@ class QSTile : TileService() {
}
}

// #### detail view (samsung only) ####

/** #### App update #### **/

private fun checkForUpdate() {
val mDatabase =
FirebaseDatabase.getInstance().reference.child(context.getString(R.string.firebase_childName))
mDatabase.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
try {
val hashMap = HashMap<String?, String?>()
for (child in snapshot.children) hashMap[child.key] = child.value.toString()

if (hashMap[context.getString(R.string.firebase_versionCode)]?.toInt() ?: 0 > context.packageManager.getPackageInfo(
context.packageName,
0
).versionCode
) showUpdateNoti(
hashMap[context.getString(R.string.firebase_apk)]!!,
hashMap[context.getString(R.string.firebase_versionName)]!!
)
} catch (e: PackageManager.NameNotFoundException) {
Log.e("checkForUpdate", e.message)
}
}

override fun onCancelled(error: DatabaseError) {
Log.e("checkForUpdate", error.message)
}
})
}

@SuppressLint("LaunchActivityFromNotification")
private fun showUpdateNoti(url: String, versionName: String) {
val pendingIntent = PendingIntent.getBroadcast(
context,
-2,
Intent(ACTION_APP_UPDATE).also {
it.putExtra(EXTRA_UPDATE_URL, url).putExtra(EXTRA_UPDATE_VNM, versionName)
},
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

NotificationManagerCompat.from(this)
.notify(
-2, NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
.setSmallIcon(R.drawable.ic_note)
.setContentTitle(getString(R.string.update_available))
.setContentText(getString(R.string.update_available_desc, versionName))
.addAction(
R.drawable.ic_download,
getString(R.string.download),
pendingIntent
)
.setContentIntent(pendingIntent)
.build()
)
}

private fun installUpdate(url: String, versionName: String) {
val destination = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
.toString() + "/" + context.getString(R.string.app_name) + "_" + versionName + ".apk"

val file = File(destination)
if (file.exists()) file.delete()

val request = DownloadManager.Request(Uri.parse(url))
request.setMimeType("application/vnd.android.package-archive")
request.setTitle(context.getString(R.string.app_name).toString() + " Update")
request.setDescription(versionName)
request.setDestinationUri(Uri.parse("file://$destination"))

val onComplete: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(ctxt: Context, intent: Intent) {
val apkFileUri = FileProvider.getUriForFile(
context,
context.applicationContext.packageName + ".provider",
File(destination)
)
val install = Intent(Intent.ACTION_VIEW)
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
install.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
install.setDataAndType(apkFileUri, "application/vnd.android.package-archive")
context.startActivity(install)
context.unregisterReceiver(this)
closePanelAndUnlock()
}
}
context.registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
(context.getSystemService(DOWNLOAD_SERVICE) as DownloadManager).enqueue(request)
}


/** #### Detail view (samsung only) #### **/

fun semGetSettingsIntent(): Intent =
Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/Yanndroid/NotiNotes"))
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/res/drawable-v24/ic_download.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dip"
android:height="24.0dip"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:fillType="nonZero"
android:pathData="M18.1535,19.7385C18.5675,19.7385 18.9035,20.0745 18.9035,20.4885C18.9035,20.9025 18.5675,21.2385 18.1535,21.2385L18.1535,21.2385L5.8465,21.2385C5.4325,21.2385 5.0965,20.9025 5.0965,20.4885C5.0965,20.0745 5.4325,19.7385 5.8465,19.7385L5.8465,19.7385ZM12.0091,2.7615C12.4231,2.7615 12.7591,3.0975 12.7591,3.5115L12.7591,3.5115L12.7591,15.5725L16.6171,11.5485C16.9031,11.2485 17.3781,11.2385 17.6781,11.5255C17.9771,11.8115 17.9861,12.2865 17.7001,12.5865L17.7001,12.5865L13.1611,17.3215C12.8601,17.6355 12.4361,17.8165 12.0001,17.8165C11.5641,17.8165 11.1411,17.6355 10.8391,17.3215L10.8391,17.3215L6.3001,12.5865C6.0141,12.2865 6.0241,11.8115 6.3221,11.5255C6.6221,11.2385 7.0961,11.2485 7.3831,11.5485L7.3831,11.5485L11.2591,15.5915L11.2591,3.5115C11.2591,3.0975 11.5951,2.7615 12.0091,2.7615Z" />
</vector>
1 change: 0 additions & 1 deletion app/src/main/res/layout/popup_edit_layout.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@
android:id="@+id/color_8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:button="@drawable/color_radio_button"
android:buttonTint="@color/color_8" />

Expand Down
Loading

0 comments on commit 58cda08

Please sign in to comment.