Konuyu Paylaş : facebook gplus twitter

Konuyu Oyla:
  • Derecelendirme: 5/5 - 2 oy
  • 1
  • 2
  • 3
  • 4
  • 5
[GÜNCEL] Firebase GCM Android Bilgi Mesajı (Notification Push Message)
#1
Lightbulb 
Merhaba

Arrow  SON SÖZÜ BAŞTAN SÖYLEYELİM :
Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol Güncelleme yaparsam oradan son halini indirirsiniz. 
Şimdi burada paylaşılan kod sürekli eskiyecek.
Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol

Forum mesajlarında 10 resim sınırı olduğu, konu içeriğine eklenen smiley ifadelerin de bu sayıya dahil olduğunu görünce MS-Word ortamında yazıp HTML çıktı alarak örnek host hesabında bulundurarak buraya yansısını eklemek zorunda kaldım.
  • Makalenin (1) nolu bölümü için Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol
  • Makalenin (2) nolu bölümü için Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol (altta gördüğünüz kısım) ulaşabilirsiniz.
  • Makalenin ilk bölümü Google API Key oluşturmak için hangi aşamalarda neler yapmanız gerektiğini içeriyor.
  • Makalenin ikinci bölümü ise Delphi Yakasında ANDROID proje oluşturma aşamalarını anlatıyor. 
sample_init_gcm1.png
  • Bu komplike bir sistemdir.
  • Bu sistemde Kinvey veya Parse isimli hizmet aboneliklerine gerek duymayacaksınız. 

   PAS Unit'in 
  •   "initialization" kısmında  xGCMHelper := TGCM_Helper.Create;
  •   "finalization"   kısmında  FreeAndNil(xGCMHelper);
yerleşik olduğundan ana projede ayrıca create edilmesine lüzum yoktur. USES'a eklendiğinde doğal olarak kendiliğinden kullanıma hazır haldedir. 


Sadece EVENT bağlantıları yapılır.


PHP Yakasından bahsedelim
  • Şöyle ki, bir mobil cihaza kurulum gerçekleştiğinde, çalışır çalışmaz DeviceID ve DeviceToken bilgisi yine proje ile verdiğim PHP kodları sayesinde WEB üzerinde daha önce belirlediğimiz bir MySQL veritabanına kaydedilir.
  • Mesaj gönderilecek cihaz listesi daha sonra bu sayede elde edilebiliyor. 
  • Dinamizm bu şekilde korunabiliyor.
  • Aynı zamanda bu PHP kod bünyesine WEB üzerinden de mesaj gönderme opsiyonunu da dahil ettim.
  • Kendi sahip olduğum bir host üzerinde barındırdım, siz de kendinizinkine göre yapılandırabilirsiniz. 
  • Linki http://www.armanlab.com/GoogleCloudMes/ şeklindedir
  • Buraya (4) adet PHP dosyası ekledim. 
  1. include.php (serverAPI key, MySQL kullanıcı hesap bilgileri barındırır)
  2. index.php ki action ile (3) farklı fonksiyon içerir
  3. Table Create ile tabloları oluşturma
  4. Table Drop ile tabloları silme
WEB üzerinden mesaj yayınlamak için basit bir GET metod satır ile aşağıdaki şekilde
Kod:
http://www.armanlab.com/GoogleCloudMes/index.php?action=sendmsg&msgText=ARMAN Deneme&msgId=1973

satırı yeterlidir. 
Bu mesaj aynı zamanda MySQL veritabanında saklanır. olası gönderim hataları durumu takip edilebilir niteliktedir.


ctrl.include.php dosyası 
  • web üzerinden uygulama için varsayılan APIKey bilgisi, Veritabanı bilgileri ve Veritabanı Erişim modülü içerir.
PHP Kod:
<?php
/* Firebase Google Server GCM API Key */
  $serverPCK    'com.DelphiCan.Uygulamasi.android';
  $senderId     '508552148797';
  $serverAPIKey 'AAAAdmgR9z0:APA91bEmBy1XPje24KoigMKC_o26IkflWWZYsDhokjA5E5j3ru6z5JO4V-kR12n1anHmnQH8FkKS8Pqvz1M7uf-XjL5aG7G4ktMRUbSCJ2UePtpF3-iNk-ed_LYLd-pFDe1-sI61iLif';
  $sendUrl      'https://fcm.googleapis.com/fcm/send';
 
/* Database host */
  $dbhost "localhost";

/* Database name */
  $dbname "armanDB";

/* Database username and password */
  $dblogin"armanUSER";
  $dbpass "parola12345";

  $failure "MySQL problemi. ".$dbhost." bağlantısı yapılamadı.";

if(!
$connect = @mysql_connect($dbhost$dblogin$dbpass)) {
    die(
$failure);
} else {
    if(!@
mysql_select_db($dbname,$connect)) {
        die(
$failure);
    }
}
// diğer türlü bağlantı sağlanmış demektir.
  $dbtable $dbname;
?>

index.php dosyası 
  • action parametresiyle güdümlenen 3 farklı işlevi gerçekleştiren ana yapıdır 'regdevice','sendmsg','tokenlist'

PHP Kod:
<?php
  
// Veritabanı Bağlantı Bilgileri ve Global Değişkenler
    include("ctrl.include.php");

  // Lazım olabilecek bilgi...
    $domain getenv('REMOTE_ADDR');

  // "action" parametresine göre hangi fonksiyonun çalışacağına karar verilecek.
    
if (isset($_REQUEST['action'])) {
    switch ($_REQUEST['action']) {
      case 'regdevice':
        RegisterDevice($_REQUEST['did'], $_REQUEST['token'], $_REQUEST['platform'] );
        break;
      case 'sendmsg':
        SendPush($_REQUEST['msgText'], $_REQUEST['msgId'], $serverAPIKey$sendUrl);
        break;
      case 'tokenlist':
        DeviceList();
        break;
      case 'arman':
        SendPushARMAN$serverAPIKey$sendUrl );
        break;
      default:
        DeviceList();
        break;
    }
  }
  else
    {
    
  DeviceList(); //echo '"action" parametreniz uygun değil.<br>Herhangi bir işlem yapılmadı !';
    
}

