‏הצגת רשומות עם תוויות פיתוח תוכנה. הצג את כל הרשומות
‏הצגת רשומות עם תוויות פיתוח תוכנה. הצג את כל הרשומות

יום שבת, 29 במרץ 2014

Chrome Extensions Guide




אין ספק שגוגל היא חממת הפיתוח המעניינת ביותר בתעשיה, אחד הפרוייקטים המרשימים ביותר שלה לטעמי הוא Chrome שהכניס בעיטה רצינית לתחום הדפדפנים שנשלט בעיקר ע"י Internet Explorer ו Firefox, אז חוץ מהעובדה שזו תוכנה נפלאה שתומכות ב HTML 5 בצורתו המלאה, ניתן להוסיף Extensions בצורה פשוטה שהופכת את הגלישה להרבה יותר מעניינת.

פיתוח Extension ב Chrome נעשה עם שפות לכתיבת אתרים כמו Html, JavaScript ו Css ולא נדרשת למידה של שפה חדשה בניגוד לשאר השחקנים שנעזרים בשפות כמו Bash ב Firefox או #C ב Internet Explorer, לא פחות חשוב שה Extension הוא Cross Platform כך שמערכת ההפעלה לא באמת לוקחת תפקיד, הפיתוח אפשרי בכל IDE לכתיבת אתרים וה Debug מתבצעת ע"ג הדפדפן עצמו.



Manifest File

בדומה לפיתוח ב Android גם פה יש קובץ הגדרות כללי שבו מצהירים על התוכן וההרשאות, המבנה בצורת JSON והוא הקובץ הראשון שנטען ע"י הדפדפן.

manifest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
    
    "permissions": [...],

    "browser_action": {...},
    "background": {...},

     "content_scripts": [{...}]
}

Page Action\Browser Action

ניתן להגדיר את ה Extension  ברמת דף ספציפי כלומר כאשר הדפדפן יכנס לאתר מסויים רק אז יטען ויופיע כחלק מה ה Address Bar או כאשר הדפדפן עצמו יטען ה Extension יעלה אוטומטית ויופעל על כל האתרים, לא ניתן לשלב בין 2 המצבים ולכן כדי להחליט כבר בהתחלה מה המצב המתאים ביותר ל Extension.  

Page Action

manifest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
 
"permissions": [
        "tabs"
    ],

"page_action":{
"default_title": "This is Proxytype Blog!",
        "default_icon": "fishluck.png"
},

"background":{"scripts":["background.js"]},

"icons":{
"48":"fishluck.png",
"128":"fishluck.png"
}
}

background.js

chrome.tabs.onUpdated.addListener(function (tabid, data, tab) {
    if (tab.url.indexOf('proxytype.blogspot') > -1) {
        chrome.pageAction.show(tabid);
    }
});




ה Extension  יופיע כחלק מה Address Bar.

Browser Action

manifest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
 
"permissions": [
        "tabs"
    ],

"browser_action":{
             "default_title": "This is Proxytype Blog!",
             "default_icon": "fishluck.png"
},

"icons":{
"48":"fishluck.png",
"128":"fishluck.png"
}
}




ה Extension יופיע מימין ל Address Bar ויהיה תקף לכל האתרים.


Popup

דף להצגת תוכן כאשר לוחצים על ה Extension.

manifest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
 
"permissions": [
        "tabs"
    ],

"browser_action":{
             "default_title": "This is Proxytype Blog!",
             "default_icon": "fishluck.png",
             "default_popup": "popup.html"
},

"icons":{
"48":"fishluck.png",
"128":"fishluck.png"
}
}

popup.html

<html>
<head></head>
<body>
<div style="width:300px;height:200px;background-color:black">
<p style="font-family:Arial; font-size:14px; font-weight:bold;color:White;padding-top:10px; padding-left:10px">This is Example Popup</p>
<div style="text-align:center">
<img src="fishluck.png" width="100" height="100"/>
</div>
</div>
</body>
</html>






Options

דף שמכיל את האפשרויות של ה Extension.

manifest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
 
"permissions": [
        "tabs"
    ],

"options_page": "options.html",

"browser_action":{
   "default_title": "This is Proxytype Blog!",
           "default_icon": "fishluck.png",
    "default_popup": "popup.html"
},

"icons":{
"48":"fishluck.png",
"128":"fishluck.png"
}
}






Background Script/Content Script

אפשר להגיד שהגענו לדבר האמיתי, הקוד של ה Javascript עושה את כל הקסם, וגם בו יש 2 מצבים אבל הפעם ניתן לשלב בניהם, Background Script נטען ללא דף Html ורץ ברקע של ה Extension ו Content Script רץ במקביל לדף ומאפשר לשנות את התוכן לאחר טעינה.

Background Script

manifest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
 
"permissions": [
        "tabs"
    ],

"options_page": "options.html",

"browser_action":{
"default_title": "This is Proxytype Blog!",
                "default_icon": "fishluck.png",
"default_popup": "popup.html"
},

"background": {
                 "scripts": ["jquery.js","json.js","background.js"]
},

"icons":{
"48":"fishluck.png",
"128":"fishluck.png"
}
}


background.js

chrome.tabs.onUpdated.addListener(function (tabid, data, tab) {
    console.log("this is a background script");
});




Content Script

mainfest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
   
