Ir al contenido principal

Xamarin.Forms - Notificaciones locales

Seguimos aprendiendo y profundizando nuestros conocimientos en desarrollo móvil con Xamarin.Forms, este fin de semana se ha revisando y desarrollo algunos ejercicios prácticos acerca de las notificaciones locales haciendo uso de algunas funcionalidades de reloj del dispositivo móvil y la clase DependencyService que es un localizador de servicios que habilita las aplicaciones desde Xamarin.Forms con el fin de invocar la funcionalidad nativa de la plataforma desde código compartido.

Considerando que las notificaciones locales son alertas enviadas por aplicaciones instaladas en nuestros dispositivo móvil y menudo se usan para funciones como:

👦 Recordatorios.
📅 Eventos del calendario.
📌 Ubicaciones basado en geolocalización

DependencyService
Es el servicio de dependencia que permite acceder a funcionalidades nativas de cada plataforma con el fin de resolver la implementación de una capa interfaz sencilla. En el siguiente diagrama, se muestra cómo se invoca la funcionalidad nativa de la plataforma en una aplicación de Xamarin.Forms:

Información general de la ubicación del servicio mediante la clase DependencyService de Xamarin.Forms
Diagrama referencial de https://docs.microsoft.com
Para poder profundizar el concepto acerca de DependencyService sugiero revisar el siguiente link:


Lo interesante de todo esto es que Xamarin.Forms también nos facilita el uso de Plugins, para realizar este tipo de funcionalidad sin tener que codificar en cada plataforma, los más usados son:

👍 Xam.Plugin.Badge
👍 Plugin.Notifications

Más adelante iremos revisando y creando ejemplos prácticos con ambos plugins, por ahora implementaremos el ejemplo práctico apoyándonos con DependencyService y de este modo ir potenciando nuestra curva de aprendizaje en Xamarin.Forms; empecemos.

Requisitos:
Microsoft Visual Studio Community 2019.
Xamarin.Froms - XAML.
DependencyService.

Para lo cual iniciamos creando nuestro proyecto de nombre LocalNotifications de tipo Aplicación Móvil (Xamarin.Forms), seleccionamos la plantilla En Blanco y marcamos para las Plataformas de Android y iOS.

Luego agregaremos un elemento de tipo interfaz de nombre INotification.cs y una clase con el nombre de Notification.cs en el proyecto principal LocalNotifications. Finalmente agregamos clase AndroidNotification en el proyecto LocalNotifications.Android.

Diseño de Interfaz:
En el archivo XAML de nombre MainPage.xaml agregaremos los siguiente elementos:

Entry x:Name="_entryMessage": Será el encargado de determinar el texto o mensaje a notificar.

TimePicker x:Name="_timeExecute": Encargado de definir la hora para ejecutar la notificación local.

Switch x:Name="_switchActive": Define si se envía o no la notificación de acuerdo a lo ingresado y según la hora definido; es decir en caso este inactivo se ejecuta la notificación de acuerdo a la HORA definido, caso contrario a pesar de estar en la HORA definido y NO este activo, la notificación no se ejecutará.

Los demás elementos son de tipo Label que son etiquetas estáticas como completo de nuestra interfaz de diseño con XAML.

Para este ejemplo práctico todas las pruebas se viene realizando en un equipo móvil físico con Sistema Operativo Android, aclarando que esto no restringe el uso de emuladores y recordar que sólo se viene implementado por ahora los proyectos para la plataforma de Android.