//  Mob.Cihaz TokenID'yi db'ye yaz, varsa tarih güncelle
// -----------------------------------------------------
    function RegisterDevice($deviceId$deviceToken$platform)
    {
      $sorgu  ""
                ."INSERT INTO devices"
                ." (deviceID, deviceToken, devicePlatform)"
                ." VALUES"
                ." ( '$deviceId', '$deviceToken', '$platform' )"
                ." ON DUPLICATE KEY UPDATE deviceLastDate=now()"
                .";";
       echo $sorgu."<br>";
      if ( @mysql_query$sorgu ) )
        { echo "ok"; }
        else
        { echo "error_RegDevice"; }
    }

//  Notification Gönderim Operasyonu
// -----------------------------------------------------
    function SendPush($msgText$msgId$APIKey$Url)
    {
      $headers      = array ( 'Authorization: key='.$APIKey,
                              'Content-Type : application/json'
                            );

      $notification = array (
      'body'      => $msgText,
      'title'     => $msgId,
      'icon'      => 'http://www.armanlab.com/GoogleCloudMes/mesaj.png',
      'color'     => '#f45342'
      );

      $data  = array (
      'id'         => $msgId,
      'message'    => $msgText,
      'site_adi'   => 'armanlab.com',
      'link'       => 'http://www.armanlab.com'
      );

      $fields = array (
      'priority'         => 'normal',
      'registration_ids' => getRegistrationIds(),
      'collapse_key'     => 'Bilgi_Mesaj',
      'notification'     => $notification,
      'data'             => $data
      
);

      // Bağlantıyı Hazırladık
      $ch curl_init();

      // Hazırlanan bağlantıya url, POST variables ile POST data'yı set ettik
      curl_setopt($chCURLOPT_URL,            $Url);
      curl_setopt($chCURLOPT_POST,           true);
      curl_setopt($chCURLOPT_HTTPHEADER,     $headers);
      curl_setopt($chCURLOPT_RETURNTRANSFERtrue);

      // Geçici olarak SSL sertifika desteğini kapattık çünkü burada gerek yok
      curl_setopt($chCURLOPT_SSL_VERIFYPEERfalse);
      curl_setopt($chCURLOPT_POSTFIELDS,     json_encode($fields));

      // post işlemini yaptık
      $result curl_exec($ch);

      if ($result === FALSE) {
          die('Curl failed: ' curl_error($ch));
      } else {
          print_r"<br>".$result."<br>" );
      }
    // Bağlantımızı kapattık
    curl_close($ch);

    // Gönderdiğimiz Mesajı LOG'layalım
      $sorgu  ""
                ."INSERT INTO messages"
                ." (MessageText, MessResult)"
                ." VALUES"
                ." ( '$msgText', '$result' )"
                .";";
        // echo $sorgu."<br>";
        if ( @mysql_query$sorgu ) )
          { echo "ok"; }
          else
          { echo "error_MesLog"; }
      }

//  DeviceID'ye göre gruplandırılmış en son TokenID'ler
// -----------------------------------------------------
    function getRegistrationIds()
    {
      $sorgu  ""
                ."SELECT id, deviceID, deviceToken"
                ." FROM devices"
                ." WHERE id IN ("
                ."    SELECT MAX(id)"
                ."    FROM devices"
                ."    GROUP BY deviceID"
                ." );";
      $sonuc        =  mysql_query$sorgu );
      $result_array = array();
      while($row mysql_fetch_assoc($sonuc ))
      {
        $result_array[] = $row['deviceToken'];
      }
      return $result_array;
    }

    function DeviceList()
    {
      $sorgu  ""
         ."SELECT id, deviceID, deviceToken, devicePlatform, deviceSignDate, deviceLastDate"
         ." FROM devices"
         ." WHERE id IN ("
         ."    SELECT MAX(id)"
         ."    FROM devices"
         ."    GROUP BY deviceID"
         ." );";
      //$sorgu  = "SELECT deviceID, deviceToken FROM devices;";
      //echo $sorgu."<br>";
      $sonuc  =  mysql_query$sorgu );
      $xsql   'select';

      if (!$sonuc)
           $sayi = -1; }
      else { $sayi mysql_num_rows($sonuc); }
      //echo "SQL Query : ".$sorgu."<br>";
      //echo "Rec Count : ".$sayi."<br>";

      if ( $sayi )
      {
        $Result "<?xml version='1.0' encoding='utf-8'?>\n<arman>\n";
        while($data mysql_fetch_assoc($sonuc)) {
          $Result .= " <rec>\n";
          foreach($data as $key => $value) {
            $Result .=  "  <$key>$value</$key>\n"."<br>";
          }
            $Result .= " </rec>\n"."<br>";
        }
        $Result .= "</arman>\n";
        echo $Result;
      }
    }
?>


ctrl.create.php dosyası 
  • include.php ile yanımlanmış veritabanına otomatik olarak TABLO'ları CREATE eden modüldür. 
  • 1 defa çalıştırmak kurulum işlemi için kafidir.
PHP Kod:
<?php
  
include("ctrl.include.php");
 $veri[1] = "
CREATE TABLE devices (
  id int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Sıra No'
 ,deviceID varchar(255) NOT NULL COMMENT 'CihazID'
 ,deviceToken varchar(255) NOT NULL COMMENT 'CihazToken'
 ,devicePlatform varchar(255) NOT NULL COMMENT 'CihazPlatform'
 ,deviceSignDate TIMESTAMP NOT NULL DEFAULT now() COMMENT 'Kullanıcı hesabı ilk kayıt tarihi'
 ,deviceLastDate TIMESTAMP NOT NULL DEFAULT 0     COMMENT 'Kullanıcı hesabı son giriş tarihi'
 ,PRIMARY KEY (id)
 ,UNIQUE (deviceToken) COMMENT 'UPDATE aşamasında aynı tokena LastDate yazılacak.'
)
ENGINE = MYISAM
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
COMMENT = 'Mesaj PUSH edilecek cihazların tablosudur.';
"
;

 $veri[2] = "CREATE TABLE messages (
  ID int(10) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT COMMENT 'Sıra No'
  ,DateTime TIMESTAMP NOT NULL DEFAULT Now() COMMENT 'Mesaj Tarihi'
  ,MessageText text DEFAULT NULL COMMENT 'Mesaj İçeriği'
  ,MessResult  text DEFAULT NULL COMMENT 'GCM Icerik Result'
  ,PRIMARY KEY (ID)
)
ENGINE = MYISAM
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
COMMENT = 'Push edilmiş mesajlar tablosudur.';
"
;

 if ( @mysql_query$veri[1] ) )
 { echo "Tablomuz 'devices' Başarılı bir şekilde create edildi <br>"; }
 else
 