"permissions": [
            "tabs",
    "activeTab",
    "http://*/*",
            "https://*/*"
      ],

"options_page": "options.html",

"browser_action":{
"default_title": "This is Proxytype Blog!",
                "default_icon": "fishluck.png",
"default_popup": "popup.html"
},


      "content_scripts": [{
 "matches": ["<all_urls>"],
          "js": ["jquery.js","json.js","script.js"]
       }],

"icons":{
"48":"fishluck.png",
"128":"fishluck.png"
}
}

script.js

$(document).ready(function () {
    document.title = "Proxytype Take Over!";
    document.body.innerHTML = "<html><head></head><body>Extension Denial Of Service For Fun!</body>";
});



Messages

קיימת חלוקה ברורה בין הקוד שמריץ הדף בדפדפן לבין הקוד שרץ לצורך הדוגמה ב Popup, ניתן להעביר מידע מתוכן הדף ל Extension בעזרת Messages, לדוגמה כל עוד ה Popup פתוח ישלח אליו כותרת העמוד.

manifest.json

{
    "name": "Extenstion Example",
    "description": "Chrome Extenstion Example",
    "manifest_version": 2,
    "version": "1.0",
   
"permissions": [
          "tabs",
  "activeTab",
          "storage"
    ],

"options_page": "options.html",

"browser_action":{
"default_title": "This is Proxytype Blog!",
        "default_icon": "fishluck.png",
"default_popup": "popup.html"
},

"content_scripts": [{
 "matches": ["<all_urls>"],
      "js": ["jquery.js","json.js","script.js"]
    }],

"icons":{
"48":"fishluck.png",
"128":"fishluck.png"
}
}

popup.js

$(document).ready(function () {
    var message = document.querySelector('#message');
    chrome.runtime.onMessage.addListener(function (request, sender) {
        if (request.action == "sendMessage") {
            message.innerHTML = request.source;
        }
    });
});

script.js

function sendMessage() {
    return document.title;
}

chrome.runtime.sendMessage({
    action: "sendMessage",
    source: sendMessage()
});



ניתן לחסוך המון כאבי ראש אם פשוט משתמשים ב Local Storage כ Shared Memory וכך ניתן לגשת ישירות למידע.

   chrome.storage.local.get('sharedMemory', function (result) {
        try {

             list = JSON.parse(result.sharedMemory);

        } catch (e) {
            console.log(e);
        }
    });

  chrome.storage.local.set({ 'sharedMemory': json_data });



Packaging

על מנת להתחיל פרויקט יש ליצור תיקייה ולהכניס לתוכה את הקבצים הדרושים, ללחוץ על תגית Extension ב Tools ולעבור למצב Developer, לצורך בדיקות ניתן לטעון את התיקייה ישירות:



בסיום הפיתוח ניתן לייצר חבילה שהיא קובץ Zip שמכיל את כל הקבצים בסיומת Crx, וקובץ Pem שהוא המפתח של Extension ששומרים בצד לצורך עדכונים עתידיים.

סיכום

עם היכולות של Html 5 אפשר לבצע כמעט כל פעולה במקרים נדירים נרצה להוסיף קבצי Native Code על מנת לקחת את ה Extension לקצה אבל את זה אני אשמור למאמר אחר בנושא, אין ספק ש Chrome הוא הדפדפן המתקדם ביותר כרגע בשוק, פשוט תענוג לעבוד איתו.

למידע נוסף:


תוספת משהו?...

יום רביעי, 12 בפברואר 2014

Managed / UnManaged Code




לאורך הבלוג נזרק המושג Managed Code מספר פעמים ולא יותר מידי הרחבתי עליו , אבל מסתתר מאחוריו פואנטה שכדאי להכיר ולכן החלטתי לכתוב מאמר קצרצר שיסביר את הנושא למרות שכבר עברתי עליו בצורה כזאת או אחרת בבלוג, החיבור בין 2 שיטות ריצה שונות מאפשר יצירת תוכניות מורכבות שמאפשרות ניצול של 2 פלטפורמות שונות זו מזו, לדוגמה האפשרות לכתוב שכבה שעובדת מול Win32 API שלא ניתן לגשת אליה באופן חופשי מ .Net כפי שכבר הראתי בעבר ב Personal Firewall.


Managed Code

סביבת .Net מוגדרת כ Manage Code ומזכירה בהתנהגות שלה את Java בכך שב 2 הסביבות נוצר חדר נקי שמאפשר לקוד לרוץ ללא הפרעות, במקרה של .Net אומנם נוצר קובץ EXE בסוף התהליך, אך כאשר מפעילים אותו הוא מפעיל מערכת שלמה מאחורי הקלעים על מנת שיוכל לרוץ, כאשר הקובץ נוצר, הוא נשמר בשפת בניים הנקראת IL ועובר למנגנון CLR ומשם ל JIT כפי שכתבתי במאמר Managed .Net RootKit, לכן זה לא באמת קוד מכונה, הוא נהפך לקוד מכונה תוך כדי ריצה.

UnManaged Code

