aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-09-18 10:43:55 -0400
committerGitHub <noreply@github.com>2017-09-18 10:43:55 -0400
commitd75ad7fb42aba81b334f9453c012f04c3d5f3e0a (patch)
tree9dfd08dd48325ae7f813a4cfa43ec5629662db79
parent5ae7fd03522dd2c2843ff522314ab1d20f85d391 (diff)
downloadfrost-d75ad7fb42aba81b334f9453c012f04c3d5f3e0a.tar.gz
frost-d75ad7fb42aba81b334f9453c012f04c3d5f3e0a.tar.bz2
frost-d75ad7fb42aba81b334f9453c012f04c3d5f3e0a.zip
Fix/notification defaults (#308)
* Update downloader * Disable deaults on creation * Use notifCount rather than index * Remove quiet * Add checks to ensure job service exists * Update changelog
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt151
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt50
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt172
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt2
-rw-r--r--app/src/main/res/xml/frost_changelog.xml10
-rw-r--r--docs/Changelog.md4
8 files changed, 141 insertions, 254 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
index 3d016909..cd7d9002 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
@@ -30,7 +30,7 @@ class FbUrlFormatter(url: String) {
cleanedUrl = cleanedUrl.substring(0, qm)
}
discardableQueries.forEach { queries.remove(it) }
- if (cleanedUrl.startsWith("#!/")) cleanedUrl = cleanedUrl.substring(2)
+ if (cleanedUrl.startsWith("#!")) cleanedUrl = cleanedUrl.substring(2)
if (cleanedUrl.startsWith("/")) cleanedUrl = FB_URL_BASE + cleanedUrl.substring(1)
cleanedUrl = cleanedUrl.replaceFirst(".facebook.com//", ".facebook.com/") //sometimes we are given a bad url
L.v(null, "Formatted url from $url to $cleanedUrl")
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt
index ee0c2027..986467b8 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt
@@ -28,6 +28,8 @@ import java.io.File
*
* Background file downloader
* All we are given is a link and a mime type
+ *
+ * With reference to the <a href="https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java">OkHttp3 sample</a>
*/
class DownloadService : IntentService("FrostVideoDownloader") {
@@ -64,7 +66,7 @@ class DownloadService : IntentService("FrostVideoDownloader") {
.url(url)
.build()
- notifBuilder = frostNotification.quiet
+ notifBuilder = frostNotification
notifId = Math.abs(url.hashCode() + System.currentTimeMillis().toInt())
val cancelIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
index 800fe492..b892af91 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
@@ -22,6 +22,7 @@ import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.FrostWebActivity
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.enums.OverlayContext
+import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.formattedFbUrl
import com.pitchedapps.frost.utils.*
import org.jetbrains.anko.runOnUiThread
@@ -34,16 +35,13 @@ import org.jetbrains.anko.runOnUiThread
val Context.frostNotification: NotificationCompat.Builder
- get() = frostNotification()
+ get() = NotificationCompat.Builder(this, BuildConfig.APPLICATION_ID).apply {
+ setSmallIcon(R.drawable.frost_f_24)
+ setAutoCancel(true)
+ color = color(R.color.frost_notification_accent)
+ }
-/**
- * Wrap the default builder with our icon and accent color
- */
-fun Context.frostNotification(ringtone: String = Prefs.notificationRingtone): NotificationCompat.Builder
- = NotificationCompat.Builder(this, BuildConfig.APPLICATION_ID).apply {
- setSmallIcon(R.drawable.frost_f_24)
- setAutoCancel(true)
- color = color(R.color.frost_notification_accent)
+fun NotificationCompat.Builder.withDefaults(ringtone: String = Prefs.notificationRingtone) = apply {
var defaults = 0
if (Prefs.notificationVibrate) defaults = defaults or Notification.DEFAULT_VIBRATE
if (Prefs.notificationSound) {
@@ -54,12 +52,6 @@ fun Context.frostNotification(ringtone: String = Prefs.notificationRingtone): No
setDefaults(defaults)
}
-val NotificationCompat.Builder.quiet
- get() = apply { setDefaults(0) }
-
-val NotificationCompat.Builder.messageRingtone
- get() = apply { }
-
val NotificationCompat.Builder.withBigText: NotificationCompat.BigTextStyle
get() = NotificationCompat.BigTextStyle(this)
@@ -81,8 +73,82 @@ class FrostNotificationTarget(val context: Context,
}
}
-internal const val FROST_NOTIFICATION_GROUP = "frost"
-internal const val FROST_MESSAGE_NOTIFICATION_GROUP = "frost_im"
+/**
+ * Enum to handle notification creations
+ */
+enum class NotificationType(
+ private val groupPrefix: String,
+ private val overlayContext: OverlayContext,
+ private val contentRes: Int,
+ private val pendingUrl: String,
+ private val ringtone: () -> String) {
+ GENERAL("frost", OverlayContext.NOTIFICATION, R.string.notifications, FbItem.NOTIFICATIONS.url, { Prefs.notificationRingtone }),
+ MESSAGE("frost_im", OverlayContext.MESSAGE, R.string.messages, FbItem.MESSAGES.url, { Prefs.messageRingtone });
+
+ /**
+ * Create and submit a new notification with the given [content]
+ * If [withDefaults] is set, it will also add the appropriate sound, vibration, and light
+ * Note that when we have multiple notifications coming in at once, we don't want to have defaults for all of them
+ */
+ fun createNotification(context: Context, content: NotificationContent, withDefaults: Boolean) {
+ with(content) {
+ val intent = Intent(context, FrostWebActivity::class.java)
+ intent.data = Uri.parse(href.formattedFbUrl)
+ intent.putExtra(ARG_USER_ID, data.id)
+ intent.putExtra(ARG_OVERLAY_CONTEXT, overlayContext)
+ val group = "${groupPrefix}_${data.id}"
+ val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
+ val notifBuilder = context.frostNotification
+ .setContentTitle(title ?: context.string(R.string.frost_name))
+ .setContentText(text)
+ .setContentIntent(pendingIntent)
+ .setCategory(Notification.CATEGORY_SOCIAL)
+ .setSubText(data.name)
+ .setGroup(group)
+
+ if (withDefaults)
+ notifBuilder.withDefaults(ringtone())
+
+ if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000)
+ L.v("Notif load", context.toString())
+ NotificationManagerCompat.from(context).notify(group, notifId, notifBuilder.withBigText.build())
+
+ if (profileUrl.isNotBlank()) {
+ context.runOnUiThread {
+ //todo verify if context is valid?
+ Glide.with(context)
+ .asBitmap()
+ .load(profileUrl)
+ .withRoundIcon()
+ .into(FrostNotificationTarget(context, notifId, group, notifBuilder))
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a summary notification to wrap the previous ones
+ * This will always produce sound, vibration, and lights based on preferences
+ * and will only show if we have at least 2 notifications
+ */
+ fun summaryNotification(context: Context, userId: Long, count: Int) {
+ frostAnswersCustom("Notifications", "Type" to name, "Count" to count)
+ if (count <= 1) return
+ val intent = Intent(context, FrostWebActivity::class.java)
+ intent.data = Uri.parse(pendingUrl)
+ intent.putExtra(ARG_USER_ID, userId)
+ val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
+ val notifBuilder = context.frostNotification.withDefaults(ringtone())
+ .setContentTitle(context.string(R.string.frost_name))
+ .setContentText("$count ${context.string(contentRes)}")
+ .setGroup("${groupPrefix}_$userId")
+ .setGroupSummary(true)
+ .setContentIntent(pendingIntent)
+ .setCategory(Notification.CATEGORY_SOCIAL)
+
+ NotificationManagerCompat.from(context).notify("${groupPrefix}_$userId", userId.toInt(), notifBuilder.build())
+ }
+}
/**
* Notification data holder
@@ -93,44 +159,7 @@ data class NotificationContent(val data: CookieModel,
val title: String? = null,
val text: String,
val timestamp: Long,
- val profileUrl: String) {
-
- fun createNotification(context: Context) = createNotification(context, FROST_NOTIFICATION_GROUP)
-
- fun createMessageNotification(context: Context) = createNotification(context, FROST_MESSAGE_NOTIFICATION_GROUP)
-
- private fun createNotification(context: Context, groupPrefix: String) {
- val intent = Intent(context, FrostWebActivity::class.java)
- intent.data = Uri.parse(href.formattedFbUrl)
- intent.putExtra(ARG_USER_ID, data.id)
- val overlayContext = if (groupPrefix == FROST_MESSAGE_NOTIFICATION_GROUP) OverlayContext.MESSAGE else OverlayContext.NOTIFICATION
- intent.putExtra(ARG_OVERLAY_CONTEXT, overlayContext)
- val group = "${groupPrefix}_${data.id}"
- val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
- val ringtone = if (groupPrefix == FROST_MESSAGE_NOTIFICATION_GROUP) Prefs.messageRingtone else Prefs.notificationRingtone
- val notifBuilder = context.frostNotification(ringtone)
- .setContentTitle(title ?: context.string(R.string.frost_name))
- .setContentText(text)
- .setContentIntent(pendingIntent)
- .setCategory(Notification.CATEGORY_SOCIAL)
- .setSubText(data.name)
- .setGroup(group)
-
- if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000)
- L.v("Notif load", this.toString())
- NotificationManagerCompat.from(context).notify(group, notifId, notifBuilder.withBigText.build())
-
- if (profileUrl.isNotBlank()) {
- context.runOnUiThread {
- Glide.with(context)
- .asBitmap()
- .load(profileUrl)
- .withRoundIcon()
- .into(FrostNotificationTarget(context, notifId, group, notifBuilder))
- }
- }
- }
-}
+ val profileUrl: String)
const val NOTIFICATION_PERIODIC_JOB = 7
@@ -139,7 +168,11 @@ const val NOTIFICATION_PERIODIC_JOB = 7
* returns false if an error occurs; true otherwise
*/
fun Context.scheduleNotifications(minutes: Long): Boolean {
- val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
+ val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler?
+ if (scheduler == null) {
+ L.e("JobScheduler not found; cannot schedule notifications")
+ return false
+ }
scheduler.cancel(NOTIFICATION_PERIODIC_JOB)
if (minutes < 0L) return true
val serviceComponent = ComponentName(this, NotificationService::class.java)
@@ -161,7 +194,11 @@ const val NOTIFICATION_JOB_NOW = 6
* Run notification job right now
*/
fun Context.fetchNotifications(): Boolean {
- val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
+ val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler?
+ if (scheduler == null) {
+ L.e("JobScheduler not found")
+ return false
+ }
val serviceComponent = ComponentName(this, NotificationService::class.java)
val builder = JobInfo.Builder(NOTIFICATION_JOB_NOW, serviceComponent)
.setMinimumLatency(0L)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
index 9321c42f..d481e941 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
@@ -1,17 +1,12 @@
package com.pitchedapps.frost.services
-import android.app.Notification
-import android.app.PendingIntent
import android.app.job.JobParameters
import android.app.job.JobService
import android.content.Context
-import android.content.Intent
-import android.net.Uri
import android.support.v4.app.NotificationManagerCompat
import ca.allanwang.kau.utils.string
import com.pitchedapps.frost.BuildConfig
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.activities.FrostWebActivity
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.dbflow.lastNotificationTime
import com.pitchedapps.frost.dbflow.loadFbCookie
@@ -21,7 +16,6 @@ import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
import com.pitchedapps.frost.facebook.formattedFbUrl
import com.pitchedapps.frost.injectors.JsAssets
-import com.pitchedapps.frost.utils.ARG_USER_ID
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.frostAnswersCustom
@@ -130,20 +124,18 @@ class NotificationService : JobService() {
val prevLatestEpoch = prevNotifTime.epoch
L.v("Notif Prev Latest Epoch $prevLatestEpoch")
var newLatestEpoch = prevLatestEpoch
- unreadNotifications.forEach unread@ {
- elem ->
+ unreadNotifications.forEach unread@ { elem ->
val notif = parseNotification(data, elem) ?: return@unread
L.v("Notif timestamp ${notif.timestamp}")
if (notif.timestamp <= prevLatestEpoch) return@unread
- notif.createNotification(this@NotificationService)
+ NotificationType.GENERAL.createNotification(this, notif, notifCount == 0)
if (notif.timestamp > newLatestEpoch)
newLatestEpoch = notif.timestamp
notifCount++
}
if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epoch = newLatestEpoch).save()
L.d("Notif new latest epoch ${lastNotificationTime(data.id).epoch}")
- frostAnswersCustom("Notifications", "Type" to "General", "Count" to notifCount)
- summaryNotification(data.id, notifCount)
+ NotificationType.GENERAL.summaryNotification(this, data.id, notifCount)
}
fun parseNotification(data: CookieModel, element: Element): NotificationContent? {
@@ -161,9 +153,6 @@ class NotificationService : JobService() {
return NotificationContent(data, notifId.toInt(), a.attr("href"), null, text, epoch, pUrl)
}
- fun summaryNotification(userId: Long, count: Int)
- = summaryNotification(userId, count, R.string.notifications, FbItem.NOTIFICATIONS.url, FROST_NOTIFICATION_GROUP)
-
/*
* ----------------------------------------------------------------
* Instant message notification logic.
@@ -174,8 +163,7 @@ class NotificationService : JobService() {
inline fun fetchMessageNotifications(data: CookieModel, crossinline callback: (success: Boolean) -> Unit) {
launchHeadlessHtmlExtractor(FbItem.MESSAGES.url, JsAssets.NOTIF_MSG) {
- it.observeOn(Schedulers.newThread()).subscribe {
- (html, errorRes) ->
+ it.observeOn(Schedulers.newThread()).subscribe { (html, errorRes) ->
L.d("Notf IM html received")
if (errorRes != -1) return@subscribe callback(false)
fetchMessageNotifications(data, html)
@@ -193,20 +181,18 @@ class NotificationService : JobService() {
val prevLatestEpoch = prevNotifTime.epochIm
L.v("Notif Prev Latest Im Epoch $prevLatestEpoch")
var newLatestEpoch = prevLatestEpoch
- unreadNotifications.forEach unread@ {
- elem ->
+ unreadNotifications.forEach unread@ { elem ->
val notif = parseMessageNotification(data, elem) ?: return@unread
L.v("Notif im timestamp ${notif.timestamp}")
if (notif.timestamp <= prevLatestEpoch) return@unread
- notif.createMessageNotification(this@NotificationService)
+ NotificationType.MESSAGE.createNotification(this, notif, notifCount == 0)
if (notif.timestamp > newLatestEpoch)
newLatestEpoch = notif.timestamp
notifCount++
}
if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epochIm = newLatestEpoch).save()
L.d("Notif new latest im epoch ${lastNotificationTime(data.id).epochIm}")
- frostAnswersCustom("Notifications", "Type" to "Message", "Count" to notifCount)
- summaryMessageNotification(data.id, notifCount)
+ NotificationType.MESSAGE.summaryNotification(this, data.id, notifCount)
}
fun parseMessageNotification(data: CookieModel, element: Element): NotificationContent? {
@@ -225,32 +211,12 @@ class NotificationService : JobService() {
return NotificationContent(data, notifId.toInt(), a.attr("href"), a.text(), text, epoch, pUrl)
}
- fun summaryMessageNotification(userId: Long, count: Int)
- = summaryNotification(userId, count, R.string.messages, FbItem.MESSAGES.url, FROST_MESSAGE_NOTIFICATION_GROUP)
-
private fun Context.debugNotification(text: String) {
if (!BuildConfig.DEBUG) return
- val notifBuilder = frostNotification
+ val notifBuilder = frostNotification.withDefaults()
.setContentTitle(string(R.string.frost_name))
.setContentText(text)
NotificationManagerCompat.from(this).notify(999, notifBuilder.build())
}
- private fun summaryNotification(userId: Long, count: Int, contentRes: Int, pendingUrl: String, groupPrefix: String) {
- if (count <= 1) return
- val intent = Intent(this, FrostWebActivity::class.java)
- intent.data = Uri.parse(pendingUrl)
- intent.putExtra(ARG_USER_ID, userId)
- val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
- val notifBuilder = frostNotification
- .setContentTitle(string(R.string.frost_name))
- .setContentText("$count ${string(contentRes)}")
- .setGroup("${groupPrefix}_$userId")
- .setGroupSummary(true)
- .setContentIntent(pendingIntent)
- .setCategory(Notification.CATEGORY_SOCIAL)
-
- NotificationManagerCompat.from(this).notify("${groupPrefix}_$userId", userId.toInt(), notifBuilder.build())
- }
-
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt
index e161533a..64fb130f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt
@@ -1,162 +1,38 @@
package com.pitchedapps.frost.utils
-import android.app.Notification
-import android.app.PendingIntent
+import android.app.DownloadManager
import android.content.Context
-import android.content.Intent
-import android.support.v4.app.NotificationCompat
-import android.support.v4.app.NotificationManagerCompat
-import android.support.v4.content.FileProvider
+import android.content.Context.DOWNLOAD_SERVICE
+import android.net.Uri
+import android.os.Environment
+import android.webkit.URLUtil
import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE
import ca.allanwang.kau.permissions.kauRequestPermissions
-import ca.allanwang.kau.utils.copyFromInputStream
-import ca.allanwang.kau.utils.string
-import com.pitchedapps.frost.BuildConfig
-import com.pitchedapps.frost.R
-import com.pitchedapps.frost.services.DownloadService
-import com.pitchedapps.frost.services.frostNotification
-import com.pitchedapps.frost.services.getNotificationPendingCancelIntent
-import com.pitchedapps.frost.services.quiet
-import okhttp3.MediaType
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okhttp3.ResponseBody
-import okio.*
-import org.jetbrains.anko.AnkoAsyncContext
-import org.jetbrains.anko.startService
-import java.io.File
-import java.io.IOException
-import java.lang.ref.WeakReference
+import com.pitchedapps.frost.dbflow.loadFbCookie
+
/**
* Created by Allan Wang on 2017-08-04.
*
- * With reference to the <a href="https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java">OkHttp3 sample</a>
- *
- * TODO delete this file; we've moved to our downloader service for now and will create our own player soon
+ * With reference to <a href="https://stackoverflow.com/questions/33434532/android-webview-download-files-like-browsers-do">Stack Overflow</a>
*/
-fun Context.frostDownload(url: String) {
+fun Context.frostDownload(url: String, userAgent: String, contentDisposition: String, mimeType: String, contentLength: Long) {
L.d("Received download request", "Download $url")
kauRequestPermissions(PERMISSION_WRITE_EXTERNAL_STORAGE) { granted, _ ->
- if (granted)
-// doAsync { frostDownloadImpl(url, type) }
- startService<DownloadService>(DownloadService.EXTRA_URL to url)
- }
-}
-
-private const val MAX_PROGRESS = 1000
-//private val DOWNLOAD_GROUP: String? = null
-private const val DOWNLOAD_GROUP = "frost_downloads"
-
-private enum class DownloadType(val downloadingRes: Int, val downloadedRes: Int) {
- VIDEO(R.string.downloading_video, R.string.downloaded_video),
- FILE(R.string.downloading_file, R.string.downloaded_file);
-
- fun getPendingIntent(context: Context, file: File): PendingIntent {
- val uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
- val type = context.contentResolver.getType(uri)
- L.d("DownloadType: retrieved pending intent - $uri $type")
- val intent = Intent(Intent.ACTION_VIEW, uri)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- .setDataAndType(uri, type)
- return PendingIntent.getActivity(context, 0, intent, 0)
- }
-}
-
-private fun AnkoAsyncContext<Context>.frostDownloadImpl(url: String, type: DownloadType) {
- L.d("Starting download request")
- val notifId = Math.abs(url.hashCode() + System.currentTimeMillis().toInt())
- var notifBuilderAttempt: NotificationCompat.Builder? = null
- weakRef.get()?.apply {
- notifBuilderAttempt = frostNotification.quiet
- .setContentTitle(string(type.downloadingRes))
- .setCategory(Notification.CATEGORY_PROGRESS)
- .setWhen(System.currentTimeMillis())
- .setProgress(MAX_PROGRESS, 0, false)
- .setOngoing(true)
- .addAction(R.drawable.ic_action_cancel, string(R.string.kau_cancel), getNotificationPendingCancelIntent(DOWNLOAD_GROUP, notifId))
- .setGroup(DOWNLOAD_GROUP)
- }
- val notifBuilder = notifBuilderAttempt ?: return
- notifBuilder.show(weakRef, notifId)
-
- val request: Request = Request.Builder()
- .url(url)
- .tag(url)
- .build()
-
- var client: OkHttpClient? = null
- client = OkHttpClient.Builder()
- .addNetworkInterceptor { chain ->
- val original = chain.proceed(chain.request())
- return@addNetworkInterceptor original.newBuilder().body(ProgressResponseBody(original.body()!!) { bytesRead, contentLength, done ->
- //cancel request if context reference is now invalid
- if (weakRef.get() == null) {
- client?.cancel(url)
- }
- val ctx = weakRef.get() ?: return@ProgressResponseBody client?.cancel(url) ?: Unit
- val percentage = bytesRead.toFloat() / contentLength.toFloat() * MAX_PROGRESS
- L.v("Download request progress: $percentage")
- notifBuilder.setProgress(MAX_PROGRESS, percentage.toInt(), false)
- if (done) {
- notifBuilder.setFinished(ctx, type)
- L.d("Download request finished")
- }
- notifBuilder.show(weakRef, notifId)
- }).build()
- }
- .build()
- client.newCall(request).execute().use { response ->
- if (!response.isSuccessful) throw IOException("Unexpected code $response")
- val stream = response.body()?.byteStream()
- if (stream != null) {
- val destination = createMediaFile(".mp4")
- destination.copyFromInputStream(stream)
- weakRef.get()?.apply {
- notifBuilder.setContentIntent(type.getPendingIntent(this, destination))
- notifBuilder.show(weakRef, notifId)
- }
- }
- }
-}
-
-private fun NotificationCompat.Builder.setFinished(context: Context, type: DownloadType)
- = setContentTitle(context.string(type.downloadedRes))
- .setProgress(0, 0, false).setOngoing(false).setAutoCancel(true)
- .apply { mActions.clear() }
-
-private fun OkHttpClient.cancel(url: String) {
- val call = dispatcher().runningCalls().firstOrNull { it.request().tag() == url }
- if (call != null && !call.isCanceled) call.cancel()
-}
-
-private fun NotificationCompat.Builder.show(weakRef: WeakReference<Context>, notifId: Int) {
- val c = weakRef.get() ?: return
- NotificationManagerCompat.from(c).notify(DOWNLOAD_GROUP, notifId, build())
-}
-
-private class ProgressResponseBody(
- val responseBody: ResponseBody,
- val listener: (bytesRead: Long, contentLength: Long, done: Boolean) -> Unit) : ResponseBody() {
-
- private val bufferedSource: BufferedSource by lazy { Okio.buffer(source(responseBody.source())) }
-
- override fun contentLength(): Long = responseBody.contentLength()
-
- override fun contentType(): MediaType? = responseBody.contentType()
-
- override fun source(): BufferedSource = bufferedSource
-
- private fun source(source: Source): Source = object : ForwardingSource(source) {
-
- private var totalBytesRead = 0L
-
- override fun read(sink: Buffer?, byteCount: Long): Long {
- val bytesRead = super.read(sink, byteCount)
- // read() returns the number of bytes read, or -1 if this source is exhausted.
- totalBytesRead += if (bytesRead != -1L) bytesRead else 0
- listener(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
- return bytesRead
- }
+ if (!granted) return@kauRequestPermissions
+
+ val request = DownloadManager.Request(Uri.parse(url))
+
+ request.setMimeType(mimeType)
+ val cookie = loadFbCookie(Prefs.userId) ?: return@kauRequestPermissions
+ request.addRequestHeader("cookie", cookie.cookie)
+ request.addRequestHeader("User-Agent", userAgent)
+ request.setDescription("Downloading file...")
+ request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimeType))
+ request.allowScanningByMediaScanner()
+ request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
+ request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "Frost/" + URLUtil.guessFileName(url, contentDisposition, mimeType))
+ val dm = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
+ dm.enqueue(request)
}
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
index 7fb7324e..f6d64ab7 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
@@ -70,7 +70,7 @@ class FrostWebView @JvmOverloads constructor(
webChromeClient = FrostChromeClient(this)
addJavascriptInterface(FrostJSI(this), "Frost")
setBackgroundColor(Color.TRANSPARENT)
- setDownloadListener { downloadUrl, _, _, _, _ -> context.frostDownload(downloadUrl) }
+ setDownloadListener(context::frostDownload)
}
}
diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml
index 42046133..895c271f 100644
--- a/app/src/main/res/xml/frost_changelog.xml
+++ b/app/src/main/res/xml/frost_changelog.xml
@@ -11,6 +11,12 @@
<!--<version title="Beta Updates" />-->
<version title="Beta Updates"/>
+ <item text="Add default download manager to download all files" />
+ <item text="Limit notification sounds when multiple notifications come in" />
+ <item text="Check that job scheduler exists before scheduling notifications" />
+ <item text="" />
+
+ <version title="v1.5.1"/>
<item text="Release day is here!" />
<item text="Add full support for messaging in overlays. We will dynamically launch new overlays when required to." />
<item text="Prevent bad messenger intent from launching" />
@@ -19,10 +25,6 @@
<item text="Ensure that bottom bar layout does not hide the web content" />
<item text="Add option to share external links to Frost" />
<item text="Trigger notification service on each app start" />
- <item text="" />
- <item text="" />
- <item text="" />
- <item text="" />
<version title="v1.4.13"/>
<item text="Prevent image loading from trimming too many characters" />
diff --git a/docs/Changelog.md b/docs/Changelog.md
index b9bf21ee..0518f37f 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -1,6 +1,9 @@
# Changelog
## Beta Updates
+* Add default download manager to download all files
+
+## v1.5.1
* Release day is here!
* Add full support for messaging in overlays. We will dynamically launch new overlays when required to.
* Prevent bad messenger intent from launching
@@ -8,6 +11,7 @@
* Add contextual menu items. Easily go to your full list of notifications or messages from the overlay.
* Ensure that bottom bar layout does not hide the web content
* Add option to share external links to Frost
+* Trigger notification service on each app start
## v1.4.13
* Prevent image loading from trimming too many characters