{ echo "HATA: Tablo create edilemedi...<br>"; }

 if ( @mysql_query$veri[2] ) )
 { echo "Tablomuz 'messages' Başarılı bir şekilde create edildi <br>"; }
 else
 
{ echo "HATA: Tablo create edilemedi...<br>"; }
?>

  ctrl.drop.php dosyası 

  • include.php ile yanımlanmış veritabanındaki TABLO'ları SİLMEYE YARAYAN modüldür. 
  • çalıştırılırsa TABLOLAR SİLİNİR.
PHP Kod:
<?php
  
include("ctrl.include.php");
 $veri[1] = "DROP TABLE devices";
 $veri[2] = "DROP TABLE messages";
 if ( @mysql_query$veri[1] ) )
 { echo "Tablomuz 'devices' Silindi...<br>"; }
 else
 
{ echo "HATA: Tablo silme işlemi başarısız oldu...<br>"; }

 if ( @mysql_query$veri[2] ) )
 { echo "Tablomuz 'messages' Silindi...<br>"; }
 else
 
{ echo "HATA: Tablo silme işlemi başarısız oldu...<br>"; }
?>





DELPHI (Andrdoid) Yakasından bahsedelim
  • Proje paketinde birbiriyle ilişkili (3) proje ve bu projelerin ortak paydası (1) Helper Class Unit bulacaksınız.
  • (1) PHP ile MySQL hesabı üzerindeki veritabanınıza devices ve messages isimli iki tablo açacak PHP web desteği. Web üzerinden de mesaj yollayabiliyorsunuz.
  • (2) Helper.PAS dosyamız ki hem android hem de windows yakasına uygun fonksiyonlar barındırıyor.
  • (3) Helper PAS ile Android Push Mesaj dinleyen modül
  • (4) Helper PAS ile Windows Win32 uygulaması olarak Push Mesaj atan modül.


  • Ana Omurga GoogleCloudMessaging_Helper.PAS dosyamız 