זה קוד שנהפך לקוד מכונה ברגע שמבצעים את ה Compile ומאותו רגע הוא יכול לרוץ ולכן מהיר יותר, בדר"כ נכתוב אותו ב ++C/ C ונשתמש ב Compilers ותיקים בתחום, לא קיים חדר נקי ולכן התוכניות עלולות לגרום נזק למערכת הפעלה ישירות, בדרך כלל נשתמש בו כאשר נרצה לדבר ישירות עם ה Win32API או לשפר ביצועים, השפה שנהנת מכל העולמות היא ++C שמאפשרת כתיבת קוד משולב.

שימו לב! - ב Managed Code קיים מנגנון ה Garbage Collector שמשחרר זיכרון באופן עצמי גם אם לא שחררתם אותו בעצמכם לעומת זאת ב UnManaged Code הוא לא קיים ואחריות שחרור הזיכרון היא על המפתחים בלבד.

בדוגמה הבאה אני ידגים כיצד לחבר בין אפליקציית .Net עם פונקציות שנכתבו ב  ++C/ C, תחילה נתמקד ב UnManaged Code שאותו אני אכתוב ב ++C בעזרת Visual Studio.

dllctest.h

//in C++ we can have functions with the same name,
//the compiler create unique name for each function, 
//in C is not possible to have different functions with the same name, 
//the extern "C" tell the compiler to keep the name as is like in C
//be aware from functions with the same name!.
extern "C" {
//exported function signature,
//__declspec tell to compiler to expose the function
__declspec(dllexport)   char *  getString(void);
__declspec(dllexport)   void  setString(char * data);
}


dllctest.cpp

//dllctest.cpp : Defines the exported functions for the DLL application.
#include "stdafx.h"
#include "dllctest.h"

static char * sdata;

//tell to compiler someone else going to use this method
extern char * getString(void)
{
return sdata;
}

//tell to compiler someone else going to use this method
extern void setString(char * data)
{
sdata = data;
}

יש מספר מילים שמורות שאומרות ל Compiler שמדובר על פונקציות שמשהו אחר (חיצוני) הולך להשתמש בהם, ומאפשרות לחשוף אותן החוצה, בגלל שהקוד עצמו  ב ++C חשוב להגיד ל Compiler שישמור על השמות של הפונקציות כמו שהם בדומה להתנהגות של C כי אחרת הוא ממציא להן שמות בעצמו.

שימו לב! - חשוב מאוד שהשמות של הפונקציות לא יחזרו על עצמן, ב C זה בלתי אפשרי אבל ב ++C זה אפשרי ועלולה להיות התנגשות בין הפונקציות.

dllctestcs.cs

class Program
{
        //call to external function from dll 
        //using the same signature from the dll
        [DllImport(@"C:\inetpub\wwwroot\dllctest\Debug\dllctest.dll", CharSet = CharSet.Unicode)]
        extern static void setString(string data);

        [DllImport(@"C:\inetpub\wwwroot\dllctest\Debug\dllctest.dll",CharSet=CharSet.Unicode)]
        extern static string getString();

        static void Main(string[] args)
        {
            setString("HI");

            Console.Write(getString());

            Console.ReadLine();
        }
    }