XAML completo del archivo SignUpLogin.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="LocalNotifications.MainPage"
             Title="Notifications"
             Padding="10">
    <StackLayout Margin="0,35,0,0"
                 x:Name="stackLayout">
        <Label Text="Crear notificación"
               TextColor="#424949"
               HorizontalOptions="Center"
               VerticalOptions="Start"
               FontSize="Large"/>
        <Entry x:Name="_entryMessage"
               Placeholder="Ingresa en mensaje o texto a notificar..."
               FontSize="16"/>
        <StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal">
            <Label Text="Determinar Hora"
                   TextColor="#424949"
                   HorizontalOptions="StartAndExpand"
                   FontSize="16"
                   VerticalOptions="Center"/>
            <TimePicker x:Name="_timeExecute"
                    Time="12:00:00"
                    Format="t"
                    PropertyChanged="OnTimePickerPropertyChanged" />
        </StackLayout>
        <StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal">
            <Label Text="Activar Notificación"
                   TextColor="#424949"
                   HorizontalOptions="StartAndExpand"
                   FontSize="16"
                   VerticalOptions="Center"/>
            <Switch x:Name="_switchActive"
                    HorizontalOptions="EndAndExpand"
                    Toggled="OnSwitchToggled" />
        </StackLayout>
    </StackLayout>
</ContentPage>

Implementar el CodeBehind:
En la interfaz INotification.cs ya creada en el proyecto LocalNotifications e ingresamos las siguiente instrucción:

using System;
namespace LocalNotifications
{
    public interface INotification
    {
        /// <summary>
        /// Delegado nulo que servira para retorna el valor de la notificación a nuestro intefaz del XAML MainPage
        /// </summary>
        event EventHandler NotificationReceived;

        /// <summary>
        /// Método para inicializar la creación del canal de notificación en base a la versión del SDK
        /// </summary>
        void Initialize();

        /// <summary>
        /// Método para definir el horario de ejecución a notificar
        /// </summary>
        /// <param name="title"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        int ScheduleNotification(string title, string message);

        /// <summary>
        /// Método para mostrar la notificación en la interfaz creada
        /// </summary>
        /// <param name="title"></param>
        /// <param name="message"></param>
        void ReceiveNotification(string title, string message);
    }
}

Luego creamos los siguiente atributos de tipo publico de nuestra clases o entidad Notification.cs ubicado en el proyecto  LocalNotifications:

using System;
namespace LocalNotifications
{
    public class Notification : EventArgs
    {
        public string Title { get; set; }
        public string Message { get; set; }
    }
}

Para finalizar el desarrollo en las 3 clases creadas, se implementará la interfaz de cada método creado en la clase AndroidNotification del proyecto LocalNotifications.Android:

using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Support.V4.App;
using Xamarin.Forms;
using AndroidApp = Android.App.Application;

[assembly: Dependency(typeof(LocalNotifications.Droid.AndroidNotification))]
namespace LocalNotifications.Droid
{
    public class AndroidNotification : INotification
    {
        const string channelId = "default";
        const string channelName = "Default";
        const string channelDescription = "The default channel for notifications.";
        const int pendingIntentId = 0;

        public const string TitleKey = "title";
        public const string MessageKey = "message";

        bool channelInitialized = false;
        int messageId = -1;
        NotificationManager manager;

        public event EventHandler NotificationReceived;

        public void Initialize()
        {
            CreateNotificationChannel();
        }

        public int ScheduleNotification(string title, string message)
        {
            if (!channelInitialized)
            {
                CreateNotificationChannel();
            }

            messageId++;

            Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
            intent.PutExtra(TitleKey, title);
            intent.PutExtra(MessageKey, message);

            PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId, intent, PendingIntentFlags.OneShot);

            NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
                .SetContentIntent(pendingIntent)
                .SetContentTitle(title)
                .SetContentText(message)
                .SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.xamagonBlue))
                .SetSmallIcon(Resource.Drawable.xamagonBlue)
                .SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);

            Android.App.Notification notification = builder.Build();
            manager.Notify(messageId, notification);

            return messageId;
        }

        public void ReceiveNotification(string title, string message)
        {
            var args = new Notification()
            {
                Title = title,
                Message = message,
            };
            NotificationReceived?.Invoke(null, args);
        }

        void CreateNotificationChannel()
        {
            manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);

            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                var channelNameJava = new Java.Lang.String(channelName);
                var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
                {
                    Description = channelDescription
                };
                manager.CreateNotificationChannel(channel);
            }

            channelInitialized = true;
        }
    }
}

