-
2022-03-03 09:35:23
NotificationUtil.kt
object NotificationUtil { /** * 打开手机设置页面 * @param context Context */ fun setNotification(context: Context) { val enabled = isNotificationEnabled(context) if (!enabled) { val localIntent = Intent() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { localIntent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS localIntent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) localIntent.putExtra("app_uid", context.applicationInfo.uid) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { localIntent.action = "android.settings.APP_NOTIFICATION_SETTINGS" localIntent.putExtra("app_package", context.packageName) localIntent.putExtra("app_uid", context.applicationInfo.uid) } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { localIntent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS localIntent.addCategory(Intent.CATEGORY_DEFAULT) localIntent.data = Uri.parse("package:" + context.packageName) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) localIntent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS localIntent.data = Uri.fromParts("package", context.packageName, null) } else { localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) if (Build.VERSION.SDK_INT >= 9) { localIntent.action = "android.settings.APPLICATION_DETAILS_SETTINGS" localIntent.data = Uri.fromParts("package", context.packageName, null) } else if (Build.VERSION.SDK_INT <= 8) { localIntent.action = Intent.ACTION_VIEW localIntent.setClassName( "com.android.settings", "com.android.setting.InstalledAppDetails" ) localIntent.putExtra( "com.android.settings.ApplicationPkgName", context.packageName ) } } context.startActivity(localIntent) } } /** * 判断当前app在手机中是否开启了允许消息推送 * @param mContext Context * @return Boolean */ fun isNotificationEnabled(mContext: Context): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val mNotificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager var channel = mNotificationManager.getNotificationChannel(PortalCode.ChannelId) !(!mNotificationManager.areNotificationsEnabled() || channel.importance == NotificationManager.IMPORTANCE_NONE) } else if (Build.VERSION.SDK_INT >= 24) { val mNotificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager mNotificationManager.areNotificationsEnabled() } else if (Build.VERSION.SDK_INT >= 19) { val CHECK_OP_NO_THROW = "checkOpNoThrow" val OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION" val appOps = mContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager val appInfo = mContext.applicationInfo val pkg = mContext.applicationContext.packageName val uid = appInfo.uid try { val appOpsClass = Class.forName(AppOpsManager::class.java.name) val checkOpNoThrowMethod = appOpsClass.getMethod( CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String::class.java ) val opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION) val value = opPostNotificationValue[Int::class.java] as Int (checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) as Int == AppOpsManager.MODE_ALLOWED) } catch (e: ClassNotFoundException) { true } catch (e: NoSuchMethodException) { true } catch (e: NoSuchFieldException) { true } catch (e: InvocationTargetException) { true } catch (e: IllegalAccessException) { true } catch (e: RuntimeException) { true } } else { true } } }
调用:
if (NotificationUtil.isNotificationEnabled(this)) { //当前app允许消息通知 } else { AlertDialog.Builder() .message("为了您能收到推送通知请点击确定开始设置") .okButtonText("确定") .build().apply { setOkBtnClickListener { NotificationUtil.setNotification(this@CurMainActivity) } show(this@CurMainActivity) } }
更多相关内容 -
Android仿QQ消息通知栏新消息实时更新效果
2017-03-16 18:11:50Android仿QQ消息通知栏新消息实时更新效果,实时接收新消息显示并更新,点击通知栏清除所有新消息 -
Vue 自定义消息通知组件
2021-07-28 15:31:26由于业务需求,需要使用一个消息的全局通知组件,项目原来是使用Element上的通知组件,但是到了后面发现Element的通知组件无法满足消息通知的时间性以及可条跳转性准则所以需要自己开发一个消息通知组件,话不多说,...前言
由于业务需求,需要使用一个消息的全局通知组件,项目原来是使用Element上的通知组件,但是到了后面发现Element的通知组件无法满足消息通知的时间性以及可条跳转性准则所以需要自己开发一个消息通知组件,话不多说,先上干货吧。
下面为消息通知组件的使用情况,这个组件设置为可跳转和不可跳转两种。
可跳转通知(在查看详情中可定义相关方法通过点击去触发,比如说放路由,或者相关URL)
不可跳转通知(纯通知)
这个是通知组件设计时的vue上的目录结构
通知组件的调用方法
组件调用代码
this.$myMsg.notify({ title:"通知标题", content: "通知内容", type: 'jumpNotification', time: 0, timeNow:new Date().getTime() });
界面组件代码
<!-- * @Descripttion: 通知组件的绘制界面 * @version: * @Author: 刘延强 * @Date: 2021-07-26 09:51:32 * @LastEditors: 刘延强 * @LastEditTime: 2021-07-28 15:52:07 --> <template> <transition name="slide-fade"> <div class="my-notify" v-if="notifyFlag"> <!-- 可跳转通知 --> <div class="notify" v-if="type=='jumpNotification'"> <div class="notice"> <div><div class="iconmessage"><div class="el-icon-bell icon-xiaoxi"></div></div></div> <div class="subject-content"> <div class="h3">{{title}}<!-- 消息标题 --> </div> <div class="content"> <div class="left-part"> <span class="right">{{ content }}</span><!-- 消息内容 --> </div> </div> </div> <div class="close" @click="close()"> <i class="el-icon-circle-close"/> </div> </div> <div class="tableTitle"></div> <div class="more" @click="clickSet()" > <div class="txt" v-time="timeNow"></div> <div class="toinfo"> <span class="txt">查看详情</span> <span class="el-icon-arrow-right el-icon--right"/> </div> </div> </div> <!-- 不跳转的通知 --> <div class="notify" v-if="type=='noJumpNotification'"> <div class="notice"> <div><div class="iconmessage1"><div class="el-icon-bell icon-xiaoxi"></div></div></div> <div class="subject-content"> <div class="h3">{{title}}<!-- 消息标题 --> </div> <div class="content"> <div class="left-part"> <span class="right">{{ content }}</span><!-- 消息内容 --> </div> </div> </div> <div class="close" @click="close()"> <i class="el-icon-circle-close"/> </div> </div> </div> </div> </transition> </template> <style scoped> .subject-content{ margin: 0 0 15px 10px; } .left-part { width: 260px; overflow: hidden; display: flex; flex-flow: row wrap; justify-content: start; align-items: center; } .iconmessage { width: 44px; height: 44px; background: rgba(125, 191, 243, 1); border-radius: 100%; margin-top: 7px; } .iconmessage1 { width: 44px; height: 44px; background: rgba(125, 191, 243, 1); border-radius: 100%; margin-top: -17px; } .icon-xiaoxi { text-align: center; line-height: 45px; font-size: 26px; color: white; margin-left: 9px; } .notice { display: flex; flex-flow: row nowrap; align-items: center; width: calc(100% - 30px); padding: 0 15px; background: white; padding-bottom: 10px; border-radius: 5px; padding-top: 25px; z-index:99999; } .close { position: absolute; right: 5px; top: 1px; color: #409eff; cursor: pointer; font-size: 22px; font-weight: bold; } .tableTitle { position: relative; margin: 0 auto; width: 100%; height: 1px; background-color: #d4d4d4; text-align: center; font-size: 16px; color: rgba(101, 101, 101, 1); } .more { display: flex; flex-flow: row nowrap; justify-content: space-between; align-items: center; width: calc(100% - 30px); padding: 0 15px; background: white; border-radius: 5px; } .txt{ cursor:pointer; font-size: 12px; font-family: MicrosoftYaHei; color: rgba(169, 169, 169, 1); line-height: 16px; padding: 20px 0; } .info { display: flex; flex-flow: column nowrap; justify-content: center; align-items: flex-start; position: relative; top: 0.66667vw; width: calc(100% - 24vw); } .toinfo { display: flex; justify-content: center; align-items: center; } .slide-fade-leave-active { transition: all .2s cubic-bezier(1.0, 0.5, 0.8, 1.0); } .slide-fade-enter, .slide-fade-leave-to{ transform: translateX(10px); opacity: 0; } .my-notify{ margin: 10px; width: 350px; z-index: 99999; } .notify{ position: relative; width: 350px; border-radius: 5px; background-color:white; box-shadow: -5px 5px 12px 0 rgba(204, 204, 204, .8); animation: show cubic-bezier(.18,.89,.32,1.28) .4s; /* 图文不可复制CSS */ -webkit-user-select: none; -ms-user-select: none; -moz-user-select: none; -khtml-user-select: none; user-select: none; } .notify .tip{ height: 30px; margin-bottom: 5px; line-height: 30px; } .h3 { width: 260px; font-family: MicrosoftYaHei; color: rgba(169, 169, 169, 1); color: #232323; font-size: 17px; font-weight: 600; padding: 0; } .notify .tip span{ line-height: 30px; font-size: 17px; font-weight: 600; } @keyframes show{ 0%{ right: -350px; } 100%{ right: 10px; } } </style>
界面逻辑JS
/* * @Descripttion: 通知组件的相关逻辑 * @version: * @Author: 刘延强 * @Date: 2021-07-26 09:52:22 * @LastEditors: 刘延强 * @LastEditTime: 2021-07-28 16:16:00 */ import vue from 'vue' import myNotify from './myNotify' import './timeDifference.js' import Utils from './util'; // 创建vue组件实例 const notify = vue.extend(myNotify); //添加通知节点(用来存放通知的元素) let notifyWrap = document.createElement('div'); notifyWrap.className = "notify-wrap" // notifyWrap.style = "position: fixed; right: 0px; top: 90px; transition-duration: .5s;" notifyWrap.style = "position: fixed;right: 0px; bottom: 0px; transition-duration: .5s;" document.body.appendChild(notifyWrap); let myMsg = { /** * 通知框 * @title 提示标题 * @content 提示内容; * @type 提示框类型,parameter: jumpNotification,noJumpNotification * @time 显示时长 * @timeNow 弹窗出现的时间 */ notify: ({ title, timeNow, content, type, time = 2000, }) => { //创建一个存放通知的div const notifyDom = new notify({ el: document.createElement('div'), data () { return { title:title, //文本标题 timeNow:timeNow,//弹窗出现的时间 notifyFlag: true, // 是否显示 time: time,//取消按钮是否显示 content: content, // 文本内容 type: type, // 类型 timer: '', timeFlag: false, } }, watch:{ timeFlag(){ if(this.timeFlag){ this.notifyFlag = false; window.clearTimeout(this.timer); } } }, created(){ this.timer = setTimeout(() => { if(this.time!=0){ this.timeFlag = true; } }, this.time); }, //销毁一个通知实例 beforeDestroy(){ window.clearTimeout(this.timer); }, methods: { //关闭通知消息 close() { window.clearTimeout(this.timer); this.notifyFlag = false; }, //点击查看详情的方法 clickSet() { Utils.$emit('Brainstorm','msg') this.close(); } } }) //往notifyWrap里面添加通知 // notifyWrap.appendChild(notifyDom.$el); //这个的意思是把div元素节点添加到body元素节点中成为其子节点,但是其后面添加的新节点放在body的现有子节点的最后 notifyWrap.insertBefore(notifyDom.$el, document.body.lastElementChild.firstChild);//这个的意思是把div元素节点添加到body元素节点中成为其子节点,但是放在body的现有子节点的最前面 } } //注册 function register(){ vue.prototype.$myMsg = myMsg } export default { myMsg, register }
通知时间JS
/* * @Descripttion: 消息推送的时间变化组件 * @version: * @Author: 刘延强 * @Date: 2021-07-23 18:29:19 * @LastEditors: 刘延强 * @LastEditTime: 2021-07-23 19:29:52 */ import Vue from 'vue' /** * 实时时间转换指令,大于一个月则返回具体的年月日 * @param { string } timeStamp - 时间 格式:年-月-日 时:分:秒 或 时间戳 * @returns { string } */ function getFormatTime(timeStamp){ var dateTime = new Date(timeStamp) // 将传进来的字符串或者毫秒转为标准时间 var year = dateTime.getFullYear() var month = dateTime.getMonth() + 1 var day = dateTime.getDate() var hour = dateTime.getHours() var minute = dateTime.getMinutes() // var second = dateTime.getSeconds() var millisecond = dateTime.getTime() // 将当前编辑的时间转换为毫秒 var now = new Date() // 获取本机当前的时间 var nowNew = now.getTime() // 将本机的时间转换为毫秒 var milliseconds = 0 var timeSpanStr milliseconds = nowNew - millisecond if (milliseconds <= 1000 * 60 * 1) { // 小于一分钟展示为刚刚 timeSpanStr = '刚刚' } else if (1000 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60) { // 大于一分钟小于一小时展示为分钟 timeSpanStr = Math.round((milliseconds / (1000 * 60))) + '分钟前' } else if (1000 * 60 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24) { // 大于一小时小于一天展示为小时 timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60)) + '小时前' } else if (1000 * 60 * 60 * 24 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24 * 15) { // 大于一天小于十五天展示位天 timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60 * 24)) + '天前' } else if (milliseconds > 1000 * 60 * 60 * 24 * 15 && year === now.getFullYear()) { timeSpanStr = month + '-' + day + ' ' + hour + ':' + minute } else { timeSpanStr = year + '-' + month + '-' + day + ' ' + hour + ':' + minute } return timeSpanStr } Vue.directive('time', { bind: function(el, binding){ el.innerHTML = getFormatTime(binding.value); //每隔一分钟更新一次 el.__timeout__ = setInterval(function(){ el.innerHTML = getFormatTime(binding.value); }, 60000); }, unbind: function(el){ clearInterval(el.__timeout__); delete el.__timeout__; } });
查看详情的调用方法的公共文件
/* * @Descripttion: 跨页面方法调用方法的公共文件 * @version: * @Author: 刘延强 * @Date: 2021-07-26 17:55:56 * @LastEditors: 刘延强 * @LastEditTime: 2021-07-26 20:01:49 */ import Vue from 'vue' export default new Vue
被远程跨界面调用的方法
//被远程跨界面调用的方法 //@Brainstorm 头脑风暴跳转调用 var that = this; Utils.$on('Brainstorm', function (msg) { that.jumpPage(); })
最后需要去main.js注册才可以全局使用这个组件
//通知消息 import message from "@/components/myMsg/index" Vue.use(message.register);
写的不是很好,望大佬们指正!
-
Laravel 论坛系统之消息通知功能
2019-01-09 14:58:48消息通知 这篇文章我们来开发消息通知功能,当话题有新回复时,我们将通知作者『你的话题有新回复,请查看』类似的信息。 Laravel 的消息通知系统 Laravel 自带了一套极具扩展性的消息通知系统,尤其还支持多种通知...消息通知
这篇文章我们来开发消息通知功能,当话题有新回复时,我们将通知作者『你的话题有新回复,请查看』类似的信息。
Laravel 的消息通知系统
Laravel 自带了一套极具扩展性的消息通知系统,尤其还支持多种通知频道,我们将利用此套系统来向用户发送消息提醒。
什么是通知频道?
通知频道是通知传播的途径,Laravel 自带的有数据库、邮件、短信(通过 Nexmo)以及 Slack。本章节中我们将使用数据库通知频道,后面也会使用到邮件通知频道。
1. 准备数据库
数据通知频道会在一张数据表里存储所有通知信息。包含了比如通知类型、JSON 格式数据等描述通知的信息。我们后面会通过查询这张表的内容在应用界面上展示通知。但是在这之前,我们需要先创建这张数据表,Laravel 自带了生成迁移表的命令,执行以下命令即可:
$ php artisan notifications:table
会生成 database/migrations/{$timestamp}_create_notifications_table.php 迁移文件,执行 migrate 命令将表结构写入数据库中:
$ php artisan migrate
我们还需要在 users 表里新增 notification_count 字段,用来跟踪用户有多少未读通知,如果未读通知大于零的话,就在站点的全局顶部导航栏显示红色的提醒。
$ php artisan make:migration add_notification_count_to_users_table --table=users
打开生成的文件,修改为以下:
database/migrations/{$timestamp}_add_notification_count_to_users_table.php
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddNotificationCountToUsersTable extends Migration { public function up() { Schema::table('users', function (Blueprint $table) { $table->integer('notification_count')->unsigned()->default(0); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('notification_count'); }); } }
再次应用数据库修改:
$ php artisan migrate
2. 生成通知类
Laravel 中一条通知就是一个类(通常存在 app/Notifications 文件夹里)。看不到的话不要担心,运行一下以下命令即可创建:
$ php artisan make:notification TopicReplied
修改文件为以下:
app/Notifications/TopicReplied.php
<?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use App\Models\Reply; class TopicReplied extends Notification { use Queueable; public $reply; public function __construct(Reply $reply) { // 注入回复实体,方便 toDatabase 方法中的使用 $this->reply = $reply; } public function via($notifiable) { // 开启通知的频道 return ['database']; } public function toDatabase($notifiable) { $topic = $this->reply->topic; $link = $topic->link(['#reply' . $this->reply->id]); // 存入数据库里的数据 return [ 'reply_id' => $this->reply->id, 'reply_content' => $this->reply->content, 'user_id' => $this->reply->user->id, 'user_name' => $this->reply->user->name, 'user_avatar' => $this->reply->user->avatar, 'topic_link' => $link, 'topic_id' => $topic->id, 'topic_title' => $topic->title, ]; } }
每个通知类都有个 via() 方法,它决定了通知在哪个频道上发送。我们写上 database 数据库来作为通知频道。
因为使用数据库通知频道,我们需要定义 toDatabase()。这个方法接收 $notifiable 实例参数并返回一个普通的 PHP 数组。这个返回的数组将被转成 JSON 格式并存储到通知数据表的 data 字段中。
3. 触发通知
我们希望当用户回复主题后,通知到主题作者。故触发通知的时机是:『回复发布成功后』,在模型监控器里,我们可以在 created 方法里实现此部分代码,修改 created() 方法为以下:
app/Observers/ReplyObserver.php
<?php . . . use App\Notifications\TopicReplied; class ReplyObserver { public function created(Reply $reply) { $reply->topic->reply_count = $reply->topic->replies->count(); $reply->topic->save(); // 通知话题作者有新的评论 $reply->topic->user->notify(new TopicReplied($reply)); } . . . }
请注意顶部引入 TopicReplied 。默认的 User 模型中使用了 trait —— Notifiable,它包含着一个可以用来发通知的方法 notify() ,此方法接收一个通知实例做参数。虽然 notify() 已经很方便,但是我们还需要对其进行定制,我们希望每一次在调用 $user->notify() 时,自动将 users 表里的 notification_count +1 ,这样我们就能跟踪用户未读通知了。
打开
User.php
文件,将 use Notifiable, MustVerifyEmailTrait; 修改为以下:app/Models/User.php
<?php . . . use Auth; class User extends Authenticatable implements MustVerifyEmailContract { use MustVerifyEmailTrait; use Notifiable { notify as protected laravelNotify; } public function notify($instance) { // 如果要通知的人是当前用户,就不必通知了! if ($this->id == Auth::id()) { return; } // 只有数据库类型通知才需提醒,直接发送 Email 或者其他的都 Pass if (method_exists($instance, 'toDatabase')) { $this->increment('notification_count'); } $this->laravelNotify($instance); } . . . }
请注意顶部 Auth 的引入。
我们对 notify() 方法做了一个巧妙的重写,现在每当你调用 $user->notify() 时, users 表里的 notification_count 将自动 +1。
接下来我们需要将通知展示出来
4. 新建路由器
首先我们需要新增路由入口:
routes/web.php
. . . Route::resource('notifications', 'NotificationsController', ['only' => ['index']]);
5. 修改顶部导航栏入口,新增通知标识
我们希望用户在访问网站时,能在很显眼的地方提醒他你有未读信息,接下来我们会利用上 notification_count 字段,新增下面的 消息通知标记 区块:
resources/views/layouts/_header.blade.php
<li class="nav-item notification-badge"> <a class="nav-link mr-3 badge badge-pill badge-{{ Auth::user()->notification_count > 0 ? 'hint' : 'secondary' }} text-white" href="{{ route('notifications.index') }}"> {{ Auth::user()->notification_count }} </a> </li>
刷新页面即可看到消息提醒标示:
样式有点乱,我们稍加调整:
resources/sass/app.scss
. . . /* 消息通知 */ .notification-badge { .badge { font-size: 12px; margin-top: 14px; } .badge-secondary { background-color: #EBE8E8; } .badge-hint { background-color: #d15b47 !important;; } }
默认情况下样式很低调:
我们重新使用 Summer 用户登录,可看到显眼的红色标示,并且带有未读消息数量:
6. 新建 Notifications 控制器
如果你点击红色标示,会报错 —— 控制器文件并不存在:
接下来我们使用命令行生成控制器:$ php artisan make:controller NotificationsController
修改控制器的代码如下:
app/Http/Controllers/NotificationsController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Auth; class NotificationsController extends Controller { public function __construct() { $this->middleware('auth'); } public function index() { // 获取登录用户的所有通知 $notifications = Auth::user()->notifications()->paginate(20); return view('notifications.index', compact('notifications')); } }
控制器的构造方法 __construct() 里调用 Auth 中间件,要求必须登录以后才能访问控制器里的所有方法。
7. 新建通知列表视图
再次刷新页面,你会看到视图文件未找到的异常:
接下来新建此模板:
resources/views/notifications/index.blade.php
@extends('layouts.app') @section('title', '我的通知') @section('content') <div class="container"> <div class="col-md-10 offset-md-1"> <div class="card "> <div class="card-body"> <h3 class="text-xs-center"> <i class="far fa-bell" aria-hidden="true"></i> 我的通知 </h3> <hr> @if ($notifications->count()) <div class="list-unstyled notification-list"> @foreach ($notifications as $notification) @include('notifications.types._' . snake_case(class_basename($notification->type))) @endforeach {!! $notifications->render() !!} </div> @else <div class="empty-block">没有消息通知!</div> @endif </div> </div> </div> </div> @stop
通知数据库表的 Type 字段保存的是通知类全称,如 :
App\Notifications\TopicReplied
。snake_case(class_basename($notification->type))
渲染以后会是 ——topic_replied
。class_basename()
方法会取到TopicReplied
,Laravel 的辅助方法snake_case()
会字符串格式化为下划线命名。刷新页面,会提示我们对应类型的模板文件不存在:
创建此文件:
resources/views/notifications/types/_topic_replied.blade.php
<li class="media @if ( ! $loop->last) border-bottom @endif"> <div class="media-left"> <a href="{{ route('users.show', $notification->data['user_id']) }}"> <img class="media-object img-thumbnail mr-3" alt="{{ $notification->data['user_name'] }}" src="{{ $notification->data['user_avatar'] }}" style="width:48px;height:48px;" /> </a> </div> <div class="media-body"> <div class="media-heading mt-0 mb-1 text-secondary"> <a href="{{ route('users.show', $notification->data['user_id']) }}">{{ $notification->data['user_name'] }}</a> 评论了 <a href="{{ $notification->data['topic_link'] }}">{{ $notification->data['topic_title'] }}</a> {{-- 回复删除按钮 --}} <span class="meta float-right" title="{{ $notification->created_at }}"> <i class="far fa-clock"></i> {{ $notification->created_at->diffForHumans() }} </span> </div> <div class="reply-content"> {!! $notification->data['reply_content'] !!} </div> </div> </li>
我们可以通过 $notification->data 拿到在通知类 toDatabase() 里构建的数组。
刷新页面即可看到我们的消息通知列表:
8. 清除未读消息标示
下面我们来开发去除顶部未读消息标示的功能 —— 当用户访问通知列表时,将所有通知状态设定为已读,并清空未读消息数。
接下来在 User 模型中新增 markAsRead() 方法:
app/Models/User.php
<?php . . . class User extends Authenticatable implements MustVerifyEmailContract { . . . public function markAsRead() { $this->notification_count = 0; $this->save(); $this->unreadNotifications->markAsRead(); } }
修改控制器的 index() 方法,新增清空未读提醒的状态:
app/Http/Controllers/NotificationsController.php
<?php . . . class NotificationsController extends Controller { . . . public function index() { // 获取登录用户的所有通知 $notifications = Auth::user()->notifications()->paginate(20); // 标记为已读,未读数量清零 Auth::user()->markAsRead(); return view('notifications.index', compact('notifications')); } }
现在进入消息通知页面,未读消息标示将被清除:
邮件通知
1. 开启 QQ 邮箱的 SMTP 支持
首先我们需要在 QQ 邮箱的账号设置里开启 POP3 和 SMTP 服务。具体请查看 如何打开POP3/SMTP/IMAP功能? 。
只需要开启以下:
复制方框里的『授权码』,授权码将作为我们的密码使用:
2. 邮箱发送配置
Laravel 中邮箱发送的配置存放于 config/mail.php 中。不过 mail.php 中我们所需的配置,都可以通过 .env 来配置。作为最佳实践,我们优先选择通过环境变量来配置:
.env
. . . MAIL_DRIVER=smtp MAIL_HOST=smtp.qq.com MAIL_PORT=465 MAIL_USERNAME=825217374@qq.com MAIL_PASSWORD=anoyvonurhqmcdad MAIL_ENCRYPTION=ssl MAIL_FROM_ADDRESS=825217374@qq.com MAIL_FROM_NAME=LewisCoder . . .
3. 添加邮件通知频道
首先我们需要修改 via() 方法,并新增 mail 通知频道:
app/Notifications/TopicReplied.php
<?php . . . class TopicReplied extends Notification { . . . public function via($notifiable) { // 开启通知的频道 return ['database', 'mail']; } . . . }
因为开启了 mail 频道,我们还需要新增 toMail 方法:
a
pp/Notifications/TopicReplied.php
<?php . . . class TopicReplied extends Notification { . . . public function toMail($notifiable) { $url = $this->reply->topic->link(['#reply' . $this->reply->id]); return (new MailMessage) ->line('你的话题有新回复!') ->action('查看回复', $url); } }
4. 使用队列发送邮件
大家应该会发现我们提交回复时,服务器响应会变得非常缓慢,这是『邮件通知』功能请求了 QQ SMTP 服务器进行邮件发送所产生的延迟。对于处理此类延迟,最好的方式是使用队列系统。
我们可以通过对通知类添加 ShouldQueue 接口和 Queueable trait 把通知加入队列。它们两个在使用 make:notification 命令来生成通知文件时就已经被导入,我们只需添加到通知类接口即可。
修改 TopicReplied.php 文件,将以下这一行:
class TopicReplied extends Notification
改为:
class TopicReplied extends Notification implements ShouldQueue
Laravel 会检测 ShouldQueue 接口并自动将通知的发送放入队列中,所以我们不需要做其他修改。
测试下队列
将 QUEUE_DRIVER 的值改为 redis:.env
QUEUE_CONNECTION=redis
命令行运行队列监控:
$ php artisan horizon
发送邮件测试,可以发现速度大大提高,队列监控也接收到队列任务,并成功处理:
-
一文彻底玩转Android通知栏消息通知
2021-12-09 19:58:20Google为什么要引入消息渠道这个,因为在之前版本的Android通知管理中,通知消息要么全部接收,要么全部屏蔽,然而消息渠道改变了这一特点。 就是每条通知都要属于一个对应的渠道。每个App都可以自由地创建当前App...消息渠道
Google为什么要引入消息渠道这个,因为在之前版本的Android通知管理中,通知消息要么全部接收,要么全部屏蔽,然而消息渠道改变了这一特点。
就是每条通知都要属于一个对应的渠道。每个App都可以自由地创建当前App拥有哪些通知渠道,但是这些通知渠道的控制权都是掌握在用户手上的。用户可以自由地选择这些通知渠道的重要程度,是否响铃、是否振动、或者是否要关闭这个渠道的通知。
以小米手机上的微信为例
API介绍
不带 channelId 已被废弃,慎用!!!
当然,Google也并没有完全做绝,即使方法标为了废弃,但还是可以正常使用的。可是如果你将项目中的targetSdkVersion指定到了26或者更高,那么Android系统就会认为你的App已经做好了8.0系统的适配工作,当然包括了通知栏的适配。这个时候如果还不使用通知渠道的话,那么你的App的通知将完全无法弹出。因此这里给大家的建议就是,一定要适配。
创建通知渠道
package com.wust.notifydemo; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.NotificationCompat; import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.os.Build; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initNotificationChannel(); } /** * 初始化消息渠道 */ private void initNotificationChannel() { // 要确保的是当前手机的系统版本必须是Android 8.0系统或者更高, // 因为低版本的手机系统并没有通知渠道这个功能, // 不做系统版本检查的话会在低版本手机上造成崩溃。 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { // 创建一个通知渠道至少需要渠道ID、渠道名称以及重要等级这三个参数 // 渠道ID可以随便定义,只要保证全局唯一性就可以 String channelId = "chat"; // 渠道名称是给用户看的,需要能够表达清楚这个渠道的用途 String channelName = "聊天消息"; // 重要等级的不同则会决定通知的不同行为,重要等级还可以设置为IMPORTANCE_LOW、IMPORTANCE_MIN,分别对应了更低的通知重要程度。 int importance = NotificationManager.IMPORTANCE_HIGH; createNotificationChannel(channelId, channelName, importance); channelId = "subscribe"; channelName = "订阅消息"; importance = NotificationManager.IMPORTANCE_DEFAULT; createNotificationChannel(channelId, channelName, importance); } } @TargetApi(Build.VERSION_CODES.O) private void createNotificationChannel(String channelId, String channelName, int importance) { // 创建 channel NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); // 获取 notificationManager NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // 注册 channel notificationManager.createNotificationChannel(channel); } }
运行代码,可以在 对应应用的 应用信息 =》 通知管理 =》 通知类别下看到我们创建的两个 channel ,效果如下:
刚才我们创建的两个通知渠道这里已经显示出来了。可以看到,由于这两个通知渠道的重要等级不同,通知的行为也是不同的,聊天消息可以发出提示音并在屏幕上弹出通知,而订阅消息只能发出提示音。
当然,用户还可以点击进去对该通知渠道进行任意的修改,比如降低聊天消息的重要等级,甚至是可以完全关闭该渠道的通知。
至于创建通知渠道的这部分代码,你可以写在MainActivity中,也可以写在Application中,实际上可以写在程序的任何位置,只需要保证在通知弹出之前调用就可以了。并且创建通知渠道的代码只在第一次执行的时候才会创建,以后每次执行创建代码系统会检测到该通知渠道已经存在了,因此不会重复创建,也并不会影响任何效率。
发送通知
触发通知的代码和之前版本基本是没有任何区别的,只是在构建通知对象的时候,需要多传入一个通知渠道ID,表示这条通知是属于哪个渠道的。
- 布局文件
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/btn_send_chat" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送聊天消息" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/btn_send_subscribe" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送订阅消息" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_send_chat" /> </androidx.constraintlayout.widget.ConstraintLayout>
- 发送消息逻辑
btn_send_chat.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //第一步:获取 NotificationManager NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //第二步:创建 Notification notification = new NotificationCompat.Builder(MainActivity.this, "chat") .setContentTitle("收到一条聊天消息") .setContentText("在干嘛呢?") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.drawable.bb) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bb)) .setAutoCancel(true) .build(); //第三步:发送 notificationManager.notify(1, notification); } }); btn_send_subscribe.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //第一步:获取 NotificationManager NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //第二步:创建 Notification notification = new NotificationCompat.Builder(MainActivity.this, "subscribe") .setContentTitle("收到一条订阅消息") .setContentText("你买的彩票中了2个亿?") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.drawable.bb) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bb)) .setAutoCancel(true) .build(); //第三步:发送 notificationManager.notify(2, notification); } });
管理通知渠道
btn_send_chat.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //第一步:获取 NotificationManager NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); //第四步:检测 "chat" 这个通道的消息等级 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { NotificationChannel channel = notificationManager.getNotificationChannel("chat"); if (channel.getImportance() != NotificationManager.IMPORTANCE_HIGH) { Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); intent.putExtra(Settings.EXTRA_CHANNEL_ID, channel.getId()); startActivity(intent); Toast.makeText(MainActivity.this, "请手动将通知打开", Toast.LENGTH_SHORT).show(); } } //第二步:创建 Notification notification = new NotificationCompat.Builder(MainActivity.this, "chat") .setContentTitle("收到一条聊天消息") .setContentText("在干嘛呢?") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.drawable.bb) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bb)) .setAutoCancel(true) .build(); //第三步:发送 notificationManager.notify(1, notification); } });
测试条件:你将 聊天消息 重要程度 手动设置为 低,当你点击发送聊天消息的时候,代码会引导你设置聊天消息等级
显示未读角标
修改两个地方
第一是在创建通知渠道的时候,调用了NotificationChannel的setShowBadge(true)方法,表示允许这个渠道下的通知显示角标。第二是在创建通知的时候,调用了setNumber()方法,并传入未读消息的数量。
-
Android消息通知栏
2018-04-09 17:45:30日常生活中,相信很多人都会有这样的经历,每天手机都会收到一些来自不同来源app的一些消息,显示与状态栏,下拉即可查看,甚至可以点击进行调转到相应app界面, ... 跟Android中的api结合起来,通知消息... -
消息通知系统模型设计
2019-02-21 00:42:29本篇主要明确消息通知系统的概念和具体实现,包括数据库设计、技术方案、逻辑关系分析等。消息通知系统是一个比较复杂的系统,这里主要分析站内消息如何设计和实现。 我们常见的消息推送渠道有以下几种: 设备推送 ... -
uni-app开启消息通知
2021-11-24 16:07:20场景:uni-app开启移动app,如果用户没开启消息通知提示开启 因为uni-app升级到androidx,之前的android.support.v4.app.NotificationManagerCompat 已经找不到了,androidx中采用androidx.core.app.... -
集成钉钉机器人消息通知
2021-07-21 15:37:28集成钉钉机器人消息通知 集成消息发送呢,还是比较常见的,比如说,异常的时候,发送钉钉通知,艾特到某个人,可以点击查看详情;还可以集成调度,在某个时间段,执行特定的操作,比如每天早上8点发送今日热点新闻,... -
java对接钉钉发送消息通知
2020-11-24 10:14:45现在,许多的消息会议,工作任务安排都不在是通过邮件呀,或者是使用口头表达的形式了,都是一些通讯软件进行分发,在工作中,也会有一些东西需要通过程序进行自动发送一些消息通知到指定的人,告诉他,你的,干活的... -
一个websocket消息通知案例
2019-01-11 09:33:09消息通知是服务器主动向客户发送消息,可以使用另一个用户模拟后台下载任务完成给888888用户发送消息,不嫌麻烦可以写定时任务测试 ()">测试 function refMessage(){ $.ajax({ type: "GET",dataType: "STRING... -
Django站内消息通知
2019-07-29 08:51:59站内通知使用django-notifications-hq第三方库。执行如下命令安装django-notifications-hq: pipinstalldjango-notifications-hq 执行命令后,安装3个库。django-model-utils、django-notifications-hq、json... -
音乐播放器消息通知栏切歌栏效果(Notification+BroadcastReceiver)
2014-05-14 19:47:26类似于酷狗音乐播放器通知栏,实现切换歌曲的效果 -
HTML5如何实现网页消息通知提醒
2021-07-22 14:19:50在网页开发过程中,有时需要实现聊天功能,或者在一些网站跟在线客服咨询的时候,会看到一些消息通知的提示,常见的有浏览器标签页的闪烁和屏幕右侧的消息通知。这里总结一下自己的实现方法 实现的效果: 当前窗体失... -
Android 消息通知栏用法详解(一)
2020-04-02 21:36:59通知(Notification) 是Android 系统比较有特色的一个功能,当某个应用程序希望向用户发出一些提示信息的时,而该应用程序又处于后台,就可以借助通知来实现。比如微信弹窗。通过这篇文章,我们将学习到 ... -
前端做消息通知
2019-09-10 14:57:571、消息通知,原生一些用webScoket 2、收费的,可以使用goeasy,http://www.goeasy.io/cn/doc/event/event.html 查看文档,事件与监听器即可 -
「Android基于MQTT实现消息通知」
2022-03-01 18:51:28「Android基于MQTT实现消息通知」 一、写在前面 在对接项目中IoT时,发现目前有对MQTT做了接入,这里记录一下,官方的资料比较详细,这里主要从实现细节出发;对具体的需求以及配套的技术方案进行整理,以供参考。 ... -
安卓接收消息通知——从后台发出到显示消息弹窗详解
2019-07-03 14:53:104.现在只是安卓接收到了,但还没有显示出像QQ那样的消息通知。 接下来,把刚刚接收到消息后,写的textView,改成这个方法。 final Handler incomingMessageHandler = new Handler() { @Override public ... -
Electron中的消息通知
2021-04-25 20:40:29Electron中的消息通知是基于H5的Notification来实现的,比较常见的使用场景是用于监听网络变化然后进行消息提示。 1. 消息提示 var option = { title: '温馨提示', body: '不要天天坐在电脑前,要注意休息!' }... -
PHP消息通知(微信通知,短信通知,邮件通知,订单通知)怎么做?
2019-04-02 11:22:58目前对消息队列并不了解其原理,记录下来为后续学习打下基础。 众所周知在对网站设计的时候,会遇到给用户“群发短信”,“订单系统有大量的日志”,“秒杀设计”等,服务器没法处理这种瞬间迸发的压力,这种情况要... -
安卓APP开启锁屏通知&微信、qq顶部消息通知
2020-07-17 20:39:59APP消息通知遇到了两个问题:1.没有类似于微信的锁屏通知,2.没有类似于微信收到消息之后的顶部通知。在经过两天的摸索之后有了答案,先看下实际的实现效果图吧(部分通知内容因为隐私问题进行了模糊处理): ... -
微信公众号消息通知手把手教程
2020-10-16 16:29:16项目需求:微信公众号消息推送 首先你需要我给你提供以下地址 方便你进行开发 准备阶段: a 模版消息推送开发文档 链接 b 微信公众号测试号地址 链接 c 微信公众平台接口调试工具 链接 开发调试阶段 1 查看a... -
Laravel消息通知系统之数据库
2020-04-11 12:02:56Laravel 自带了一套极具扩展性的消息通知系统,尤其还支持多种通知频道,我们将利用此套系统来向用户发送消息提醒。 通知频道指通知的各种途径,Laravel自带的有如下几种 数据库 邮件 短信(通过 Nexmo) Slack ... -
企业微信实现消息通知功能
2020-07-15 14:44:57一:获取消息通知所需参数:appid,secret,agentid 登录企业微信:https://work.weixin.qq.com/ 企业微信消息通知文档:https://work.weixin.qq.com/api/doc/90000/90135/90236 获取appid,secret,agentid方式可以... -
Notification消息通知(消息合并且显示条数,自定义消息通知内容布局)
2017-09-04 18:19:32Notification消息通知(消息合并且显示条数,自定义消息通知内容布局) -
通过RabbitMQ 进行消息通知
2020-03-20 13:46:52通过RabbitMQ 进行消息通知 /// <summary> /// 通过RabbitMQ 进行消息通知 /// </summary> /// <param name="MQCode">消息队列的名称</param> /// <param name="MQmessage">... -
uniapp消息通知权限 兼容ios和android
2022-02-28 10:52:45uniapp 通知权限 兼容ios和android -
小程序之模版消息通知
2018-08-16 18:19:33手把手教你开发微信小程序之模版消息通知 一、模板消息功能概述 模板推送位置:服务通知 模板消息效果展示图: 进入小程序通知: 二、开发前的配置准备 1、验证token是否有效 <?php /**... -
Android 监听系统中消息通知事件
2018-03-24 14:48:030. 学习文章 参考了下面Blog 完全没有任何多余的代码 ...原来百度卫士的通知栏收纳是类似这样的原理完成的,很不错. 1.演示结果 数据源头 监听到的log 03-24 14:37:04.264 6155-6155/com.lava.noticeobser ... -
网站消息通知设计
2019-06-13 14:54:21一个通知系统主要由:通知来源、通知控制、通知方式、通知模板和通知的目标五个部分组成。 后面详细介绍各个组成的作用。组成结构如下图所示: 通知来源 通知来源是指触发本次通知事件的源头,一般包括以下三种情况...