על מנת לדבר עם ה DLL שיצרנו נשתמש ב (Platform Invocation Services (PInvoke , מנגנון שמגיע עם .Net שמאפשר לטעון DLL של UnManaged Code תוך כדי ריצה, במקרה שלנו אנחנו מעבירים מחרוזות לכן חשוב להגדיר את סוג הקידוד.

סיכום

תחשבו לכם איזה יתרון אדיר יש בשילוב בין 2 פלטפורמות שונות שמאפשרות לנו לגעת בכל העולמות, לצורך העניין ב .Net יש כלים נהדרים לעבודה על אתרי אינטרנט, בניית ממשקים, DB וכו' ואילו ב C נוכל לשפר ביצועים ולהגיע למקומות ש .Net לא יכול.

השילוב הראוי...

יום שני, 27 בינואר 2014

C# Reflection Guide



אחד התחומים המעניינים בפיתוח, האפשרות לתוכנית ללמוד על עצמה תוך כדי ריצה מאפשרת לפתח תוכניות מיוחדות במיוחד, האמת שנושא ה Reflection ישן והוא קיים עוד מתחילת ה Framework הראשון אבל אף פעם לא התייחסתי אליו כי בדרך כלל אין בו צורך, לרוב אנחנו יודעים מה אנחנו הולכים לפתח אבל קיימים מקרים מסויימים בהם יש חור שחור ואין לנו מספיק מידע ולכן עלינו לשמור על קוד גנרי ככל האפשר.

Reflection

יכולת של תוכנית לזהות במהלך הריצה את האובייקטים שרצים בה, לנתח אותם ולהפעיל בהם פונקציות באופן דינמי לפי סוג האובייקט או על פי קונבנציה קבועה, במאמר זה אני יעבור על נושא ה Reflection בסביבת .Net אבל היכולת הזאת נתמכת גם בסביבות נוספות כמו JavaScript, C++,Phyton וכו', הרעיון המרכזי הוא להפוך את התוכנית ל"שקופה" ושתוכל להתמודד עם אובייקטים שונים מבלי לעבור Compile כל פעם מחדש.

לצורך הדוגמה בניתי תוכנית והיא מאפשרת למשתמשים ליצור עבורה Plugins מיוחדים, מהרגע שהכנסתי את התכונה הזאת אני לא יודע מה המשתמש יעשה (האמת שזה לא כלכך צריך לעניין אותי), ואני לא יכול לעשות Compile לתוכנית המקורית כל פעם מחדש כי זה יהיה מגוחך, כאן נכנס ה Reflection לתמונה שבעזרתו אני יכול להגדיר קונבנציה עבור טעינת Plugin כחלק מה SDK של התוכנית, משתמש הקצה מייצר אובייקט שמכיל בתוכו קריאות לפונקציות שנקבעו מראש שמטרתם לחבר בין התוכנית לקוד שהמשתמש יצר, ברגע שמופעלת הפונקציה הקוד של המשתמש רץ מבלי שהיה חלק מהתוכנית עצמה כפי שניתן לראות בקוד הבא:

public class example
    {
        //private string
        private string msg0 = "this is exmaple of private string!";

        //public string
        public string msg1 = "this is exmaple of public string!";

        //constructor
        public example()
        {

        }

        //public function for invoke return string
        public string returnPrivateMessage()
        {
            return msg0;
        }

        //public function for invoke getting external 
        //parameter and return string
        public string returnEditMessage(string msg)
        {
            return "you write: " + msg;
        }
    }


המשתמש עוטף את המחלקה כ DLL ומעתיק אותה לתיקיית Plugin בתוכנית,התוכנית טוענת אותו לזיכרון ומחפשת את הפונקציה שנקבעה מראש על מנת להפעיל אותה ובעצם מעבירה את הריצה ל DLL מבלי לדעת מה בדיוק עומד לקרות שם.


class Program
    {
        static void Main(string[] args)
        {
            //load assmbly dynamically
            Assembly assemblyInstance = Assembly.LoadFrom(@"C:\\RefDLL.dll");

            Type[] types = assemblyInstance.GetTypes();

            for (int i = 0; i < types.Length; i++)
            {
                //search for specific type
                if (types[i].Name.ToLower() == "example")
                {
                    MethodInfo[] methods = types[i].GetMethods();
                    if (methods != null)
                    {
                        for (int z = 0; z < methods.Length; z++)
                        {
                            //search for specific method
                            if (methods[z].Name == "returnPrivateMessage")
                            {
                                //create instance for dynamic object
                                object classInstance = Activator.CreateInstance(types[i], null);

                                //execute method and gettig string
                                object res = methods[z].Invoke(classInstance, new object[0]);

                            }

                            if (methods[z].Name == "returnEditMessage")
                            {
                                //create parameters array for function,
                                //you can get the types of the arguments using
                                //ParameterInfo[] parameters = methods[z].GetParameters();
                                object[] parametersArray = new object[] { "my information!" };
                                object classInstance = Activator.CreateInstance(types[i], null);

                                object res = methods[z].Invoke(classInstance, parametersArray);
                            }
                        }
                    }
                }
            }

        }
    }



ראינו כיצד ניתן להפוך את התוכנית למשהו גנרי אבל מצד שני הקוד שלנו חשוף וכל אחד יכול להפעיל אותו, הצגתי בעבר כיצד ניתן לעשות Disassembly לקבצי DLL שנוצרו ב .Net וה Reflection לוקח חלק חשוב בתהליך, לסיום החלטתי שזו הזדמנות טובה להראות את היכולות של ה Reflection בעזרת כלי קטן שמאפשר לנתח קבצי DLL בצורה נוחה ומהירה, בדומה לדוגמה הקודמת התוכנית מקבלת נתיב ל DLL ופורסת אותו, היא מראה את הפונקציות, המשתנים והאירועים של האובייקטים בקובץ.

DLLReflector


סיכום:

אז כמו שכבר אמרתי בהתחלה בדרך כלל לא נמצא ל Reflection שימוש אלא במקרים מאוד מסויימים, ההתעסקות בניתוח האובייקט עלולה להפוך את התוכנית למסורבלת ואיטית ולכן כדאי לחשוב פעמיים לפני שבוחרים בדרך הזאת, אבל אין ספק שזה לוקח את האינטליגנציה של התוכנה צעד אחד קדימה.

קוד להורדה:


סקיינט?



יום שבת, 5 באוקטובר 2013

Arduino LCD Guide



האמת שיש נושא שעדיין לא סגור כמו שצריך, במאמר Arduino And .Net השתמשתי במסך שמגיע כחלק מ Shield, אבל עדיין לא הסברתי כיצד לחבר מסך LCD ישירות ל Arduino והגיע הזמן לעשות זאת, יש שפע של מסכים מסוגים שונים בשוק את שלי מצאתי כמו תמיד ב DX במחיר של 4 דולר.


דרישות:

  • Arduino Uno
  • (LCD Screen (16X2
  • Potentiometer
  • BreadBoard
  • Wires




Liquid-Crystal Display

רוב מסכי ה LCD מגיעים עם 16 פינים אבל לא צריך להשתמש בכולם, על מנת לחבר את המסך ל Arduino נצטרך רק 9 פינים את חלקם נעביר לספרייה LiquidCrystal שמגיעה עם הסביבה של Arduino ונחבר פוטנציומטר על מנת שנשלוט בחוזק התאורה האחורית (K, A).

6 כניסות דיגיטליות, GND ו 5V

מבנה סופי

הטקסט תקין למרות שבתמונה נראה שלא...



קוד

בשביל שלא יהיה משעמם, הקוד מכניס את הטקסט משמאל לימין, כל הודעה לשורה אחרת במסך.

#include <LiquidCrystal.h>

LiquidCrystal lcd(7,6,5,4,3,2);
char screen[16];
char message1[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','P','r','o','x','y','T','y','p','e',' ','B','l','o','g'};
char message2[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','P','r','o','x','y','T','y','p','e','.','b','l','o','g','s','p','o','t','.','c','o','m'};

void setup()
{
      //set the screen chars and rows
     lcd.begin(16,2 );
}

void loop()
{

    //send the first message to buffer
    sendMsg1();
    delay(100);
    
    cleanArray();
    
    //send the second message to buffer
    sendMsg2();
   delay(100);
    cleanArray();
}

void sendMsg1()
{
   int msgsize = sizeof(message1) / sizeof(char);
  for(int i = msgsize; i > 0;i--)
  {
      pushArray(message1[i],0);
       delay(200);
  }  
}

void sendMsg2()
{
   int msgsize = sizeof(message2) / sizeof(char);
  for(int i = msgsize; i >0;i--)
  {
      pushArray(message2[i],1);
       delay(200);
  }  
}


void pushArray(char c, int line)
{

  lcd.clear();
  lcd.setCursor(0,line);
  char tmp = screen[0];
  screen[0] = c;
  
  for (int i = 1; i < 16; i++)
     {
                char locker;

                    locker = screen[i];
                screen[i] = tmp;

                if (i == 16)
                    break;

                tmp = locker; 
       }
       
         lcd.print(screen);
}

void cleanArray()
{
    //clear screen buffer
    for(int i=0; i < 17; i++)
    {
      screen[i] = '\0';
    }
}


סרט הדגמה




סיכום 

בד"כ Shields של LCD מגיעים עם רכיבים נוספים כמו כפתורים ו Leds ולא תמיד יש בהם שימוש וברוב המקרים אנחנו צריכים רק את המסך, מסכים הם אחד הרכיבים הנחוצים עבור חווית משתמש ונשלב אותם כחלק מהפרויקט או ככלים לניטור שננתק אח"כ.

בהצלחה...

יום שישי, 12 באפריל 2013

Google Maps Android



מפות רבותי, מפות, אז אחרי ש Google עשתה את כל העבודה כשמיפתה את העולם ופתחה אותו לכל מי שרק חופץ, העסק נהפך להרבה יותר פשוט, אם תשאלו אותי לפני ש Google החליטה לבצע כזה צעד נועז בתור מפתח לא חשבתי שבכלל תהיה לי היכולת להשתמש במערכות מיפוי, העסק תמיד היה נראה לי מסובך ומצריך משאבים רבים,  Google החליטה לתת השירות באופן חופשי עבור משתמשי Android ולאפשר למפתחים לשלב שירותי מיפוי באפליקציות שלהם ללא תשלום, ובשילוב ה Gps שבמכשירים ניתן לפתח אפליקציות מיקום מתקדמות.

במאמר זה אני יעבור על תהליך ההתממשקות ושלב כתיבת האפליקציה ב Android.

שימו לב! - המאמר עובר על תהליך ההתממשקות בגרסת  Google Maps Api 2 בשילוב גרסת Api 10 של Android.


התממשקות לשירותי Google

תחילה יש להירשם ל Google (מי שעוד לא הספיק) יש להיכנס לאתר API Console ואח"כ לבחור את ה Api המתאים ברשימת ה Services.



לאחר מכן  נגדיר את המפתח עבור האפליקציה, נלחץ על Create New Android Key:


במקרה של עבודה של Debug ניתן להכניס את המפתח ה Default ולהוסיף את שם ה Package שלנו כפי שניתן לראות בדוגמה:

45:B5:E4:6F:36:AD:0A:98:94:B4:02:66:2B:12:17:F2:56:26:A0:E0;com.example

במקרה של Release צריך לייצר מפתח חדש בעזרת הכלי Keytool, את מחרוזת ה SHA1 נטען בתור המפתח ולאחר מכן נרשום אותו ב Manifest כפי שמוצג בהמשך.

יצירת פרויקט:

לפני שניצור פרוייקט חדש חשוב לוודא ש Google Services Api מותקן כחלק מ Android Api 10, בנוסף יש להתקין את הדוגמאות עבור ה SDK.




ניצור פרוייקט חדש ב Eclipse של Android,   ונשנה את ה Android Build Target ל Google Api, נוסיף פרויקט נוסף  ב Workspace שנקרא google_play_services_lib  הוא נמצא בתיקיית ה Extra של ה SDK ונוסיף אותו ב Library בפרויקט כפי שניתן לראות בדוגמה:



קוד:

הקוד עצמו יחסית פשוט , ניצור מחלקה שיורשת FragmentActivity  ונחבר Interface מסוג LocationListener, נממש מספר פונקציות ונגדיר את ה Service Provider ע"פ החומרה הקיימת במכשיר על מנת לקבל את המיקום, נסמן את עצמנו במפה ונדאג לעדכן את המיקום בכל פרק זמן או מרחק שעברנו.

Activity

package com.example.urban;

import com.google.android.gms.maps.*;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.content.Context;
import android.graphics.Color;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
import android.support.v4.app.FragmentActivity;


public class GoogleMapActivity2 extends FragmentActivity implements LocationListener {

private GoogleMap mymap;

   private LocationManager locationManager;
   private String provider;
   private Marker locationMarker;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_google_map_activity2);

set_titlecolor();

//get map component from activity
   mymap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
   //map.setMyLocationEnabled(true);
 
   //set location manager and start active and checking availability of location service
   LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
     
   //check for GPS
   boolean enabledGPS = service
                .isProviderEnabled(LocationManager.GPS_PROVIDER);
   //check for WIFI
   boolean enabledWiFi = service
                .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

       //Check if GPS enable throw error
        if (!enabledGPS) {
            Toast.makeText(this, "GPS signal not found", Toast.LENGTH_LONG).show();
        }

       Location location = get_location();

        // Initialize the location fields
        if (location != null) {
            Toast.makeText(this, "Selected Provider " + provider,
                    Toast.LENGTH_SHORT).show();
            onLocationChanged(location);
         
        }

}

private void set_titlecolor()
{
                //just for fun change title color
View title = getWindow().findViewById(android.R.id.title);
View titleBar = (View) title.getParent();
titleBar.setBackgroundColor(Color.argb(100, 58, 58, 58));
}

private Location get_location()
{
//getting the location manager
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
       //define new Criteria for location service
       Criteria criteria = new Criteria();
       //request accuracy fine => GPS provider
       criteria.setAccuracy(Criteria.ACCURACY_FINE);
   
       provider = locationManager.getBestProvider(criteria, false);
       Location location = locationManager.getLastKnownLocation(provider);
       return location;
}

@Override
    protected void onResume() {
        super.onResume();
        //request to update location every 200 millisecond, and every 1 meter
        locationManager.requestLocationUpdates(provider,200, 1, this);
    }

    /* Remove the locationlistener updates when Activity is paused */
    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);
    }

@Override
   public void onLocationChanged(Location location) {
       double lat =  location.getLatitude();
       double lng = location.getLongitude();
   
       Toast.makeText(this, "Location " + lat+","+lng,
               Toast.LENGTH_LONG).show();
     
       LatLng coordinate = new LatLng(lat, lng);
       Toast.makeText(this, "Location " + coordinate.latitude+","+coordinate.longitude,
               Toast.LENGTH_LONG).show();
     
       //remove the marker if exists
       if(locationMarker != null)
        locationMarker.remove();
     
       //add marker to map
       locationMarker = mymap.addMarker(new MarkerOptions()
       .position(coordinate)
       .title("Are You Spy On Me?")
       .snippet("don't expose your position!")
       .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));
     
   
       //going to update the camera with the new coordinates
       CameraUpdate center=
                    CameraUpdateFactory.newLatLng(new LatLng(coordinate.latitude,
                    coordinate.longitude));
                 //set camera zoom
CameraUpdate zoom=CameraUpdateFactory.zoomTo(17);

//move camera to location
mymap.moveCamera(center);
mymap.animateCamera(zoom);
   }

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.google_map_activity2, menu);
return true;
}