Kod:
{***************************************************************************}
{                                                                           }
{                   Google Cloud Messaging (GCM)  Class                     }
{                                                                           }
{                    Copyright (C) 2016 Muharrem ARMAN                      }
{                         muharrem.arman@trt.net.tr                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Google Cloud Messaging (GCM) Servisinden Yardım alarak                   }
{  Mobil cihazlara PUSH mesaj yollamak amacıyla tasarlanmıştır.             }
{                                                                           }
{  php ile veritabanı modülü de mevcuttur. Bu veritabanından mobil cihaz    }
{  listesi alınarak TokenID'lerine mesaj yollamak için kullanılmaktadır.    }
{                                                                           }
{  USES listesine bu UNIT'in eklenmesi yeterlidir.                          }
{  Ekstra bir tanımlamaya gerek yoktur.                                     }
{  Sistem kendiliğinden NOTIFICATION takibine girecektir                    }
{                                                                           }
{  Kullanımı :                                                              }
{ (1) Formunuzun Private kısmına aşağıdaki tanımı ekleyiniz...              }
{                                                                           }
{   procedure OnNewNotification(Sender: TObject; const                      }
{     strSent_time,                                                         }
{     strId,                                                                }
{     strFrom,                                                              }
{     strMessage_id,                                                        }
{     strMessage,                                                           }
{     strCollapse_key : String );                                           }
{                                                                           }
{ (2) Formun OnCreate olayına da aşağıdaki size özel tanımları yapınız.     }
{  // ANDROID CLIENT //                                                     }
{  xGCMHelper.GCM_ID          := '1070957438881';                           }
{  xGCMHelper.API_Key         := 'AAAA-VoJp6E:APA91bG13P.....RpzcVocv6Ebc'; }
{  xGCMHelper.NotifyTitle     := 'ARMAN Bilgi Merkezi';                     }
{  xGCMHelper.PHPRegisterHost := 'http://www.armanlab.com/GoogleCloudMes/'; }
{                                                                           }
{  xGCMHelper.OnServiceConnectionChange := OnServiceConnectionChange;       }
{  xGCMHelper.OnReceiveNotification     := OnReceiveNotification;           }
{  xGCMHelper.LogMemo                   := Memo1;                           }
{  xGCMHelper.Active                    := True;                            }
{                                                                           }
{  // MSWINDOWS SENDER //                                                   }
{  xGCMHelper.API_Key         := 'AAAAX8..................bFN77bz';         }
{  xGCMHelper.NotifyTitle     := 'ARMAN Bilgi Merkezi';                     }
{  xGCMHelper.PHPRegisterHost := 'http://www.armanlab.com/GoogleCloudMes/'; }
{  xGCMHelper.ProjectName     := 'arman-firebase';                          }
{  xGCMHelper.LogMemo         :=  Memo3;                                    }
{                                                                           }
{ (3) DeviceToken alındı ve/veya Notification geldi eventleri aşağıdadır.   }
{ procedure TForm1.OnServiceConnectionChange ( Sender: TObject;             }
{                                              strDeviceId,                 }
{                                              strDeviceToken  : String );  }
{ begin                                                                     }
{   Memo1.Lines.Add( 'Ana Form OnServiceConnectionChange event fired:' );   }
{     Memo1.Lines.Add( '  DeviceId   : ' + strDeviceId       );             }
{     Memo1.Lines.Add( '  DeviceToken: ' + strDeviceToken    );             }
{ end;                                                                      }
{                                                                           }
{ procedure TForm1.OnReceiveNotification     ( Sender: TObject; const       }
{                                              strSent_time,                }
{                                              strId,                       }
{                                              strFrom,                     }
{                                              strMessage_id,               }
{                                              strMessage,                  }
{                                              strCollapse_key : String );  }
{ begin                                                                     }
{   Memo1.Lines.Add( 'Ana Form OnReceiveNotification event fired:'   );     }
{     Memo1.Lines.Add( '  SentTime: '     + strSent_time    );              }
{     Memo1.Lines.Add( '  Id: '           + strId           );              }
{     Memo1.Lines.Add( '  From: '         + strFrom         );              }
{     Memo1.Lines.Add( '  MessageId: '    + strMessage_id   );              }
{     Memo1.Lines.Add( '  Message: '      + strMessage      );              }
{     Memo1.Lines.Add( '  Collapse_key: ' + strCollapse_key );              }
{ end;                                                                      }
{                                                                           }
{ (4) Mesaj Sender                                                          }
{ strResult := xGCMHelper.PushMessage( 'Mesajımız', 'MesajID', False );     }
{                                                                           }
{***************************************************************************}
{  Üzerinde değişiklik yapmak serbesttir ancak lütfen bu etiket bloğu       }
{  içine yaptığınız değişikliği ve künyenizi yazmayı ihmal etmeyiniz.       }
{***************************************************************************}
{  Değişikliği Yapan,  Yapılan Ekleme/Değişiklik bilgisi :                  }
{                                                                           }
{                                                                           }
{                                                                           }
{***************************************************************************}

// Başlangıçta Dikkat Edilecek Hususlar :
// --------------------------------------
// (1)   Manifest Dosyasındaki  <%receivers%> başlığının hemen altına
//       Aşağıdaki TAG bloğunu ekleyiniz.
//       (Bu sayede programımız çalışmıyor olsa dahi Push uyarı alınır.)
// <service android:name="com.embarcadero.gcm.notifications.GCMIntentService" />

//
// (2) "Project" / "Options" / "Entitlement List" kısmına gelerek
//     "Receive push notifications" başlığını TRUE yapınız.
//
// (3) "Project" / "Options" / "Uses Permissions" kısmına gelerek
//     "Internet" başlığını TRUE yapınız.

//{$DEFINE SSL_DLLs_inResourceMode}
unit GoogleCloudMessaging_Helper;

interface

Uses
 {$IF defined(MSWINDOWS)}
   Windows, Forms, Graphics, Controls, GifImg, Dialogs, IdAntiFreeze,
   ShellApi, StdCtrls,
   IdUri, IdSSLOpenSSL, IdSSLOpenSSLHeaders,
 {$ELSEIF defined(ANDROID)}
   System.Notification,
   System.PushNotification,
   FMX.PushNotification.Android,
   System.Threading, FMX.Memo,
   Androidapi.JNI.JavaTypes,
   Androidapi.JNI.GraphicsContentViewText,
   Androidapi.Helpers,
   Androidapi.JNI.Embarcadero,
 {$ENDIF}
   IdHttp, Classes, SysUtils, DateUtils;

{$REGION 'Type Tanımlar Bölümü'}

 Type
   pCihaz = ^tCihaz;
   tCihaz = Record
     CihazId,
     CihazToken,
     CihazPlatform,
     CihazTarih : String;
   End;
   pCihazBilgileri_Genel = Array of pCihaz;

   pIcerik = ^tIcerik;
   tIcerik = Record
     strSent_time,
     strId,
     strFrom,
     strMessage_id,
     strMessage,
     strCollapse_key : String;
   End;
   pMesajGrubu = Array of pIcerik;

{$ENDREGION 'Type Tanımlar Bölümü'}

{$REGION 'EVENT Tanımlar Bölümü'}
Type
 TOnEvent01 = procedure( Sender: TObject; const
                         strSent_time,
                         strId,
                         strFrom,
                         strMessage_id,
                         strMessage,
                         strCollapse_key : String ) of object;

 TOnEvent02 = procedure( Sender: TObject;
                         strDeviceId,
                         strDeviceToken  : String ) of object;

 TOnEvent03 = procedure( Sender: TObject;
                         AError: String ) of object;
{$ENDREGION 'EVENT Tanımlar Bölümü'}

Type
 TGCM_Helper = Class(TObject)
 private
 Const
   GCM_OpenUrl   =  'http://fcm.googleapis.com/fcm/send';
   GCM_SecureUrl = 'https://fcm.googleapis.com/fcm/send';
 Var
   FNotifyTitle          : String;
   FApiKey,
   FPhpPush              : String;

{$REGION 'ANDROID Bölümü'}
 {$IF defined(ANDROID)}
 private
 Var
   FGCM_ID               : String;
   FMemo                 : FMX.Memo.TMemo;
   FOnReceiveNotification: TOnEvent01;
   FOnServConnChange     : TOnEvent02;
   FOnRegisError         : TOnEvent03;

   FPushServ             : TPushService;
   FPushServConn         : TPushServiceConnection;
   FDeviceID,
   FDeviceToken          : String;
   FMesaj                : pIcerik;
   procedure  FOnReceiveNotificationEvent( Sender: TObject;
                                     const ANotification: TPushServiceNotification );
   procedure  FOnPushServConnChangeEvent ( Sender: TObject;
                                           PushChanges: TPushService.TChanges );
   procedure  FSetGCMappId ( strGCM_ID: String );
   procedure  FMesajParse  ( ANotification: TPushServiceNotification );
   procedure  FSetActive   ( aDurum:Boolean    );
   function   FGetActive : boolean;
   procedure  FPushServActivate;
   procedure  FPushServPassive;

   procedure  FPushServiceInit;
   procedure  FPushServiceFree;
 {$ENDIF}
{$ENDREGION 'MSWINDOWS Bölümü'}

{$REGION 'MSWINDOWS Bölümü'}
 {$IF defined(MSWINDOWS)}
 Private
 Var
   FMemo                 : StdCtrls.TMemo;
 {$ENDIF}
{$ENDREGION 'MSWINDOWS Bölümü'}
 private
   function    AradanSec( var strIcerik: String; strBas, strSon: String; boolTrim:boolean=false ): string;
   procedure   LogLa( strIcerik: String );
 public
   constructor Create;
   destructor  Destroy; Override;
   property    API_Key         : string read FApiKey      write FApiKey;
   property    NotifyTitle     : string read FNotifyTitle write FNotifyTitle;
   property    PHPRegisterHost : String read FPhpPush     write FPhpPush;
   function    PushMessage( strMesaj, strMesajID: String; boolSecure:Boolean = false ): String;

{$REGION 'MSWINDOWS Bölümü'}
 {$IF defined(MSWINDOWS)}
 Public
   Property    LogMemo         : StdCtrls.TMemo  read FMemo        write FMemo;
 {$ENDIF}
{$ENDREGION 'MSWINDOWS Bölümü'}

{$REGION 'ANDROID Bölümü'}
 {$IF defined(ANDROID)}
 Private
 Public
   property    GCM_ID      : string write FSetGCMappId; //read FGCM_ID Write FGCM_ID;
   Property    LogMemo         : FMX.TMemo  read FMemo        write FMemo;
   property    OnReceiveNotification     : TOnEvent01 read FOnReceiveNotification write FOnReceiveNotification;
   property    OnServiceConnectionChange : TOnEvent02 read FOnServConnChange      write FOnServConnChange;
   property    OnRegistrationError       : TOnEvent03 read FOnRegisError          write FOnRegisError;
   function    PHPRegisterDevice( DeviceID : string; DeviceToken : string ): boolean;

   property    GelenMesaj  : pIcerik read FMesaj;
   property    Active      : boolean read FGetActive write FSetActive;
   property    DeviceID    : String  read FDeviceID;
   property    DeviceToken : String  read FDeviceToken;
   procedure   LocalAndroidNotification( aName, aMessageText: string; aNotificationNumber: integer; aSaniyeSonra:Integer = -1);
   function    StartUpNotifications: String;
 {$ENDIF}
{$ENDREGION 'ANDROID Bölümü'}

 end;

Var
 xGCMHelper : TGCM_Helper;

implementation

{$IF defined(MSWINDOWS)}
 {$R RES\RES.RES} // SSL DLL'leri Resource olarak saklanacak...
{$ENDIF}

constructor TGCM_Helper.Create;
begin
 Inherited;  // Create'de  daima başta call edeceğiz...
{$IF defined(ANDROID)}
 FPushServiceInit;
{$ENDIF}
 //...
end;

destructor  TGCM_Helper.Destroy;
begin
 //...
{$IF defined(ANDROID)}
 FPushServiceFree;
{$ENDIF}
 Inherited;  // Destroy'da daima sonda call edeceğiz...
end;

{$REGION 'ANDROID Bölümü'}

{$IF defined(ANDROID)}
function   TGCM_Helper.FGetActive : boolean;
begin
 Result := FPushServConn.Active;
end;

procedure TGCM_Helper.FPushServActivate;
begin
 System.Threading.TTask.Run(procedure
 begin
   FPushServConn.Active := True;
 end);
end;

procedure TGCM_Helper.FPushServPassive;
begin
 System.Threading.TTask.Run(procedure
 begin
   FPushServConn.Active := False;
   TThread.Synchronize( nil, procedure
   begin
     FDeviceID    := '';
     FDeviceToken := '';
   end                );
 end);
end;

procedure  TGCM_Helper.FSetActive( aDurum:Boolean );
begin
 if (FPushServConn <> nil) and (aDurum = FPushServConn.Active)
   then Exit; // Aynı State olduğundan işleme gerek yok...

 Case aDurum of
 true  : begin
         //FPushServConn.Active := True; // Firebase'de başka bir Thread'de olmalı...
           FPushServActivate;
         end;
 false : begin
           FPushServPassive;
         end;
 end;
end;

procedure TGCM_Helper.FPushServiceInit;
begin
 if FNotifyTitle = '' then FNotifyTitle := 'ARMAN Bilgi Merkezi';

 FPushServ           := TPushServiceManager.Instance.GetServiceByName( TPushService.TServiceNames.GCM );
 FPushServConn       := TPushServiceConnection.Create( FPushServ );
 FPushServConn.OnChange              := FOnPushServConnChangeEvent;
 FPushServConn.OnReceiveNotification := FOnReceiveNotificationEvent;
end;

procedure TGCM_Helper.FPushServiceFree;
begin
 FPushServConn.Free;
 FPushServ.Free;
end;

procedure TGCM_Helper.FMesajParse( ANotification: TPushServiceNotification );
begin
 if Assigned( FMesaj ) then Dispose( FMesaj );
 New( FMesaj );
 FMesaj.strSent_time    := ANotification.Json.GetValue('google.sent_time').Value;
 FMesaj.strId           := ANotification.Json.GetValue('id').Value;
 FMesaj.strFrom         := ANotification.Json.GetValue('from').Value;
 FMesaj.strMessage_id   := ANotification.Json.GetValue('google.message_id').Value;
 FMesaj.strMessage      := ANotification.Json.GetValue('message').Value;
 FMesaj.strCollapse_key := ANotification.Json.GetValue('collapse_key').Value;
end;

procedure TGCM_Helper.FOnPushServConnChangeEvent (Sender: TObject; PushChanges: TPushService.TChanges);
begin
 if TPushService.TChange.DeviceToken in PushChanges
   then FDeviceToken := FPushServ.DeviceTokenValue[TPushService.TDeviceTokenNames.DeviceToken];

 if FPushServConn.Active then
 begin
   FDeviceID    := FPushServ.DeviceIDValue[ TPushService.TDeviceIDNames.DeviceID ];
LogLa( 'DeviceID    : ' + FDeviceID );
LogLa( 'DeviceToken : ' + FDeviceTOKEN );
 end;

 if    (FDeviceID    <> '')
   AND (FDeviceToken <> '')
   AND (FPhpPush     <> '')
 then begin
   if PHPRegisterDevice( FDeviceID, FDeviceToken )
     then LogLa( 'PHP Registered :) with DeviceToken : ' + FDeviceToken )
     else LogLa( 'PHP Register kısmında bir sorun oldu...' );
 end else begin
LogLa( 'DeviceID, DeviceToken veya PHPPush URL boş geldi... Device kaydedilemedi... :(' );
 end;

 if Assigned(OnServiceConnectionChange) then // tests if the event is assigned
 begin
   TThread.Synchronize( nil, procedure
   begin
     FOnServConnChange( Self, FDeviceID, FDeviceToken );
   end                );
 end;
end;

procedure TGCM_Helper.FSetGCMappId(strGCM_ID: string);
begin
 FGCM_ID := strGCM_ID;
 FPushServ.AppProps[ TPushService.TAppPropNames.GCMAppID ] := FGCM_ID;  // GCM App ID
end;

procedure TGCM_Helper.FOnReceiveNotificationEvent(Sender: TObject;
 const ANotification: TPushServiceNotification);
begin
 if Assigned( OnReceiveNotification ) then // tests if the event is assigned
 begin
   FMesajParse( ANotification );

// Pencere açıkken Push bildirim alınıyor ancak System Tray'e gelmiyor...
//   Biz kendimiz manuel eklesek mi ? .. dedim :)

// Aşağıdaki (//) Remark kaldırırsanız gelen notification'u Local'den
// kendimiz yayınlayarak sanki Push mesaj gelmiş gibi bir illüzyon yaratır.

// LocalAndroidNotification( FMesaj.strMessage, 0 );

   TThread.Synchronize( nil, procedure
   begin
     FOnReceiveNotification( Sender, FMesaj.strSent_time, FMesaj.strId, FMesaj.strFrom, FMesaj.strMessage_id, FMesaj.strMessage, FMesaj.strCollapse_key ); // calls the event.
   end                );
 end;
end;

procedure TGCM_Helper.LocalAndroidNotification( aName, aMessageText: string; aNotificationNumber: integer; aSaniyeSonra:Integer = -1);
var
 NotificationCenter  : TNotificationCenter;
 Notification        : TNotification;
begin
 // USES'da FMX.PushNotification.Android olmazsa çakılıyor...
 NotificationCenter  := TNotificationCenter.Create(nil);
 try
   Notification      := NotificationCenter.CreateNotification;
   try
     Notification.Name         := aName;
     Notification.AlertBody    := aMessageText;
     Notification.Title        := aMessageText;
     Notification.EnableSound  := True;
     Notification.Number       := aNotificationNumber;
     NotificationCenter.ApplicationIconBadgeNumber := aNotificationNumber;

     if aSaniyeSonra < 0 then begin
       // Hemen Yayınlansın
       NotificationCenter.PresentNotification( Notification );
     end else
     begin
       // (n) SaniyeSonra kadar saniye bekledikten sonra yayına çıksın
       Notification.FireDate   := Now + EncodeTime(0, 0, aSaniyeSonra, 0);
       NotificationCenter.ScheduleNotification( Notification );
     end;
   finally
     Notification.DisposeOf;
   end;
 finally
   NotificationCenter.Free;
   NotificationCenter.DisposeOf;
 end;
end;

function TGCM_Helper.StartUpNotifications: String;
var
 LNotification : TPushServiceNotification;
 MessageText   : String;
begin
 for LNotification in FPushServConn.Service.StartupNotifications do
 begin
   if Assigned(LNotification) and (LNotification.Json.ToString <> '') then
   begin
     Result := 'Notification Tıklanınca Geldik : '
               + #13 + '  SentTime: '     + LNotification.Json.GetValue('google.sent_time').Value
               + #13 + '  Id: '           + LNotification.Json.GetValue('id').Value
               + #13 + '  From: '         + LNotification.Json.GetValue('from').Value
               + #13 + '  MessageId: '    + LNotification.Json.GetValue('google.message_id').Value
               + #13 + '  Message: '      + LNotification.Json.GetValue('message').Value
               + #13 + '  Collapse_key: ' + LNotification.Json.GetValue('collapse_key').Value;
//      Result := 'Notification Tıklanınca Geldik : '
//                + #13 + '  Message: '      + LNotification.Json.GetValue('message').Value
//                + #13 + '  Id: '           + LNotification.Json.GetValue('id').Value;
     //MessageText := LNotification.Json.ToString;
     //LogLa( DateTimeToStr(Now) + ' Message = ' + MessageText );
   end;
 end;

 // System Tray'de biriken mesajların hepsini okuyabilmeyi istedim ama olmadı...
 // Temizlik Vakti.
 With TNotificationCenter.Create(nil)
 do
   try
     CancelAll;
   finally
     Free;
     DisposeOf;
   end;
end;

function TGCM_Helper.PHPRegisterDevice(DeviceID : string; DeviceToken : string): boolean;
var
 slPostData: TStringList;
 IdHttp    : TIdHTTP;
begin
 Result := False;
 if FPhpPush <> '' then
 begin
   FPhpPush := Trim(FPhpPush);
   if FPhpPush[length(FPhpPush)-1] <> '/' // ZeroBased
        then FPhpPush := FPhpPush + '/';

   IdHttp     := TIdHTTP.Create;
   slPostData := TStringList.Create;
   try
     slPostData.Add('action=regdevice');
     slPostData.Add('did='   + DeviceID     );
     slPostData.Add('token=' + DeviceToken  );
     {$ifdef ANDROID}
       slPostData.Add('platform=android');
     {$else}
       slPostData.Add('platform=ios');
     {$endif}
     IdHttp.Post( FPHPPush + 'index.php', slPostData );
     Result := True;
   finally
     slPostData.Free;
     IdHttp.Disconnect;
     IdHttp.DisposeOf;
   end;
 end;
end;

{$ENDIF}
{$ENDREGION 'ANDROID Bölümü'}

{$REGION 'WINDOWS Bölümü'}

{$IF defined(MSWINDOWS)}
function SetDllDirectory(lpPathName:PWideChar): Bool; stdcall; external 'kernel32.dll' name 'SetDllDirectoryW';
{$ENDIF}

{$ENDREGION 'Windows Bölümü'}

{$REGION 'ORTAK Fonksiyonlar'}

function TGCM_Helper.AradanSec(var strIcerik: String; strBas, strSon: String; boolTrim:boolean=false ): string;
Var
 strOrjKaynak : String;
begin
 Result := '';
 strOrjKaynak := strIcerik;
 if Pos( strBas, strIcerik ) > 0 then
 begin
   System.Delete( strIcerik, 1, Pos( strBas, strIcerik )+ Length( strBas )-1 );
   if strSon <> ''
     then Result := Trim( Copy(strIcerik, 1, Pos( strSon, StrIcerik ) -1) )
     else Result := Trim( strIcerik );
 end;
 if NOT boolTrim then strIcerik := strOrjKaynak;
end;

procedure TGCM_Helper.LogLa( strIcerik: String );
begin
 if FMemo <> nil then
 begin
   Classes.TThread.Synchronize(nil,
     procedure
     begin
       FMemo.Lines.Add( strIcerik );
     end
   );
 end;
end;

function TGCM_Helper.PushMessage( strMesaj, strMesajID: String; boolSecure:Boolean = false ): String;
{$IF defined(MSWINDOWS)}
 function GetTempDir: string;
 var
   TempDir:     DWORD;
 begin
   SetLength(Result, MAX_PATH);
   TempDir := GetTempPath(MAX_PATH, PChar(Result));
   SetLength(Result, TempDir);
 end;

 function IsFileInUse(fName: string) : boolean;
 var
   HFileRes: HFILE;
 begin
   Result := False;
   if not FileExists(fName) then begin
     Exit;
   end;

   HFileRes := CreateFile(PChar(fName)
     ,GENERIC_READ or GENERIC_WRITE
     ,0
     ,nil
     ,OPEN_EXISTING
     ,FILE_ATTRIBUTE_NORMAL
     ,0);

   Result := (HFileRes = INVALID_HANDLE_VALUE);

   if not(Result) then begin
     CloseHandle(HFileRes);
   end;
 end;
{$ENDIF}
var
 idHTTP       : TIDHTTP;
 strJson      : String;
 JsonStream   : Classes.TStringStream;
 strGelen     : String;
 DeviceList   : TStringList;
{$IF defined(MSWINDOWS)}
 SslIOHandler : TIdSSLIOHandlerSocketOpenSSL;
{$ENDIF}
{$IFDEF SSL_DLLs_inResourceMode}
 a,b : Integer;
{$ENDIF}
begin
 if FApiKey <> '' then
 begin
   FPhpPush := Trim(FPhpPush);
   if FPhpPush[length(FPhpPush)-1] <> '/' // ZeroBased
          then FPhpPush := FPhpPush + '/';

   DeviceList := TStringList.Create;
   IdHTTP := TIdHTTP.Create(nil);
   try
     IdHTTP.Name                := 'IdHTTP';
     IdHTTP.AllowCookies        :=  True;
     IdHTTP.HandleRedirects     :=  True;
     IdHTTP.HTTPOptions         := [hoForceEncodeParams];
     IdHTTP.Request.ContentType := 'application/xml';
     IdHttp.Request.CharSet     := 'UTF-8';
     IdHTTP.Response.KeepAlive  :=  False;
     strGelen := IdHTTP.Get( FPhpPush + 'index.php?action=tokenlist' );
     while Pos('<deviceToken>', strGelen) > 0 do begin
       DeviceList.Add( AradanSec( strGelen, '<deviceToken>', '</deviceToken>', True ) );
     end;
     strJson := '{'
               +'   "priority": "normal", '
               +'   "registration_ids":'
               +'   [';
                      while DeviceList.Count > 0 do
                      begin
                        strJson := strJson + '"' + DeviceList[0] + '"';
                        DeviceList.Delete(0);
                        if DeviceList.Count > 0
                          then strJson := strJson + ',';
                      end;
     strJson := strJson
               +'   ],'
               +'   "notification": {' // notification mesaj tipi
               +'       "body": "'  + strMesaj   + '",'
               +'       "title": "' + strMesajID + '",'
               +'       "icon" : "'+FPhpPush+'mesaj.png",'
               +'       "color": "#f45342"'
               +'   },'
               +'   "data": {' // data mesaj tipi
               +'     "id": "'      + strMesajID + '",'
               +'     "message": "' + strMesaj   + '",'
               +'     "site_adi": "armanlab.com",'
               +'     "link": "http://www.armanlab.com"'
               +'   },'
               +'   "collapse_key": "Bilgi_Mesaj"'
               +'}'
               ;
     {$IF CompilerVersion >= 22.0}  // XE
       JsonStream := TStringStream.Create(strJson, TEncoding.UTF8);
     {$ELSE} //D2007 ve öncesi için
       JsonStream := TStringStream.Create( Utf8Encode(strJson) );
     {$IFEND}
     try
       if boolSecure then
       begin // HTTPS erişim yapılacak... OpenSSL kütüphaneleri Gerekli.
         {$IFDEF SSL_DLLs_inResourceMode}
         // SSL Kütüphaneleri lisans sorunu yaratmasın diye RESOURCE altından çıkardım.
         {$IF CompilerVersion >= 22.0} a := 3; b := 4;  // XE
         {$ELSE}                       a := 1; b := 2;  // D7 = 15.0
         {$IFEND}
           if NOT IsFileInUse( GetTempDir + 'ssleay32.dll' ) then
             With TResourceStream.Create(HInstance, Format('ssl_%.2d', [a]), RT_RCDATA) do
             begin
               Try
                 SaveToFile( GetTempDir + 'ssleay32.dll' );
               finally
                 free;
               end;
             end;

           if NOT IsFileInUse( GetTempDir + 'libeay32.dll' ) then
             With TResourceStream.Create(HInstance, Format('ssl_%.2d', [b]), RT_RCDATA) do
             begin
               Try
                 SaveToFile( GetTempDir + 'libeay32.dll' );
               finally
                 free;
               end;
             end;
        // Bendeki Indy sürümü ile uyumsuzdu... Alttaki şekilde değiştirdim...
        // IdSSLOpenSSLHeaders.IdOpenSSLSetLibPath( GetTempDir );
        // IdSSLOpenSSLHeaders.Load;

        // SSL DLL'lerini RES içinden TempDir'e aldık...
           SetDllDirectory( StringToOLEStr(GetTempDir) );
       {$ENDIF}
{$IF defined(MSWINDOWS)}
         SslIOHandler        := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
         IdHTTP.IOHandler    := SSLIOHandler;
{$ENDIF}
       end else begin
         IdHTTP.IOHandler    := nil;
       end;
       IdHTTP.HTTPOptions  := [];
       if boolSecure
         then IdHTTP.Request.Host := GCM_SecureUrl
         else idHTTP.Request.Host := GCM_OpenUrl;
       IdHttp.Request.CustomHeaders.AddValue('Authorization', 'key=' + FAPIKey );
       IdHTTP.Request.ContentType := 'application/json'; // Plain Text için 'application/x-www-form-urlencoded;charset=UTF-8'
       IdHTTP.Request.CharSet     := 'utf-8';
LogLa( idHTTP.Request.Host );
LogLa( JsonStream.DataString );
       Result := IdHTTP.Post(idHTTP.Request.Host, JsonStream);
     finally
       JsonStream.Free;
     end;
   finally
{$IF defined(MSWINDOWS)}
     if boolSecure then FreeAndNil(SslIOHandler);
{$ENDIF}
     FreeAndNil(IdHTTP);
     DeviceList.Free;
   end;
 end else begin
   Result := 'Google API Key girilmemiş...';
 end;
end;

{$ENDREGION 'ORTAK Fonksiyonlar'}

initialization
 xGCMHelper := TGCM_Helper.Create;

finalization
 FreeAndNil(xGCMHelper);

end.



Şimdi Delphi Yakasında ilerleyelim.


-------------------------------------------------

[b]
Android Uygulaması ile PushMesaj alma kısmına çalışalım.
Öncelikle herkesin bildiği şekilde yeni bir “Multi-Device Application” proje açıyoruz.
kEZQE7.png
 

[/b]
  • Öncelikli dikkat gerektiren husus 

[b]

[b]Project / Options  / Entitlement List
başlığından
Receive push notfication özelliğinin TRUE yapılmasıdır.
 
NkYXkN.png
 [/b]
[/b]
  • Sonra PUSH iletilerin WEB üzerinden geldiğini dikkate aldığımızda ihtiyacımız olan

[b]

[b]Project / Options  / Uses Permissions
başlığından
Internet özelliğinin TRUE yapılmasıdır.
Zaten bu özellik varsayılan olarak açıktır. Sadece hatırlatalım.
 
A3ZVn7.png
 [/b]
[/b]
  • Açtığımız bu boş projeyi bir defalığına BUILD ALL yapıyoruz ki AndroidManifest XML dosyamız CREATE edilsin. 

[b]

Çünkü bir sonraki aşamada bu dosya içerisine önemli bir satır ekleyeceğiz.
 
0ybXlD.png
           

 

[/b]
40YA10.png

[b]
 

[/b]
J3d9qq.png

[b]
 

[/b]
OEJbV0.png

[b]
 

[/b]
  • Helper PAS dosyası içinde copy/paste yapabileceğiniz şekilde Manifest dosyasına eklenecek satır yer almaktadır.



znQZaB.png

[b]
 
 
 

Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol  Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol da projelere kaynak kodlarıyla birlikte ulaşabilirsiniz.
[/b]
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif


Cevapla
#2
Vaaay hazine var burda
Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol
Kuvvete dayanamayan adalet aciz, 
Adalete dayanamayan kuvvet zalimdir.
WWW
Cevapla
#3
Hocam,
Ellerinize sağlık. 
Formumuzun hedefleri doğrultusunda (Yeni Nesil Teknoloji) hazırlanmış olduğunuz bu kaliteli makalenizden dolayı teşekkür ederim.
Allah sizi ve sizin gibileri başımızdan (formumuzdan  Wink ) eksik etmesin.
While true do; Hayat döngüsü, kısır değildir! Yapılan bir yanlış, o döngünün dışına çıkmanızı sağlayacaktır.
WWW
Cevapla
#4
mrmarman hocam,
Proje indirme linkinizde şifre istiyor. Şifresiz yayınlaam imkanınız var mı? veya şifreyi de paylaşabilir misiniz?
While true do; Hayat döngüsü, kısır değildir! Yapılan bir yanlış, o döngünün dışına çıkmanızı sağlayacaktır.
WWW
Cevapla
#5
Şifre yanlışlıkla olmuş update ettim. 

rghost.png
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif


Cevapla
#6
(12-08-2016, Saat: 21:04)mrmarman Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlŞifre yanlışlıkla olmuş update ettim. 

rghost.png

abi ana konunun en altındaki link hala şifre istiyor bende mi sorun var acıba? Şu Res dosyana ulaşmam gerek (dlller için)  Angel
Cevapla
#7
Şimdi bir daha dener misin.
Olmazsa sıfırdan yollarım.
Denedim indiriyor.
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif


Cevapla
#8
(12-08-2016, Saat: 22:42)mrmarman Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlŞimdi bir daha dener misin.
Olmazsa sıfırdan yollarım.
Denedim indiriyor.

Şimdi oldu abilerin kralı eline sağlık  Cool
Cevapla
#9
Web üzerinden de denemeyi unutma. Onu da seveceksin.

Kod:
http://www.armanlab.com/GoogleCloudMes/index.php?action=sendmsg&msgText=ARMAN Deneme&msgId=1973
Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif


Cevapla
#10
(12-08-2016, Saat: 23:02)mrmarman Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlWeb üzerinden de denemeyi unutma. Onu da seveceksin.

Kod:
http://gcmarman.esy.es/index.php?action=sendmsg&mesText=ARMAN Deneme&msgId=1973
Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol

Web olayı nefis bişey olmuş. Kullanırken aklıma bir şeyler geldi,

 * Bu sistemi kullanarak forumlara takip edilmek istenen konulara cevap geldiğinde bilgilendirme yapılabilir.
 * Uzaktan komutlarla cihazlar çalıştırılabilir.
 * Lokasyon takibi yapılabilir  Cool (Küçük bir ekleme ile)
 * Bilgisayar üzerinde kullanıcılar takip edilebilir.
 * Personel Giriş Çıkışları takip edilebilir
 * Uzaktan bilgisayarlar açılıp kapatılabilir (Bir kaç modifikasyon ile)
 * Haber Servisi amaçlı kullanılabilir
 * Update sistemi olarak kullanılabilir.
 * Casus yazılım olarak kullanılabilir.
 * Akıllı ev otomasyonları hazırlanabilir.
 
Acayip bir şey geldi aklıma ama bunu şimdi söylemeyeceğim, ki %100 muharrem abinin aklına gelmiştir.  Big Grin bunu toparlayıp bir fikrim var bölümüne açıcam. İsteyen istediği gibi geliştirebilir.

Tekrardan çook teşekkürler abicim. @mrmarman
Cevapla

Konuyu Paylaş : facebook gplus twitter



Konu ile Alakalı Benzer Konular
Konular Yazar Yorumlar Okunma Son Yorum
  Tüm platformlar (Android, iOS vb.)  için telefon çağrı durumunu yakalamak Fesih ARSLAN 18 4.388 14-04-2017, Saat: 21:17
Son Yorum: quiet1day
  Delphi Firemonkey Android İzin Kontrolü esrehmaan 2 901 23-12-2016, Saat: 22:22
Son Yorum: esrehmaan



Konuyu Okuyanlar: 1 Ziyaretçi