1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-08 20:12:39 +01:00

Merge pull request #1406 from AllanWang/feature/notif-widget

Feature/notif-widget
This commit is contained in:
Allan Wang 2019-04-25 13:56:40 -07:00 committed by GitHub
commit 00e04ce269
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 498 additions and 27 deletions

View File

@ -38,7 +38,8 @@ class NotificationDbTest : BaseDbTest() {
title = null,
text = "",
timestamp = time,
profileUrl = null
profileUrl = null,
unread = true
)
@Test

View File

@ -173,6 +173,20 @@
</intent-filter>
</receiver>
<!--Widgets-->
<receiver android:name=".widgets.NotificationWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/notification_widget_info" />
</receiver>
<service
android:name=".widgets.NotificationWidgetService"
android:enabled="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"

View File

@ -104,6 +104,7 @@ import com.pitchedapps.frost.utils.setFrostColors
import com.pitchedapps.frost.views.BadgedIcon
import com.pitchedapps.frost.views.FrostVideoViewer
import com.pitchedapps.frost.views.FrostViewPager
import com.pitchedapps.frost.widgets.NotificationWidget
import kotlinx.android.synthetic.main.activity_frame_wrapper.*
import kotlinx.android.synthetic.main.view_main_fab.*
import kotlinx.android.synthetic.main.view_main_toolbar.*
@ -450,7 +451,11 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
Runtime.getRuntime().exit(0)
return
}
if (resultCode and REQUEST_RESTART > 0) return restart()
if (resultCode and REQUEST_RESTART > 0) {
NotificationWidget.forceUpdate(this)
restart()
return
}
/*
* These results can be stacked
*/

View File

@ -20,6 +20,7 @@ import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.pitchedapps.frost.BuildConfig
import org.koin.dsl.module.module
import org.koin.standalone.StandAloneContext
@ -69,15 +70,22 @@ class FrostDatabase(private val privateDb: FrostPrivateDatabase, private val pub
}
companion object {
private fun <T : RoomDatabase> RoomDatabase.Builder<T>.frostBuild() = if (BuildConfig.DEBUG) {
fallbackToDestructiveMigration().build()
} else {
build()
}
fun create(context: Context): FrostDatabase {
val privateDb = Room.databaseBuilder(
context, FrostPrivateDatabase::class.java,
FrostPrivateDatabase.DATABASE_NAME
).build()
).frostBuild()
val publicDb = Room.databaseBuilder(
context, FrostPublicDatabase::class.java,
FrostPublicDatabase.DATABASE_NAME
).build()
).frostBuild()
return FrostDatabase(privateDb, publicDb)
}

View File