@Override
public void onProviderDisabled(String arg0) {
  Toast.makeText(this, "Enabled new provider " + provider,
               Toast.LENGTH_SHORT).show();

}


@Override
public void onProviderEnabled(String arg0) {
Toast.makeText(this, "Disabled provider " + provider,
               Toast.LENGTH_SHORT).show();

}


@Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
// TODO Auto-generated method stub

}

}

Layout

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:map="http://schemas.android.com/apk/res-auto"
  android:id="@+id/map"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  class="com.google.android.gms.maps.SupportMapFragment"
  map:cameraTilt="30"
  map:cameraZoom="13"
  map:mapType="normal"
  map:uiCompass="false"
  map:uiRotateGestures="true"
  map:uiScrollGestures="true"
  map:uiTiltGestures="true"
  map:uiZoomControls="false"
  map:uiZoomGestures="true"/>

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.urban"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="10" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <permission
        android:name="com.example.urban.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="com.example.urban.permission.MAPS_RECEIVE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="your google api key!" />

        <uses-library
            android:name="com.google.android.maps"
            android:required="true" />

        <activity
            android:name="com.example.urban.GoogleMapActivity2"
            android:label="Google Maps Api 2" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


תוצאה סופית:




סיכום:

עכשיו העולם נראה קטן מתמיד ובעזרת שירותי Google העסק נהפך לפשוט יותר ולא פחות חשוב בחינם, אני לא יודע כמה זמן Google יחליטו להשאיר את השירות חינם עבור משתמשי Android אבל כנראה שכבר לא נלך לאיבוד.