Recordemos además que debemos inicializar la aplicación y el DependencyService desde el CodeBehind del archivo App.xaml.cs del proyecto LocalNotifications:

        public App()
        {
            InitializeComponent();
            DependencyService.Get<INotification>().Initialize();
            MainPage = new MainPage();
        }

Ahora se implementará las siguientes instrucciones en el CodeBehind del archivo MainPage.xaml.cs y de este modo dar funcionalidad a todo lo desarrollado:

El método OnTimerNotification es quien realmente se encarga de realizar la notificación, es decir es la interfaz que se encarga de inicializar la ejecución de la notificación de acuerdo a la hora definido y además es el encargado de secuenciar todo el canal de ejecución en base al método  CreateNotificationChannel, recuerda que para ello debe determina la invocación o implementación de DependencyService al carga el MainPage.xaml.cs.

Los demás métodos o instrucciones cuenta con las explicación en cabecera de cada uno de ellos (sección <summary>).

using System;
using System.ComponentModel;
using Xamarin.Forms;

namespace LocalNotifications
{
    public partial class MainPage : ContentPage
    {
        INotification notification;
        int notifNumber = 0;
        DateTime _triggerTime;

        public MainPage()
        {
            InitializeComponent();

            //Inicializa la referencia a la interfaz para poder ejecutar la notificación
            notification = DependencyService.Get<INotification>();

            //Se encarga de recuperar los datos de la notificación para mostrar en pantalla
            notification.NotificationReceived += (sender, eventArgs) =>
            {
                var evtData = (Notification)eventArgs;
                ShowNotification(evtData.Title, evtData.Message);
            };

            //Se encarga de iniciar la ejecucón de la notificación según el horario definido
            Device.StartTimer(TimeSpan.FromSeconds(1), OnTimerNotification);           
        }

        #region notification
        /// <summary>
        /// Metodo para la ejecucón de la notificación
        /// </summary>
        /// <returns></returns>
        bool OnTimerNotification()
        {
            if (_switchActive.IsToggled && DateTime.Now >= _triggerTime)
            {
                _switchActive.IsToggled = false;//Se desactiva con el fin de poder crear otra notificación                                
                notifNumber++;
                string title = $"Notificación N° {notifNumber}";
                string message = _entryMessage.Text               
notification.ScheduleNotification(title, message); //Interfaz que se encarga de inicializar la ejecución de la notificación de acuerdo a la hora definido y secuenciar todo el canal de ejecución              
            }
            return true;
        }

        /// <summary>
        /// Recuperar los datos de la notificación ejecutada
        /// </summary>
        /// <param name="title"></param>
        /// <param name="message"></param>
        void ShowNotification(string title, string message)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                var msg = new Label()
                {
                    Text = $"Notificación recibida:\nTitle: {title}\nMessage: {message}"
                };
                stackLayout.Children.Add(msg);
            });
        }
        #endregion

        #region timeExecute
        /// <summary>
        /// Método encargado de capturar el tiempo del elemento TimePicker x:Name="_timeExecute"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void OnTimePickerPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == "Time")
            {
                SetTriggerTime();
            }
        }

        /// <summary>
        /// Captura el valor de la propiedad Toggled del elemento Switch x:Name="_switchActive" de acuerdo a la HORA de ejecución DEFINIDO
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void OnSwitchToggled(object sender, ToggledEventArgs args)
        {
            SetTriggerTime();
        }

        /// <summary>
        /// Método que DETERMINA o DEFINE el tiempo de ejecución en base a la PROPIEDAD OnTimePickerPropertyChanged del elemento TimePicker x:Name="_timeExecute"
        /// </summary>
        void SetTriggerTime()
        {
            if (_switchActive.IsToggled)
            {
                _triggerTime = DateTime.Today + _timeExecute.Time;
                if (_triggerTime < DateTime.Now)
                {
                    _triggerTime += TimeSpan.FromDays(1);
                }
            }
        }
        #endregion
    }
}

