diff --git a/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt b/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt index dc3a5252..b8493c2b 100644 --- a/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt +++ b/app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt @@ -5,8 +5,10 @@ import android.content.Intent import android.content.IntentFilter import androidx.appcompat.app.AppCompatDelegate import androidx.lifecycle.Observer +import com.philkes.notallyx.presentation.view.misc.BetterLiveData import com.philkes.notallyx.presentation.view.misc.BiometricLock.enabled import com.philkes.notallyx.presentation.view.misc.Theme +import com.philkes.notallyx.presentation.widget.WidgetProvider import com.philkes.notallyx.utils.backup.Export.scheduleAutoBackup import com.philkes.notallyx.utils.security.UnlockReceiver @@ -16,7 +18,7 @@ class NotallyXApplication : Application() { private lateinit var preferences: Preferences private var unlockReceiver: UnlockReceiver? = null - var isLocked = true + val locked = BetterLiveData(true) override fun onCreate() { super.onCreate() @@ -47,5 +49,7 @@ class NotallyXApplication : Application() { } } preferences.biometricLock.observeForever(biometricLockObserver) + + locked.observeForever { isLocked -> WidgetProvider.updateWidgets(this, locked = isLocked) } } } diff --git a/app/src/main/java/com/philkes/notallyx/Preferences.kt b/app/src/main/java/com/philkes/notallyx/Preferences.kt index ccc8b852..9de3760f 100644 --- a/app/src/main/java/com/philkes/notallyx/Preferences.kt +++ b/app/src/main/java/com/philkes/notallyx/Preferences.kt @@ -5,6 +5,7 @@ import android.os.Build import android.preference.PreferenceManager import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey +import com.philkes.notallyx.data.model.Type import com.philkes.notallyx.data.model.toPreservedByteArray import com.philkes.notallyx.data.model.toPreservedString import com.philkes.notallyx.presentation.view.misc.AutoBackup @@ -127,17 +128,22 @@ class Preferences private constructor(app: Application) { fun getWidgetData(id: Int) = preferences.getLong("widget:$id", 0) + fun getWidgetNoteType(id: Int) = + preferences.getString("widgetNoteType:$id", null)?.let { Type.valueOf(it) } + fun deleteWidget(id: Int) { editor.remove("widget:$id") + editor.remove("widgetNoteType:$id") editor.commit() } - fun updateWidget(id: Int, noteId: Long) { + fun updateWidget(id: Int, noteId: Long, noteType: Type) { editor.putLong("widget:$id", noteId) + editor.putString("widgetNoteType:$id", noteType.name) editor.commit() } - fun getUpdatableWidgets(noteIds: LongArray): List> { + fun getUpdatableWidgets(noteIds: LongArray? = null): List> { val updatableWidgets = ArrayList>() val pairs = preferences.all pairs.keys.forEach { key -> @@ -148,7 +154,7 @@ class Preferences private constructor(app: Application) { if (id != null) { val value = pairs[key] as? Long if (value != null) { - if (noteIds.contains(value)) { + if (noteIds == null || noteIds.contains(value)) { updatableWidgets.add(Pair(id, value)) } } diff --git a/app/src/main/java/com/philkes/notallyx/presentation/activity/ConfigureWidgetActivity.kt b/app/src/main/java/com/philkes/notallyx/presentation/activity/ConfigureWidgetActivity.kt index 0b5aff41..073f5d6e 100644 --- a/app/src/main/java/com/philkes/notallyx/presentation/activity/ConfigureWidgetActivity.kt +++ b/app/src/main/java/com/philkes/notallyx/presentation/activity/ConfigureWidgetActivity.kt @@ -28,11 +28,11 @@ class ConfigureWidgetActivity : PickNoteActivity() { override fun onClick(position: Int) { if (position != -1) { val preferences = Preferences.getInstance(application) - val noteId = (adapter.getItem(position) as BaseNote).id - preferences.updateWidget(id, noteId) + val baseNote = adapter.getItem(position) as BaseNote + preferences.updateWidget(id, baseNote.id, baseNote.type) val manager = AppWidgetManager.getInstance(this) - WidgetProvider.updateWidget(this, manager, id, noteId) + WidgetProvider.updateWidget(this, manager, id, baseNote.id, baseNote.type) val success = Intent() success.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id) diff --git a/app/src/main/java/com/philkes/notallyx/presentation/activity/LockedActivity.kt b/app/src/main/java/com/philkes/notallyx/presentation/activity/LockedActivity.kt index 2247d428..b5d66a50 100644 --- a/app/src/main/java/com/philkes/notallyx/presentation/activity/LockedActivity.kt +++ b/app/src/main/java/com/philkes/notallyx/presentation/activity/LockedActivity.kt @@ -34,8 +34,7 @@ abstract class LockedActivity : AppCompatActivity() { biometricAuthenticationActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { - notallyXApplication.isLocked = false - show() + unlock() } else { finish() } @@ -67,15 +66,17 @@ abstract class LockedActivity : AppCompatActivity() { preferences.iv!!, biometricAuthenticationActivityResultLauncher, R.string.unlock, - onSuccess = { - notallyXApplication.isLocked = false - show() - }, + onSuccess = { unlock() }, ) { finish() } } + private fun unlock() { + notallyXApplication.locked.value = false + show() + } + protected fun show() { binding.root.visibility = VISIBLE } @@ -88,7 +89,7 @@ abstract class LockedActivity : AppCompatActivity() { val keyguardManager: KeyguardManager = this.getSystemService(KEYGUARD_SERVICE) as KeyguardManager return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - (keyguardManager.isDeviceLocked || notallyXApplication.isLocked) + (keyguardManager.isDeviceLocked || notallyXApplication.locked.value) } else { false } diff --git a/app/src/main/java/com/philkes/notallyx/presentation/activity/main/fragment/SettingsFragment.kt b/app/src/main/java/com/philkes/notallyx/presentation/activity/main/fragment/SettingsFragment.kt index 57cbc10b..a2f86b74 100644 --- a/app/src/main/java/com/philkes/notallyx/presentation/activity/main/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/philkes/notallyx/presentation/activity/main/fragment/SettingsFragment.kt @@ -564,7 +564,8 @@ class SettingsFragment : Fragment() { encryptDatabase(requireContext(), passphrase) model.savePreference(BiometricLock, enabled) } - (activity?.application as NotallyXApplication).isLocked = false + val app = (activity?.application as NotallyXApplication) + app.locked.value = false showBiometricsEnabledToast() }, ) { diff --git a/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetFactory.kt b/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetFactory.kt index 5d178ee9..d91eacda 100644 --- a/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetFactory.kt +++ b/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetFactory.kt @@ -1,6 +1,5 @@ package com.philkes.notallyx.presentation.widget -import android.app.Application import android.content.Intent import android.graphics.Paint import android.os.Build @@ -8,6 +7,7 @@ import android.util.TypedValue import android.view.View import android.widget.RemoteViews import android.widget.RemoteViewsService +import com.philkes.notallyx.NotallyXApplication import com.philkes.notallyx.Preferences import com.philkes.notallyx.R import com.philkes.notallyx.data.NotallyDatabase @@ -16,8 +16,11 @@ import com.philkes.notallyx.data.model.Type import com.philkes.notallyx.presentation.view.misc.TextSize import com.philkes.notallyx.presentation.widget.WidgetProvider.Companion.getSelectNoteIntent -class WidgetFactory(private val app: Application, private val id: Long, private val widgetId: Int) : - RemoteViewsService.RemoteViewsFactory { +class WidgetFactory( + private val app: NotallyXApplication, + private val id: Long, + private val widgetId: Int, +) : RemoteViewsService.RemoteViewsFactory { private var baseNote: BaseNote? = null private lateinit var database: NotallyDatabase diff --git a/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetProvider.kt b/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetProvider.kt index cd853c06..d82ea678 100644 --- a/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetProvider.kt +++ b/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetProvider.kt @@ -14,6 +14,7 @@ import com.philkes.notallyx.R import com.philkes.notallyx.data.NotallyDatabase import com.philkes.notallyx.data.dao.BaseNoteDao import com.philkes.notallyx.data.model.BaseNote +import com.philkes.notallyx.data.model.Type import com.philkes.notallyx.data.model.findChildrenPositions import com.philkes.notallyx.data.model.findParentPosition import com.philkes.notallyx.presentation.activity.ConfigureWidgetActivity @@ -107,27 +108,6 @@ class WidgetProvider : AppWidgetProvider() { } } - private fun updateWidgets(context: Context, noteIds: LongArray) { - val app = context.applicationContext as Application - val preferences = Preferences.getInstance(app) - - val manager = AppWidgetManager.getInstance(context) - val updatableWidgets = preferences.getUpdatableWidgets(noteIds) - - updatableWidgets.forEach { pair -> updateWidget(context, manager, pair.first, pair.second) } - } - - private fun openActivity(context: Context, originalIntent: Intent, clazz: Class<*>) { - val id = originalIntent.getLongExtra(Constants.SelectedBaseNote, 0) - val widgetId = originalIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0) - val intent = Intent(context, clazz) - intent.putExtra(Constants.SelectedBaseNote, id) - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId) - intent.data = originalIntent.data - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - context.startActivity(intent) - } - override fun onDeleted(context: Context, appWidgetIds: IntArray) { val app = context.applicationContext as Application val preferences = Preferences.getInstance(app) @@ -145,13 +125,40 @@ class WidgetProvider : AppWidgetProvider() { appWidgetIds.forEach { id -> val noteId = preferences.getWidgetData(id) - updateWidget(context, appWidgetManager, id, noteId) + val noteType = preferences.getWidgetNoteType(id) + updateWidget(context, appWidgetManager, id, noteId, noteType) } } companion object { - fun updateWidget(context: Context, manager: AppWidgetManager, id: Int, noteId: Long) { + fun updateWidgets(context: Context, noteIds: LongArray? = null, locked: Boolean = false) { + val app = context.applicationContext as Application + val preferences = Preferences.getInstance(app) + + val manager = AppWidgetManager.getInstance(context) + val updatableWidgets = preferences.getUpdatableWidgets(noteIds) + + updatableWidgets.forEach { (id, noteId) -> + updateWidget( + context, + manager, + id, + noteId, + preferences.getWidgetNoteType(id), + locked = locked, + ) + } + } + + fun updateWidget( + context: Context, + manager: AppWidgetManager, + id: Int, + noteId: Long, + noteType: Type?, + locked: Boolean = false, + ) { // Widgets displaying the same note share the same factory since only the noteId is // embedded val intent = Intent(context, WidgetService::class.java) @@ -159,17 +166,73 @@ class WidgetProvider : AppWidgetProvider() { intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id) embedIntentExtras(intent) - val view = RemoteViews(context.packageName, R.layout.widget) - view.setRemoteAdapter(R.id.ListView, intent) - view.setEmptyView(R.id.ListView, R.id.Empty) - - view.setOnClickFillInIntent(R.id.Empty, getSelectNoteIntent(id)) + val view = + if (!locked) { + RemoteViews(context.packageName, R.layout.widget).apply { + setRemoteAdapter(R.id.ListView, intent) + setEmptyView(R.id.ListView, R.id.Empty) + setOnClickFillInIntent(R.id.Empty, getSelectNoteIntent(id)) + setPendingIntentTemplate(R.id.ListView, getOpenNoteIntent(context, noteId)) + } + } else { + RemoteViews(context.packageName, R.layout.widget_locked).apply { + noteType?.let { + val openNoteIntent = + when (it) { + Type.NOTE -> Intent(context, EditNoteActivity::class.java) + Type.LIST -> Intent(context, EditListActivity::class.java) + }.apply { + putExtra(Constants.SelectedBaseNote, noteId) + addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_CLEAR_TASK + ) + } + val lockedPendingIntent = + PendingIntent.getActivity( + context, + 0, + openNoteIntent, + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE, + ) + setOnClickPendingIntent(R.id.Layout, lockedPendingIntent) + setOnClickPendingIntent(R.id.Text, lockedPendingIntent) + } + setTextViewCompoundDrawablesRelative( + R.id.Text, + 0, + R.drawable.lock_big, + 0, + 0, + ) + } + } + manager.updateAppWidget(id, view) + if (!locked) { + manager.notifyAppWidgetViewDataChanged(id, R.id.ListView) + } + } - val openNote = getOpenNoteIntent(context, noteId) - view.setPendingIntentTemplate(R.id.ListView, openNote) + private fun openActivity(context: Context, originalIntent: Intent, clazz: Class<*>) { + val id = originalIntent.getLongExtra(Constants.SelectedBaseNote, 0) + val widgetId = originalIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0) + context.startActivity(createIntent(context, clazz, id, widgetId, originalIntent)) + } - manager.updateAppWidget(id, view) - manager.notifyAppWidgetViewDataChanged(id, R.id.ListView) + private fun createIntent( + context: Context, + clazz: Class<*>, + noteId: Long, + widgetId: Int, + originalIntent: Intent? = null, + ): Intent { + val intent = Intent(context, clazz) + intent.putExtra(Constants.SelectedBaseNote, noteId) + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId) + originalIntent?.let { intent.data = it.data } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + return intent } fun sendBroadcast(app: Application, ids: LongArray) { diff --git a/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetService.kt b/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetService.kt index 8ea25e76..e562bbc4 100644 --- a/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetService.kt +++ b/app/src/main/java/com/philkes/notallyx/presentation/widget/WidgetService.kt @@ -3,6 +3,7 @@ package com.philkes.notallyx.presentation.widget import android.appwidget.AppWidgetManager import android.content.Intent import android.widget.RemoteViewsService +import com.philkes.notallyx.NotallyXApplication import com.philkes.notallyx.presentation.view.Constants class WidgetService : RemoteViewsService() { @@ -10,6 +11,6 @@ class WidgetService : RemoteViewsService() { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { val id = intent.getLongExtra(Constants.SelectedBaseNote, 0) val widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0) - return WidgetFactory(application, id, widgetId) + return WidgetFactory(application as NotallyXApplication, id, widgetId) } } diff --git a/app/src/main/java/com/philkes/notallyx/utils/security/PhoneLockReceiver.kt b/app/src/main/java/com/philkes/notallyx/utils/security/PhoneLockReceiver.kt index d891f78d..cf5a81a7 100644 --- a/app/src/main/java/com/philkes/notallyx/utils/security/PhoneLockReceiver.kt +++ b/app/src/main/java/com/philkes/notallyx/utils/security/PhoneLockReceiver.kt @@ -9,7 +9,7 @@ class UnlockReceiver(private val application: NotallyXApplication) : BroadcastRe override fun onReceive(context: Context?, intent: Intent) { if (intent.action == Intent.ACTION_SCREEN_OFF) { - application.isLocked = true + application.locked.value = true } } } diff --git a/app/src/main/res/drawable/lock_big.xml b/app/src/main/res/drawable/lock_big.xml new file mode 100644 index 00000000..31d4297a --- /dev/null +++ b/app/src/main/res/drawable/lock_big.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/widget_locked.xml b/app/src/main/res/layout/widget_locked.xml new file mode 100644 index 00000000..98a5cc27 --- /dev/null +++ b/app/src/main/res/layout/widget_locked.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 241948a7..3a24aaa2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -163,6 +163,7 @@ Security Lock app with device biometric or PIN + Locked Enabled Disabled