@ -64,7 +64,8 @@ data class NotificationEntity(
val timestamp: Long,
val profileUrl: String?,
// Type essentially refers to channel
val type: String
val type: String,
val unread: Boolean
) {
constructor(
type: String,
@ -77,7 +78,8 @@ data class NotificationEntity(
content.text,
content.timestamp,
content.profileUrl,
type
type,
content.unread
)
}
@ -94,7 +96,8 @@ data class NotificationContentEntity(
title = notif.title,
text = notif.text,
timestamp = notif.timestamp,
profileUrl = notif.profileUrl
profileUrl = notif.profileUrl,
unread = notif.unread
)
}
@ -134,8 +137,11 @@ interface NotificationDao {
suspend fun NotificationDao.deleteAll() = dao { _deleteAll() }
suspend fun NotificationDao.selectNotifications(userId: Long, type: String): List<NotificationContent> = dao {
fun NotificationDao.selectNotificationsSync(userId: Long, type: String): List<NotificationContent> =
_selectNotifications(userId, type).map { it.toNotifContent() }
suspend fun NotificationDao.selectNotifications(userId: Long, type: String): List<NotificationContent> = dao {
selectNotificationsSync(userId, type)
}
/**

View File

@ -64,7 +64,8 @@ data class FrostMessages(
title = title,
text = content ?: "",
timestamp = time,
profileUrl = img
profileUrl = img,
unread = unread
)
}
}.toList()

View File

@ -53,7 +53,8 @@ data class FrostNotifs(
title = null,
text = content,
timestamp = time,
profileUrl = img
profileUrl = img,
unread = unread
)
}
}.toList()

View File

@ -61,7 +61,7 @@ private val _40_DP = 40.dpToPx
* Enum to handle notification creations
*/
enum class NotificationType(
private val channelId: String,
val channelId: String,
private val overlayContext: OverlayContext,
private val fbItem: FbItem,
private val parser: FrostParser<ParseNotification>,
@ -95,8 +95,8 @@ enum class NotificationType(
*/
internal open fun bindRequest(content: NotificationContent, cookie: String): (BaseBundle.() -> Unit)? = null
private fun bindRequest(intent: Intent, content: NotificationContent, cookie: String?) {
cookie ?: return
private fun bindRequest(intent: Intent, content: NotificationContent) {
val cookie = content.data.cookie ?: return
val binder = bindRequest(content, cookie) ?: return
val bundle = Bundle()
bundle.binder()
@ -181,23 +181,40 @@ enum class NotificationType(
"Debug Notif",
"Test 123",
System.currentTimeMillis() / 1000,
"https://www.iconexperience.com/_img/v_collection_png/256x256/shadow/dog.png"
"https://www.iconexperience.com/_img/v_collection_png/256x256/shadow/dog.png",
false
)
createNotification(context, content).notify(context)
}
/**
* Attach content related data to an intent
*/
fun putContentExtra(intent: Intent, content: NotificationContent): Intent {
// We will show the notification page for dependent urls. We can trigger a click next time
intent.data = Uri.parse(if (content.href.isIndependent) content.href else FbItem.NOTIFICATIONS.url)
bindRequest(intent, content)
return intent
}
/**
* Create a generic content for the provided type and user id.
* No content related data is added
*/
fun createCommonIntent(context: Context, userId: Long): Intent {
val intent = Intent(context, FrostWebActivity::class.java)
intent.putExtra(ARG_USER_ID, userId)
overlayContext.put(intent)
return intent
}
/**
* Create and submit a new notification with the given [content]
*/
private fun createNotification(context: Context, content: NotificationContent): FrostNotification =
with(content) {
val intent = Intent(context, FrostWebActivity::class.java)
// TODO temp fix; we will show notification page for dependent urls. We can trigger a click next time
intent.data = Uri.parse(if (href.isIndependent) href else FbItem.NOTIFICATIONS.url)
intent.putExtra(ARG_USER_ID, data.id)
overlayContext.put(intent)
bindRequest(intent, content, data.cookie)
val intent = createCommonIntent(context, content.data.id)
putContentExtra(intent, content)
val group = "${groupPrefix}_${data.id}"
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val notifBuilder = context.frostNotification(channelId)
@ -266,7 +283,8 @@ data class NotificationContent(
val title: String? = null, // defaults to frost title
val text: String,
val timestamp: Long,
val profileUrl: String?
val profileUrl: String?,
val unread: Boolean
) {
val notifId = Math.abs(id.toInt())

View File

@ -27,6 +27,7 @@ import com.pitchedapps.frost.db.selectAll
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.frostEvent
import com.pitchedapps.frost.widgets.NotificationWidget
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
@ -105,6 +106,9 @@ class NotificationService : BaseJobService() {
L.i { "Sent $notifCount notifications" }
if (notifCount == 0 && jobId == NOTIFICATION_JOB_NOW)
generalNotification(665, R.string.no_new_notifications, BuildConfig.DEBUG)
if (notifCount > 0) {
NotificationWidget.forceUpdate(this@NotificationService)
}
}
/**

View File

@ -0,0 +1,50 @@
/*
* Copyright 2019 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.utils
import android.content.Context
import ca.allanwang.kau.utils.string
import com.pitchedapps.frost.R
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
/**
* Converts time in millis to readable date,
* eg Apr 24 at 7:32 PM
*
* With regards to date modifications in calendars,
* it appears to respect calendar rules;
* see https://stackoverflow.com/a/43227817/4407321
*/
fun Long.toReadableTime(context: Context): String {
val cal = Calendar.getInstance()
cal.timeInMillis = this
val timeFormatter = SimpleDateFormat.getTimeInstance(DateFormat.SHORT)
val time = timeFormatter.format(Date(this))
val day = when {
cal >= Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, -1) } -> context.string(R.string.today)
cal >= Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, -2) } -> context.string(R.string.yesterday)
else -> {
val dayFormatter = SimpleDateFormat("MMM dd", Locale.getDefault())
dayFormatter.format(Date(this))
}
}
return context.getString(R.string.time_template, day, time)
}

View File

@ -186,7 +186,7 @@ fun MaterialDialog.Builder.theme(): MaterialDialog.Builder {
}
fun Activity.setFrostTheme(forceTransparent: Boolean = false) {
val isTransparent = (Color.alpha(Prefs.bgColor) != 255) || forceTransparent
val isTransparent = (Color.alpha(Prefs.bgColor) != 255) || (Color.alpha(Prefs.headerColor) != 255) || forceTransparent
if (Prefs.bgColor.isColorDark)
setTheme(if (isTransparent) R.style.FrostTheme_Transparent else R.style.FrostTheme)
else

View File

@ -0,0 +1,194 @@
/*
* Copyright 2019 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.widgets
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Icon
import android.os.Build
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import ca.allanwang.kau.utils.dimenPixelSize
import ca.allanwang.kau.utils.withAlpha
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.db.NotificationDao
import com.pitchedapps.frost.db.selectNotificationsSync
import com.pitchedapps.frost.glide.FrostGlide
import com.pitchedapps.frost.glide.GlideApp
import com.pitchedapps.frost.services.NotificationContent
import com.pitchedapps.frost.services.NotificationType
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.toReadableTime
import org.koin.standalone.KoinComponent
import org.koin.standalone.inject
class NotificationWidget : AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
val type = NotificationType.GENERAL
val userId = Prefs.userId
val intent = NotificationWidgetService.createIntent(context, type, userId)
for (id in appWidgetIds) {
val views = RemoteViews(context.packageName, R.layout.widget_notifications)
views.setBackgroundColor(R.id.widget_layout_toolbar, Prefs.headerColor)
views.setIcon(R.id.img_frost, context, R.drawable.frost_f_24, Prefs.iconColor)
views.setOnClickPendingIntent(
R.id.img_frost,
PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0)
)
views.setBackgroundColor(R.id.widget_notification_list, Prefs.bgColor)
views.setRemoteAdapter(R.id.widget_notification_list, intent)
val pendingIntentTemplate = PendingIntent.getActivity(
context,
0,
type.createCommonIntent(context, userId),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setPendingIntentTemplate(R.id.widget_notification_list, pendingIntentTemplate)
appWidgetManager.updateAppWidget(id, views)
}
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_notification_list)
}
companion object {
fun forceUpdate(context: Context) {
val manager = AppWidgetManager.getInstance(context)
val ids = manager.getAppWidgetIds(ComponentName(context, NotificationWidget::class.java))
val intent = Intent().apply {
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
}
context.sendBroadcast(intent)
}
}
}
private const val NOTIF_WIDGET_TYPE = "notif_widget_type"
private const val NOTIF_WIDGET_USER_ID = "notif_widget_user_id"
private fun RemoteViews.setBackgroundColor(@IdRes viewId: Int, @ColorInt color: Int) {
setInt(viewId, "setBackgroundColor", color)
}
/**
* Adds backward compatibility to setting tinted icons
*/
private fun RemoteViews.setIcon(@IdRes viewId: Int, context: Context, @DrawableRes res: Int, @ColorInt color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val icon = Icon.createWithResource(context, res).setTint(color).setTintMode(PorterDuff.Mode.SRC_IN)
setImageViewIcon(viewId, icon)
} else {
val bitmap = BitmapFactory.decodeResource(context.resources, res)
if (bitmap != null) {
val paint = Paint()
paint.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
val result = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(result)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
setImageViewBitmap(viewId, result)
} else {
// Fallback to just icon
setImageViewResource(viewId, res)
}
}
}
class NotificationWidgetService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = NotificationWidgetDataProvider(this, intent)
companion object {
fun createIntent(context: Context, type: NotificationType, userId: Long): Intent =
Intent(context, NotificationWidgetService::class.java)
.putExtra(NOTIF_WIDGET_TYPE, type.name)
.putExtra(NOTIF_WIDGET_USER_ID, userId)
}
}
class NotificationWidgetDataProvider(val context: Context, val intent: Intent) : RemoteViewsService.RemoteViewsFactory,
KoinComponent {
private val notifDao: NotificationDao by inject()
@Volatile
private var content: List<NotificationContent> = emptyList()
private val type = NotificationType.valueOf(intent.getStringExtra(NOTIF_WIDGET_TYPE))
private val userId = intent.getLongExtra(NOTIF_WIDGET_USER_ID, -1)
private val avatarSize = context.dimenPixelSize(R.dimen.avatar_image_size)
private val glide = GlideApp.with(context).asBitmap()
private fun loadNotifications() {
content = notifDao.selectNotificationsSync(userId, type.channelId)
}
override fun onCreate() {
}
override fun onDataSetChanged() {
loadNotifications()
}
override fun getLoadingView(): RemoteViews? = null
override fun getItemId(position: Int): Long = content[position].id
override fun hasStableIds(): Boolean = true
override fun getViewAt(position: Int): RemoteViews {
val views = RemoteViews(context.packageName, R.layout.widget_notification_item)
val notif = content[position]
views.setBackgroundColor(R.id.item_frame, Prefs.nativeBgColor(notif.unread))
views.setTextColor(R.id.item_content, Prefs.textColor)
views.setTextViewText(R.id.item_content, notif.text)
views.setTextColor(R.id.item_date, Prefs.textColor.withAlpha(150))
views.setTextViewText(R.id.item_date, notif.timestamp.toReadableTime(context))
val avatar = glide.load(notif.profileUrl).transform(FrostGlide.circleCrop).submit(avatarSize, avatarSize).get()
views.setImageViewBitmap(R.id.item_avatar, avatar)
views.setOnClickFillInIntent(R.id.item_frame, type.putContentExtra(Intent(), notif))
return views
}
override fun getCount(): Int = content.size
override fun getViewTypeCount(): Int = 1
override fun onDestroy() {
}
}

View File

@ -0,0 +1,48 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="40dp"
android:viewportWidth="300"
android:viewportHeight="400">
<path
android:pathData="M0,0h300v400H0V0z"
android:fillColor="#fafafa"/>
<path
android:pathData="M0,0h300v50H0V0z"
android:fillColor="@color/facebook_blue"/>
<path
android:pathData="M65,170a20,20 0,1 1,-40 0,20 20,0 0,1 40,0z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,150h184v11H85v-11z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,179h146v11H85v-11z"
android:fillColor="#DE000000"/>
<path
android:pathData="M65,95a20,20 0,1 1,-40 0,20 20,0 0,1 40,0z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,75h184v11H85V75z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,104h146v11H85v-11z"
android:fillColor="#DE000000"/>
<path
android:pathData="M65,245a20,20 0,1 1,-40 0,20 20,0 0,1 40,0z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,225h184v11H85v-11z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,254h146v11H85v-11z"
android:fillColor="#DE000000"/>
<path
android:pathData="M65,320a20,20 0,1 1,-40 0,20 20,0 0,1 40,0z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,300h184v11H85v-11z"
android:fillColor="#DE000000"/>
<path
android:pathData="M85,329h146v11H85v-11z"
android:fillColor="#DE000000"/>
</vector>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:paddingStart="@dimen/kau_activity_horizontal_margin"
android:paddingTop="@dimen/kau_activity_vertical_margin"
android:paddingEnd="@dimen/kau_activity_horizontal_margin"
android:paddingBottom="@dimen/kau_activity_vertical_margin">
<ImageView
android:id="@+id/item_avatar"
android:layout_width="@dimen/avatar_image_size"
android:layout_height="@dimen/avatar_image_size" />
<!--
Unlike the actual notification panel,
we do not show thumbnails, and we limit the title length
-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/kau_padding_normal"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="2" />
<TextView
android:id="@+id/item_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_layout_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/widget_layout_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="@dimen/kau_padding_small"
android:paddingEnd="@dimen/kau_padding_small">
<ImageView
android:id="@+id/img_frost"
android:layout_width="@dimen/toolbar_icon_size"
android:layout_height="@dimen/toolbar_icon_size"
android:layout_gravity="center_vertical"
android:layout_margin="@dimen/kau_padding_small"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
<ListView
android:id="@+id/widget_notification_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

View File

@ -9,4 +9,6 @@
<dimen name="tab_bar_height">50dp</dimen>
<dimen name="intro_bar_height">64dp</dimen>
<dimen name="badge_icon_size">20dp</dimen>
<dimen name="toolbar_icon_size">24dp</dimen>
</resources>

View File

@ -62,4 +62,16 @@
<string name="no_new_notifications">No new notifications found</string>
<string name="today">Today</string>
<string name="yesterday">Today</string>
<!--
Template used to display human readable string;
For instance:
Today at 1:23 PM
Mar 13 at 9:00 AM
The first element is the day, and the second element is the time
-->
<string name="time_template">%1s at %2s</string>
</resources>

View File

@ -6,12 +6,22 @@
<item text="" />
-->
<version title="v2.3.0" />
<item text="Converted internals of Facebook data storage; auto migration will only work from 2.2.x to 2.3.x" />
<item text="Added notification widget" />
<item text="" />
<item text="" />
<item text="" />
<item text="" />
<item text="" />
<item text="" />
<item text="" />
<version title="v2.2.4" />
<item text="Show top bar to allow sharing posts" />
<item text="Fix unmuting videos when autoplay is enabled" />
<item text="Add shortcut to toggle autoplay in settings > behaviour" />
<item text="Update theme" />
<item text="" />
<version title="v2.2.3" />
<item text="Add ability to hide stories" />

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
For sizing see:
https://developer.android.com/guide/practices/ui_guidelines/widget_design.html#anatomy_determining_size
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/widget_notifications"
android:initialLayout="@layout/widget_notifications"
android:minWidth="180dp"
android:minHeight="250dp"
android:previewImage="@drawable/notification_widget_preview" />

View File

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "0a9d994786b7e07fea95c11d9210ce0e",
"identityHash": "fe8f5b6c27f48d7e0733ee6819f06f40",
"entities": [
{
"tableName": "cookies",
@ -38,7 +38,7 @@
},
{
"tableName": "notifications",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notif_id` INTEGER NOT NULL, `userId` INTEGER NOT NULL, `href` TEXT NOT NULL, `title` TEXT, `text` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `profileUrl` TEXT, `type` TEXT NOT NULL, PRIMARY KEY(`notif_id`, `userId`), FOREIGN KEY(`userId`) REFERENCES `cookies`(`cookie_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notif_id` INTEGER NOT NULL, `userId` INTEGER NOT NULL, `href` TEXT NOT NULL, `title` TEXT, `text` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `profileUrl` TEXT, `type` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`notif_id`, `userId`), FOREIGN KEY(`userId`) REFERENCES `cookies`(`cookie_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
@ -87,6 +87,12 @@
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "unread",
"columnName": "unread",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
@ -183,7 +189,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"0a9d994786b7e07fea95c11d9210ce0e\")"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"fe8f5b6c27f48d7e0733ee6819f06f40\")"
]
}
}

View File

@ -1,5 +1,9 @@
# Changelog
## v2.3.0
* Converted internals of Facebook data storage; auto migration will only work from 2.2.x to 2.3.x
* Added notification widget
## v2.2.4
* Show top bar to allow sharing posts
* Fix unmuting videos when autoplay is enabled