Resultado:
Comparto el vídeo 📹 acerca de lo desarrollado de acuerdo a la descripción de este post publicado, las pruebas fueron validados en plataforma 📱 para Android.

Descarga la fuente de:
En proceso de carga...
Gratitud a Dios nuevamente😊y gracias a todos ustedes por la acogida de este nuevo post, éxitos y bendiciones 🙏 y un gran abrazo a todos, y cuidarse como siempre ✌...!!!

Comentarios

Entradas más populares de este blog

Habilitar Usuario HR Oracle

Al realizar la primera instalación del Oracle, el usuario HR por defecto está bloqueado y por ende no podemos loguearnos como dicho usuario, lo que debe hacer son los siguiente pasos, aplicables para Linux o Windows.
1. Conectarse como usuario system o sysdba + contraseña haciendo uso del comando connect.
Usuario: system
Password: xxxx 


2. Hacer uso  del comando alter user hr account unlock desbloqueamos la cuenta.
alter user hr account unlock;

3. Escribimos el comando alter user HR identified by hr; con esto estamos diciendo que la contraseña será hr.

alter user HR identified by hr;

4. Ahora testeamos la conexión con el comando - conn hr/hr@xe. Si deseas después de conectarnos se puede realizar un select a la tabla employees del hr.


Resultado del select realizado
5. Con todos estos pasos realizados ya podemos logearnos desde cualquier IDE como el usuario hr  y la contraseña hr que definimos en el paso 3. 
Para finalizar nos loguearemos con el IDE Oracle SQL Developer.

Espero les sea de utilidad,…

Usuario SYS y SYSTEM - ORACLE

Usuario SYS y SYSTEM
Ambos usuario son creados de forma automática al crear la base de datos ORACLE y se otorga el rol de DBA.

SYS (password por defecto: CHANGE_ON_INSTALL).
SYSTEM (password por defecto: MANAGER).

Lo que se recomienda es cambiar el password de ambos usuarios por el tema de seguridad.

SYS:
Todas las tablas y vistas para el diccionario de datos de la base de datos están almacenados en el esquema SYS. Estas tablas y vistas son críticas para el funcionamiento de la base de datos ORACLE. Para mantener la integridad del diccionario de datos, las tablas del esquema SYS son manipulados solo por la base de datos. Nunca se debería modificar algo o crear tablas en el esquema del usuario SYS.

SYSTEM:
El usuario SYSTEM se utiliza para crear tablas y vistas adicionales que muestran información administrativa, tablas internas y vistas utilizado por varias opciones y herramientas de la base de datos ORACLE. No se recomienda utilizar el esquema SYSTEM para almacenar tablas de interés para usu…

Parámetro de entrada y salida – PL/SQL

Parámetro de entrada y salida – PL/SQL:
Los parámetros de entrada y salida no son los parámetros de inicialización de la base de datos ORACLE. Los parámetros de entra y salida son utilizados mayormente en implementaciones de funciones, procedimientos almacenados o bloques de código bajo el lenguaje del PL/SQL, se considera que ambos parámetros (entra y salida) puedan realizar operaciones en el mismo bloque PL/SQL, es decir, si enviamos un parámetro de entrada hará que cumpla cierta operación y retornara los valores de salida de dicha operación procesada de acuerdo al parámetro de ingresado. Es de acuerdo al caso que nos presenta en la implementación.
Algo importante al definir los parámetros, es saber y considerar cuántos tipos de parámetro existe si solo hablamos de entrada y salida, en realidad mi determinación seria 3 tipos:

Parámetros:

IN – entrada
OUT – salida
IN OUT – entrada salida

Parámetro IN – entrada:
El comportamiento común de estés tipos de parámetros es estar siempre pendiente d…