תפסו כיוון!

יום רביעי, 13 במרץ 2013

Webkit Guide




הדפדפנים נהפכים לחלק אינטגרלי כמעט בכול אפליקציה שמשתחררת באיזה שוק, האם פיתוח ב Native Code הולך לחלוף מהעולם ואת מקומו יחליפו דפי 5 Html ו Javascript? האמת שאין סיבה שלא, עדיין צריך לתת כבוד ל Native Code כי סביר שעדיין יהיה דברים שניתן לעשות רק איתו אבל הרעיון מעניין מאוד כי אחרי הכל Html ו Javascript שפות מאוד פופולריות ומאפשרות למפתחים שלא שולטים בשפת ה Native לכתוב אפליקציות על בסיס מנוע ה Webkit שקיים במערכות ההפעלה של הפלאפונים , הטאבלטים וכו', יש המון סביבות פיתוח שמאפשרות כתיבת אפליקציות שמוגדרת Cross Platfrom בעזרת שימוש ב Webkit ובכך לשמור על קוד אחיד עבור כל המערכות בשוק.

מאחורי כל דפדפן עומד מנוע שמפענח את הקוד למה שאנחנו רואים במסך, Webkit המנוע המפורסם ביותר וזה שעומד מאחורי ה Chrome וה Safari ,כנראה שזה המנוע הנפוץ בעולם וניתן להשתמש בו בלפטפורמות רבות במיוחד בתחום ה Mobile , לכן חשוב מאוד להכיר אותו,הוא יכול לחסוך לנו בדרך לאפליקציה המושלמת שתוכל לקפוץ בקלות ממערכת למערכת.

GTK

פלטפורמה ליצירת תוכניות מבוססות חלונות בדומה ל Windows Forms של Microsoft אבל הרבה יותר מגניבה, GTK היא פלטפורה שתומכת בהמון סביבות, שפות וכמובן קוד פתוח, שילוב של GTK עם Webkit נותן את הדפדפן המושלם שיכול לרוץ על מספר מערכות הפעלה שונות בניגוד ל Explorer עם .NET, ויכול לעבוד על מספר ארכיטקטורות מעבד שונות (Arm,x86,PPC), אז אם בא לכם להכניס דפדפן נוסף לשוק אל תוותרו על השילוב בינהם.

MYBROWSER

התוכנית היא דפדפן מבוסס Webkit ו Gtk ורצה בסביבת Linux - Fedora , על מנת להריץ את הקוד יש להוריד את הספריות של ה GTK ו Webkit.

#:yum install webkitgtk-devel webkitgtk gtk+ gtk+-devel



לאחר התקנת הספריות, נגדיר את ה Compiler וה Linker ב Eclipse



יש להשתמש בכלי Pkg-config שמגדיר הגדרות ל Compiler ניתן להוסיף תוסף ב Eclipse ב Help - > Eclipse Market Place.




#include <gtk/gtk.h>
#include <webkit/webkit.h>

//set gui items
static GtkWidget* main_window;

//url box
static GtkWidget* uri_entry;

//status bar
static GtkStatusbar* main_statusbar;

//the webkit view
static WebKitWebView* web_view;

//window title
static gchar* main_title;

//progress of the loading page
static gint load_progress;

//toolbar descriptor
static guint status_context_id;

//toolbar
static GtkWidget* toolbar;

//entire instance
static GtkWidget* vbox;

static void webkit_load (GtkWidget* entry, gpointer data)
 {
  //load url to webkit
  webkit_handle_switch(entry,data,1);
 }

 static void webkit_stop(GtkWidget* widget, gpointer data)
 {
//stop webkit
  webkit_handle_switch(widget,data,0);
 }

//finish load page signal
static void load_finished_handle(WebKitWebView *web_view, WebKitWebFrame *web_frame, gpointer data) {

webkit_handle_switch(web_view,data,0);

 }

//switch between signals
 void webkit_handle_switch(GtkWidget* entry, gpointer data,gint mode)
{
//load mode
if(mode == 1)
{

const gchar* uri = gtk_entry_get_text (GTK_ENTRY (entry));
//load url to webkit
   webkit_web_view_open (web_view, uri);

  //get and remove item[3] from toolbar container
  GtkToolItem* item = gtk_toolbar_get_nth_item(toolbar,3) ;
  gtk_container_remove(GTK_TOOLBAR (toolbar),item);

  //create new button from gtk theme pack
  item = gtk_tool_button_new_from_stock (GTK_STOCK_STOP);
  //attach to clicked signal
  g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (webkit_stop), (gpointer)uri_entry);

  //insert new item to toolbar container positon 3
  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 3);
  //redraw toolbar
  gtk_widget_show_all(toolbar);
}
else
{
//stop webkit
webkit_web_view_stop_loading(web_view);

//get and remove item[3] from toolbar container
GtkToolItem* item = gtk_toolbar_get_nth_item(toolbar,3) ;
gtk_container_remove(GTK_TOOLBAR (toolbar),item);

//create new button from gtk theme pack
item = gtk_tool_button_new_from_stock (GTK_STOCK_OK);
//attach to clicked signal
g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (webkit_load), (gpointer)uri_entry);

//insert new item to toolbar container positon 3
        gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 3);
        //redraw toolbar
gtk_widget_show_all(toolbar);

}


}


static void update_title (GtkWindow* window)
{
    GString* string = g_string_new (main_title);
    g_string_append (string, " - MYBROWSER");
    if (load_progress < 100)
        g_string_append_printf (string, " (%d%%)", load_progress);
    gchar* title = g_string_free (string, FALSE);
    gtk_window_set_title (window, title);
    g_free (title);
}

static void link_hover_handle (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data)
{
   //show link from webkit to status bar
    gtk_statusbar_pop (main_statusbar, status_context_id);
    if (link)
        gtk_statusbar_push (main_statusbar, status_context_id, link);
}

//webkit page title changed handle, change window title
static void title_change_handle (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data)
{
    if (main_title)
         g_free (main_title);

     main_title = g_strdup (title);
     update_title (GTK_WINDOW (main_window));
}

//loading process webkit handle
static void progress_change_handle (WebKitWebView* page, gint progress, gpointer data)
{
   //update progress in the main window bar
    load_progress = progress;
    update_title (GTK_WINDOW (main_window));
}

//load event webkit handler
static void load_commit_handle (WebKitWebView* page, WebKitWebFrame* frame, gpointer data)
{
  //read the url ftom webkit
    const gchar* uri = webkit_web_frame_get_uri(frame);
    //update the textbox in the toolbar
     if (uri)
         gtk_entry_set_text (GTK_ENTRY (uri_entry), uri);
}

//kill the application
static void gtk_destory (GtkWidget* widget, gpointer data)
{
       gtk_main_quit ();
}

static void btn_backward_click(GtkWidget* widget, gpointer data)
{
//rise to webkit go back
webkit_web_view_go_back (web_view);
}

static void btn_forward_click (GtkWidget* widget, gpointer data)
{
//rise to webkit go forward
          webkit_web_view_go_forward (web_view);
}

static GtkWidget* init_browser ()
{
//create scroll bar object
         GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

         //create webkit component
          web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
          gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view));

         //attach to webkit events
         g_signal_connect (G_OBJECT (web_view), "title-changed", G_CALLBACK (title_change_handle),    web_view);
    g_signal_connect (G_OBJECT (web_view), "load-progress-changed", G_CALLBACK (progress_change_handle), web_view);
    g_signal_connect (G_OBJECT (web_view), "load-committed", G_CALLBACK (load_commit_handle), web_view);
    g_signal_connect (G_OBJECT (web_view), "hovering-over-link", G_CALLBACK (link_hover_handle), web_view);
    g_signal_connect (G_OBJECT (web_view), "load-finished", G_CALLBACK (load_finished_handle), web_view);

    return scrolled_window;
}

static GtkWidget* init_statusbar ()
{
    main_statusbar = GTK_STATUSBAR (gtk_statusbar_new ());
    status_context_id = gtk_statusbar_get_context_id (main_statusbar, "Link Hover");

    return (GtkWidget*)main_statusbar;
}

//create toolbar inside window
static GtkWidget* init_toolbar ()
{
  toolbar = gtk_toolbar_new ();

  //position the toolbar
    gtk_toolbar_set_orientation (GTK_TOOLBAR (toolbar), GTK_ORIENTATION_HORIZONTAL);
    gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);

    GtkToolItem* item;

    //back button -> get image from GTK stock, connect to signal of clicked
    item = gtk_tool_button_new_from_stock (GTK_STOCK_GO_BACK);
    g_signal_connect (G_OBJECT (item), "clicked", G_CALLBACK (btn_backward_click), NULL);
    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

    //forward button -> get image from GTK stock, connect to signal of clicked
    item = gtk_tool_button_new_from_stock (GTK_STOCK_GO_FORWARD);
    g_signal_connect (G_OBJECT (item), "clicked", G_CALLBACK (btn_forward_click), NULL);
    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

    //url text box
    item = gtk_tool_item_new ();
    gtk_tool_item_set_expand (item, TRUE);
    uri_entry = gtk_entry_new ();
    gtk_entry_set_text(uri_entry,"https://www.google.co.il");
    gtk_container_add (GTK_CONTAINER (item), uri_entry);
    g_signal_connect (G_OBJECT (uri_entry), "activate", G_CALLBACK (webkit_load), NULL);
    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

    //load button -> get image from GTK stock, connect to signal of clicked
    item = gtk_tool_button_new_from_stock (GTK_STOCK_OK);
    g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (webkit_load), (gpointer)uri_entry);
    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

    return toolbar;
}

static GtkWidget* init_window ()
{
  //create main window
    GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
    gtk_widget_set_name (window, "Webkit");

    //connect to destroy signal
    g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_destory), NULL);

    return window;
}

 int main (int argc, char *argv[])
 {

gtk_init (&argc, &argv);

vbox = gtk_vbox_new (FALSE, 0);

//set containers in instance
gtk_box_pack_start (GTK_BOX (vbox), init_toolbar (), FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), init_browser (), TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), init_statusbar (), FALSE, FALSE, 0);

//create main window add as container to instance
main_window = init_window ();
gtk_container_add (GTK_CONTAINER (main_window), vbox);

gchar* uri = "http://";

webkit_web_view_open (web_view, uri);

gtk_widget_grab_focus (GTK_WIDGET (web_view));

//show window
gtk_widget_show_all (main_window);
gtk_main ();

    return 0;
 }




בפיתוח ב .Net אפשר להשתמש ב Webkit . NET שמאוד חברותי, וב Android ניתן לכתוב אפליקציות שלמות על בסיס ה Webkit שמגיע במערכת ההפעלה כמו שהסברתי באחד המאמרים בבלוג.

סיכום:

פרוייקט ה Webkit בהחלט הצליח והיכולת לעבוד על כל פלטפורמה הפכה אותו לנפוץ ופופולרי, הרבה אפליקציות נעזרות בו והוא עשה סוג של מהפכה בעולם ה Gui שמאפשר להשתמש ב Webkit כשכבה עיצובית בפרויקטים שלנו.

קישורים:
GTK project

WebKit

תמשיכו לדפדף.