Smart Mirror için Android Things (Raspberry Pi 3 Model B) Uygulama Kodları
Merhaba Değerli Dostlar,
Akıllı aynamız için web sayfasını geliştirdik. Firebase ortamına faaliyet kaydını yapıp var olan faaliyetlerin listesini almayı sağladık. Bu yazıda Raspberry Pi 3 kartına yani Android Things işletim sistemine yükleyeceğimiz uygulamayı nasıl geliştireceğimizden bahsedeceğim. Umarım faydalı olur.
Burada aşağıda verilen adımları takip ederek Android Things uygulamasını geliştireceğiz:
- Android Studio ortamında yeni proje oluşturmak,
- Firebase için yapılandırma ayarlarını gerçekleştirmek,
- Son olarak Android Things uygulaması için kaynak kodlarını yazmak.
Verilen ilk iki adım oldukça önemli olup bunları yaptıktan sonra kodlarımızı gönül rahatlığıyla yazabiliriz.
Android Studio Ortamında Yeni Proje Oluşturmak
Android Studio ortamını açalım ve yeni bir proje oluşturalım.
Yukarıda açılan pencerede uygulama ismini Smart Miror olarak girelim ve Next butonuna tıklayalım.
Projede üç farklı uygulama geliştireceğiz: Phone and Tablet, Wear ve Android Things.
Phone and Tablet: Akıllı telefonlar için geliştireceğimiz uygulamadır. Amaç akıllı telefonları kullanarak akıllı aynamız için faaliyet kaydını ve var olan faaliyetlerin listesini almayı sağlamaktır.
Wear: Akıllı saatler için geliştireceğimiz uygulamadır. Amaç o gün içinde yapılması gereken bir faaliyet varsa, bunu akıllı saat ile kullanıcıya haber vermektedir.
Android Things: Raspberry Pi 3 Model B yani Android Things işletim sistemi için geliştireceğimiz uygulamadır. Akıllı aynada kullanıcıya gösterilecek olan uygulama burası olup kodlarını bu makalede sizlere aktaracağım.
Phone ve Wear için API seviyesni 23, Android Things için API seviyesini 26 olarak seçelim ve Next butonuna tıklayalım.
Mobile uygulama için etkinliğimizi Empty Activity olarak belirleyelim ve Next butonuna tıklayalım.
Mobile uygulama için Activity Name alanını MainActivity olarak girelim ve Next butonuna tıklayalım.
Wear uygulaması için etkinliğimizi Blank Wear Activity olarak belirleyelim ve Next butonuna tıklayalım.
Wear uygulaması için Activity Name alanını MainActivity olarak girelim ve Next butonuna tıklayalım.
Android Things uygulaması için etkinliğimizi Android Things Activity olarak belirleyelim ve Next butonuna tıklayalım.
Android Things uygulaması için Activity Name alanını MainActivity olarak girelim. Dikkat edilirse her üç uygulama için etkinlik ismini aynı yaptık. Üç uygulama tek bir çatı altında olmasına rağmen bu durum kesinlikle bir karmaşaya sebep olmayacaktır. Tabi isterseniz bu isimlerde değişiklik yapabilirsiniz. Bu son pencerede bulunan Finish butonuna tıklayarak projenizi oluşturabilirsiniz.
Yukarıda görüleceği üzere tek bir projede üç farklı uygulamanın oluşturulmasını sağladık. Şimdi mobile ve things projeleri için Firebase yapılandırma ayarlarını yapalım.
Firebase Kullanımı
Projemizi oluşturduk şimdi mobile ve things projeleri için Firebase yapılandırma ayarlarını yapalım. Burada sadece things projesi için yapılması gerekenleri sizlere aktaracağım aynı işlemleri mobile projesi içinde yapmanız gerekiyor. Hatırlarsanız daha önce web uygulaması için bir Firebase projesi oluşturmuştuk. Burada Web, Android Things ve Mobile uygulamalar arasında veri bütünlüğü sağlamak için aynı Firebase projesini kullanmak zorundayız.
Projemiz aşağıdaki gibiydi.
Smart Mirror projesine tıklayalım. Açılan proje sayfasında aşağıdaki alana gelelim.
Amacımız Android uygulaması ile Firebase arasındaki yapılandırmayı veya iletişimi sağlamaktır. Bundan dolayı Android simgesine tıklayalım.
Açılan pencerede Android paket adını ilgili alana yapıştırınız. Uygulamanızın paket ismini üç projede bulunan herhangi bir AndroidManifest dosyasından alabilirsiniz.
Uygulamayı Kaydet diyerek devam edelim.
Yukarıda Firebase hizmetinden faydalanmak için projenize özel google-services.json isimli bir dosya oluşturulur. Burada uygulamanızın paket ismi ve ihtiyacımız olan key ve id bilgileri bulunmaktadır. Bu dosyayı bilgisayarınıza indiriniz. Android Studio ortamında bulunan things projemize dönelim. Aşağıda görüleceği üzere Proje görünümünü Project olarak ayarlayalım ve indirdiğimiz json dosyasını aşağıda görülen alana yapıştıralım.
Dosyanın things klasöründe olduğundan emin olunuz. Bunu yaptıktan sonra yukarıda verdiğimiz Firebase sayfasındaki Sonraki butonuna tıkladığımızda aşağıdaki sayfaya geçeriz.
Firebase sizlere yapmanız gereken diğer son ayarları listelemektedir. Android Studio ortamında bulunan projemizin görünümünü Android olarak değiştirelim.
Yukarıda işaretli olan build.gradle<project> isimli dosyamızı açalım ve aşağıdaki satırı dependencies bloğuna ekleyelim.
dependencies { … classpath 'com.google.gms:google-services:4.0.0' }
Daha sonra aşağıda gösterilen build.gradle<things> isimli dosyamızı açalım.
Açılan dosyaya aşağıdaki satırları görüldüğü gibi ekleyelim.
dependencies { … compile 'com.google.firebase:firebase-core:16.0.0' compile 'com.google.firebase:firebase-database:16.0.0’ compile 'com.google.firebase:firebase-crash:16.0.0’ compile 'com.google.firebase:firebase-auth:16.0.0’ } apply plugin: 'com.google.gms.google-services'
Bunları ekledikten sonra Build–>Rebuild ile projemizi yeniden oluşturup değişiklikleri projemize uygulayalım.
Tüm bu işlemleri yaptıktan sonra Firebase alanındaki pencereden devam ettiğimizde projemiz aşağıdaki gibi hazırlanmış olur.
Burada yapılan işlemleri mobile uygulama içinde yapmanız gerekiyor. Wear uygulaması verileri mobile uygulama üzerinden alacağı için bu işlemi Wear uygulaması için yapmanıza gerek yok. Tüm bu bilgilerden sonra things uygulaması için ihtiyacımız olan kodlara geçebiliriz.
Android Things Uygulaması
Things uygulamasının kodlarına geçmeden önce uygulamanın arayüzünü sizlere göstermek istiyorum. Aslında Smart Mirror için yazdığım ilk yazıda bu arayüzü sizlere göstermiştik. Burada tekrar gösterip kodlama kısmı için takip edeceğimiz aşamaların daha anlamlı olmasını sağlayacağız.
Uygulama arayüzünde verilerin gösterildiği toplamda üç farklı alan var. Bu alanlara daha yakından bakalım.
Yukarıdaki resim aynamızın hava durumu simgesi, şehir, sıcaklık, saat, tarih ve gün bilgisinin gösterildiği alandır. Bu alanda gösterilen bilgileri aşağıdaki yollar ile temin edeceğiz.
- Hava durumu simgesi, şehir ve sıcaklık verilerini OpenWeatherMap sitesinden alacağız. Bu site hava durumu verilerini alacağınız bir key bilgisi ile JSON formatında vermektedir. JSON verileri ayrıştırmak için Retrofit kütüphanesini kullanacağız. Hava durumu simgesi için ayrıca Picasso kütüphanesini de kullanacağız.
- Saat, tarih ve gün bilgisini ise oluşturacağımız Time sınıfı içindeki bir metot vasıtasıyla alacağız.
Yukarıda gösterilen alanda ise gün içinde kullanıcıya bazı güzel sözler göstermeye çalışacağız. Ayrıca eğer o gün için yapılacak bir etkinlik veya etkinlik grubu varsa bunlarında uyarı olarak gösterilmesi bu alan ile sağlanacaktır. Örneğin aşağıdaki gibi
Burada güzel sözler bir timer ile kullanıcılara gösterilecektir. O günkü bir faaliyetin bilgisi ise Firebase üzerinden alınacaktır.
Bu iki alan dışında diğer son alan ise aynanın en altında yer alan ve bir kullanıcıya ait olan tüm faaliyetlerin sırasıyla listelendiği alandır.
Yukarıda gösterilen resimde aynanın en altında bulunan ve kayıtlı olan tüm faaliyetlerin sırasıyla sıralanmasını sağlayan alandır. Burada gösterilen verileri Firebase üzerinden alacağız.
Son olarak WiFi bağlantısı olmadığı durumlar için kullanıcıya aşağıdaki bilgiyi veren bir kontrol daha ekledik. Bu kontrol wifi bağlantısı olduğu zaman gizli olup kullanıcıya görünmemektedir. Bağlantı kesildiği anda diğer tüm alanlar gizlenip bu kontrol kullanıcıya gösterilir.
Bu ön bilgilendirmeden hemen sonra kodlarımıza geçebiliriz. Kodları yazarken aşağıda verilen işlemleri sırasıyla yapmamız gerekiyor.
- AndroidManifest.xml dosyasına gerekli izinlerin eklenmesi.
- activity_main.xml dosyasının oluşturulması ve arayüz tasarımı.
- Model sınıflarının oluşturulması. Burada Firebase üzerinden alınan faaliyet bilgilerini ayrıştırmak için Note modelini, hava durumu JSON verilerini ayrıştırmak için de WeatherJson modelini yani sınıflarını geliştirdik.
- Saat, tarih ve gün bilgisini veren Time sınıfının oluşturulması.
- Retrofit ve Picasso kütüphanelerinin eklenmesi.
- Hava durumu JSON verilerini url üzerinden almayı sağlayan servisin yazılması. Servis ismi RequestforJsonObject olacaktır.
- Son olarak yukarıda yapılan işlemlerin bir bütün olarak ele alınmasını sağlayan MainActivity isimli sınıfın kodlarının yazılması.
Değerli dostlar buradan itibaren lafı uzatmamak adına kodları açıklamaları ile birlikte veriyorum.
AndroidManifest.xml Dosyasına Gerekli İzinlerin Eklenmesi
Manifest dosyası için aşağıdaki izinleri eklememiz gerekiyor. Özellikle internet erişim izni ile Firebase ortamına bağlanıp veri alınması sağlanacağı için bu izni mutlaka eklemenize gerekiyor.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mas.smartmirror"> <!--İhtiyaç duyulan izinleri ekledik. ACCESS_WIFI_STATE INTERNET SET_TIME--> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="com.google.android.things.permission.SET_TIME"/> <application android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" android:screenOrientation="portrait"> <uses-library android:name="com.google.android.things"/> <!--MainActivity isimli etkinlik, Raspberry Pi 3 açıldığında veya Android Things başladıktan sonra otomatik olarak kullanıcıya gösterilecektir. --> <activity android:name=".MainActivity"> <!--Bu intent filter ile MainActivity uygulamanın ana giriş noktası yapılır. --> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <!--Bu intent filter ile Android Things açıldıktan hemen sonra MainActivity etkinliğinin otomatik olarak başlaması sağlanır.--> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.HOME"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> </application> </manifest>
activity_main.xml Dosyasının Oluşturulması ve Arayüz Tasarımı
Uygulamanın arayüz xml kodları aşağıdaki gibidir.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@android:color/black"> <!--Şehir ve hava durumu --> <TextView android:id="@+id/tvWeather" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginLeft="40dp" android:layout_marginTop="59dp" android:rotation="-90" android:text="Besni 11°C" android:textColor="#fff" android:textSize="25sp" /> <!--OpenWeatherMap'ten alınan hava durumu simgesi --> <ImageView android:id="@+id/ivWeather" android:layout_width="90dip" android:layout_height="90dip" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginTop="35dp" android:rotation="-90" android:src="@drawable/common_google_signin_btn_icon_dark" /> <!--Saat, tarih ve gün bilgisini gösterecek kontrol --> <TextView android:id="@+id/tvTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/tvWeather" android:layout_alignBottom="@+id/tvWeather" android:layout_marginStart="19dp" android:layout_toEndOf="@+id/ivWeather" android:gravity="right" android:rotation="-90" android:text="10:00 am" android:textColor="#fff" android:textSize="25sp" /> <!--Gündelik hoş mesajları veya o günkü faaliyetleri kullanıcıya gösterecek kontrol --> <TextView android:id="@+id/tvMessage" android:layout_width="250dp" android:layout_height="400dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:gravity="center" android:rotation="-90" android:singleLine="false" android:text="TextView" android:textColor="#fff" android:textSize="30sp" /> <!--Raspberry Pi 3 herhangi bir ağa bağlı olmadığında ekranda sadece bu kontrol gösterilir. Başlangıçta kullanıcıya gösterilmez.--> <TextView android:id="@+id/tvWifi" android:layout_width="250dp" android:layout_height="400dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:gravity="center" android:rotation="-90" android:visibility="invisible" android:singleLine="false" android:text="WiFi bulunamadı" android:textColor="#fff" android:textSize="30sp" /> <!--Kayıtlı tüm faaliyetlerin listelendiği kontrol --> <TextView android:id="@+id/tvActivities" android:layout_width="250dp" android:layout_height="400dp" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:gravity="center" android:rotation="-90" android:singleLine="false" android:text="Araştırma" android:textSize="25sp" /> </RelativeLayout>
Model Sınıflarının Oluşturulması
Things uygulaması için model sınıfları ve temel kullanım amaçları aşağıdaki gibidir:
- Note: Firebase veritabanından alınan faaliyetlerin tarih, saat, başlık ve açıklama olarak ayrıştırılmasını sağlayan modeldir.
- WeatherJson: OpenWeatherMap üzerinden alınan ve hava durumu bilgilerini bizlere sunan JSON verisini ayrıştırmak için kullanacağımız modeldir.
Note.java
package com.mas.smartmirror.models; /** * Created by DroidQ on 7.03.2018. */ public class Note { /*Kayıtlı olan faaliyetleri Note sınıfında bulunan aşağıdaki parametrelere ayrıştıracağız.*/ String key; String time; String date; String title; String description; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
WeatherJson.java
Bu sınıf için yazacağımız değişkenlerin birebir aşağıdaki gibi olması gerekiyor. Eğer bir değişiklik yaparsanız Retrofit üzerinden JSON verilerini ayrıştıramazsınız. Önce kodları verelim sonra değişken isimlerini aldığımız JSON verisinin tamamını sizlerle paylaşmak istiyorum.
package com.mas.smartmirror.models; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; import java.util.ArrayList; /** * Created by DroidQ on 29.05.2018. */ /*Hava durumu bilgilerini Retrofit ile ayrıştırmak için aşağıdaki yapıyı birebir kullanmanızı tavsiye ediyorum. Burada verilen name, coord, weather ve diğer tüm değişkenler http://api.openweathermap.org/data/2.5/weather?id=321337&APPID=79a59ac7f017322e3ca1a0939746d83f adresinde bulunan JSON yapısından alınmıştır. Retrofit ile JSON ayrıştırma işleminin başarılı olması için linkte bulunan JSON key bilgileri ile burada tanımlanan değişkenlerin birebir aynı olması gerekiyor.*/ public class WeatherJson { @SerializedName("name") @Expose private String name; @SerializedName("coord") @Expose private Coord coord; @SerializedName("weather") @Expose private ArrayList<Weather> weather =new ArrayList<>(); @SerializedName("main") @Expose private Main main; public Main getMain() { return main; } public void setMain(Main main) { this.main = main; } public ArrayList<Weather> getWeather() { return weather; } public void setWeather(ArrayList<Weather> weather) { this.weather = weather; } public Coord getCoord() { return coord; } public void setCoord(Coord coord) { this.coord = coord; } public String getName() { return name; } public void setName(String name) { this.name = name; } /*JSON verisinde bulunan coord JSON key bilgisi bir dizi olup aşağıdaki key bilgilerine sahiptir.*/ public class Coord { @SerializedName("lon") @Expose private Double lon; @SerializedName("lat") @Expose private Double lat; public Double getLon() { return lon; } public void setLon(Double lon) { this.lon = lon; } public Double getLat() { return lat; } public void setLat(Double lat) { this.lat = lat; } } /*JSON verisinde bulunan weather JSON key bilgisi bir dizi olup aşağıdaki key bilgilerine sahiptir.*/ public class Weather { @SerializedName("id") @Expose private int id; @SerializedName("main") @Expose private String main; @SerializedName("description") @Expose private String description; @SerializedName("icon") @Expose private String icon; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMain() { return main; } public void setMain(String main) { this.main = main; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } } /*JSON verisinde bulunan main JSON key bilgisi bir dizi olup aşağıdaki key bilgilerine sahiptir.*/ public class Main{ @SerializedName("temp") @Expose private Double temp; @SerializedName("pressure") @Expose private Double pressure; @SerializedName("humidity") @Expose private Double humidity; @SerializedName("temp_min") @Expose private Double temp_min; @SerializedName("temp_max") @Expose private Double temp_max; @SerializedName("sea_level") @Expose private Double sea_level; @SerializedName("grnd_level") @Expose private Double grnd_level; public Double getTemp() { return temp; } public void setTemp(Double temp) { this.temp = temp; } public Double getPressure() { return pressure; } public void setPressure(Double pressure) { this.pressure = pressure; } public Double getHumidity() { return humidity; } public void setHumidity(Double humidity) { this.humidity = humidity; } public Double getTemp_min() { return temp_min; } public void setTemp_min(Double temp_min) { this.temp_min = temp_min; } public Double getTemp_max() { return temp_max; } public void setTemp_max(Double temp_max) { this.temp_max = temp_max; } public Double getSea_level() { return sea_level; } public void setSea_level(Double sea_level) { this.sea_level = sea_level; } public Double getGrnd_level() { return grnd_level; } public void setGrnd_level(Double grnd_level) { this.grnd_level = grnd_level; } } }
Hava durumu bilgilerini almak için OpenWeatherMap sitesini kullanacağız. Bu site ücretsiz olarak hava durumu bilgilerini almanızı sağlar. Öncelikle bu siteye üye olmanız gerekiyor.
Yukarıda verilen sayfayı kullanarak sisteme üye olunuz. Daha sonra üye girişi yapınız.
Üye girişi yaptıktan sonra yukarıda görülen resimde yer alan benim için Hello Mehmet (kullanıcı isminiz ne ise burada o görünür) seçeneğine tıklayarak üye bilgileri alanına geçiniz.
Üyelik işlemlerinden API keys sekmesine geliniz. Sağ alt tarafta Create key yazan alana bir isim giriniz ve Generate seçeneğine tıklayınız. Ben daha önce bu işlemi yaptığım için sol tarafta key bilgisi görünmektedir. Bu bilgi hava durumu verilerini alırken oldukça önemlidir.
Şimdi sayfanın üstünde bulunan aşağıdaki alana yöneliniz.
Yukarıda menü seçeneklerinin bir kısmı görünmektedir. Burada bulunan API seçeneğine tıklayınız.
Yukarıda açılan yeni sayfada bulunan Current weather data başlığının hemen altında yer alan API doc seçeneğine basınız.
Açılan yeni sayfada List of city ID alanına geliniz. Hava durumu verisini almak istediğimiz şehir için ID bilgisini öğrenmemiz gerekiyor. Yukarıda kırmızı alanda bulunan linke tıklayınız.
Burada bulunan weather_14.json.gz isimli dosyayı indiriniz. Dosyayı unzip ettikten sonra içinde bir metin dosyası bulunacaktır. Dosyayı açını ve ID bilgisini öğrenmek istediğiniz şehri arayınız.
Burada bize gereken asıl konum Besni olduğu için buranın ID bilgisini öğrenmemiz gerekiyor. Yapılan aramaya göre Besni için ID bilgisi 321337 değeridir. Şimdi API key ve ID bilgisini kullanarak hava durumu bilgisini veren JSON verisine nasıl erişebiliriz ilgili linkimizi nasıl yazacağımızı gösterelim.
http://api.openweathermap.org/data/2.5/weather?id=321337&APPID=79a59ac7f017322e3ca1a0939746d83f |
Evet hava durumu bilgisi için URL bilgisi yukarıdaki gibi olacaktır.
URL içinde bulunan “id=” parametresinden sonra ID bilgisi, “APPID=” parametresinden sonrada API key bilgisi eklenmelidir. Bu URL’yi tarayıcıda çalıştırdıktan sonra hava durumu JSON verisi aşağıdaki gibi bizlere gösterilir.
{ "coord":{ "lon":37.86,"lat":37.69 }, "weather":[{ "id":800,"main":"Clear", "description":"clear sky", "icon":"01n" }], "base":"stations", "main":{ "temp":291.847, "pressure":890.71, "humidity":43, "temp_min":291.847, "temp_max":291.847, "sea_level":1018.53, "grnd_level":890.71 }, "wind":{ "speed":1.51, "deg":226 }, "clouds":{ "all":0 }, "dt":1530386257, "sys":{ "message":0.0022, "country":"TR", "sunrise":1530324622, "sunset":1530377652 }, "id":321337, "name":"Besni", "cod":200 }
Burada verilen JSON key bilgileri ile WeatherJson sınıfında bulunan değişken isimlerinin birebir aynı olduğunu görebilirsiniz. Lütfen inceleyiniz.
Time Sınıfının Oluşturulması
Kullanıcıya göstereceğimiz saat, tarih ve gün bilgisini veren Time sınıfının kodları aşağıdaki gibidir.
package com.mas.smartmirror; import java.util.Calendar; /** * Created by DroidQ on 29.08.2017. */ class Time { /*Gün ve Ay bilgilerini tutan dizilerimiz.*/ private static String[] days = new String[]{"Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi", "Pazar"}; private static String[] months = new String[]{"Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"}; static String getAndSet() { /*getInstance: Belirtilen saat dilimini kullanarak bir takvim alınmasını sağlar.*/ Calendar calendar = Calendar.getInstance(); /*Date: zaman içinde belirli bir anı almak için kullanılır. setTime: takvim için saat ayarlanmasını sağlar.*/ calendar.setTime(new java.util.Date()); /*Calendar.HOUR_OF_DAY: günün o anki saat bilgisini alır. get: verilen calendar alanının değerini döndürür.*/ int hours =calendar.get(Calendar.HOUR_OF_DAY); /*Saat bilgisi 0 ile 9 arasında ise, saat bilgisinin yanına sıfır eklenir. Eğer 10 ve 23 arasında ise, saat bilgisi olduğu gibi kullanıcıya gönderilir.*/ String modifyhours = hours<10 ? "0"+hours : ""+hours; /*Calendar.MINUTE: dakika bilgisini alır.*/ int minutes = calendar.get(Calendar.MINUTE); /*Dakika bilgisi 0 ile 9 arasında ise, dakika bilgisinin yanına sıfır eklenir. Eğer 10 ve 59 arasında ise, dakika bilgisi olduğu gibi kullanıcıya gönderilir.*/ String modifyMinutes = minutes<10 ? "0"+minutes : ""+minutes; /*İçinde olduğumuz ay'ın gün bilgisini alan kodumuz.*/ int monthDay = calendar.get(Calendar.DAY_OF_MONTH); /*Ay bilgisi sayısal olarak alınır. Bu değer 0 ile 11 arasında olur.*/ int month = calendar.get(Calendar.MONTH); /*months dizinde bulunan ay bilgisi alınır ve değişkene atanır.*/ String currentMonth = months[month]; /*Yıl bilgisi alınır*/ int year = calendar.get(Calendar.YEAR); /*Gün bilgisi sayısal olarak alınır. Bu değer 0 ile 6 arasında olur.*/ int day = calendar.get(Calendar.DAY_OF_WEEK); /*days dizinde bulunan gün bilgisi alınır ve değişkene atanır.*/ String currentDay = days[day]; /*Yukarıda alınan veriler tek bir değişkene atanır ve return ile çağrılan yere gönderilir.*/ String time = modifyhours + ":" + modifyMinutes + "\n" + monthDay + " " + currentMonth + " " + year + "\n" + currentDay; return time; } }
Retrofit ve Picasso Kütüphanesinin Eklenmesi
Hava durumu bilgisini almak için Retrofit kütüphanesini kullanacağız. Ayrıca JSON verisinden elde edilen resmi ImageView içinde sorunsuz bir şekilde göstermek için Picasso kütüphanesini de yükleyeceğiz. Retrofit ve Picasso kütüphanelerini eklemek için öncelikle aşağıda gösterilen dosyayı açınız.
Açılan dosyada bulunan dependencies bloğuna aşağıdaki üç satırı ekleyiniz.
dependencies { … compile 'com.squareup.retrofit2:retrofit:2.4.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' implementation 'com.squareup.picasso:picasso:2.71828' }
Bunları ekledikten sonra menüde bulunan Build –> Rebuild yolunu takip ederek uygulamayı yeniden oluşturalım. Bu son işlemle birlikte kütüphaneler uygulamaya yüklenmiş olur.
RequestforJsonObject Servis Sınıfının Yazılması
Bu sınıf bir arayüz olup temel amacı URL bilgisi verilen sayfada yer alan JSON verilerini almayı sağlamaktır. Bu servis ile alınan JSON verilerini MainActivity sınıfında yazacağımız Retrofit kodları ile ayrıştırıp kullanıcıya göstermeyi sağlayacağız.
package com.mas.smartmirror; import com.mas.smartmirror.models.WeatherJson; import retrofit2.Call; import retrofit2.http.GET; /** * Created by DroidQ on 29.05.2018. */ public interface RequestforJsonObject { /*@GET: Bu metot URL bilgisi verilen sunucudan veri almak için kullanılır. Retroft içinde önceden tanımlanan bir yapıdır. Bizim yaptığımız ise, bu yapıya erişmektir. Buraya URL bilgisinin sadece son kısmı eklenecektir. id=321337 değeri Besni için kullanılmaktadır. Siz hangi şehir için verileri almak istiyorsanız o şehirle ilgili id bilgisini öğrenmeniz gerekiyor. Tüm şehirler için ID bilgisini şu linkten alabilirsiniz http://bulk.openweathermap.org/sample/ APPID=79a59ac7f017322e3ca1a0939746d83f burada bulunan API bilgisini ise openweathermap sitesine üye olduktan sonra alabilirsiniz. Bu iki parametre sizin uygulamanızda kesinlikle değişkenlik gösterecektir.*/ @GET("data/2.5/weather?id=321337&APPID=79a59ac7f017322e3ca1a0939746d83f") /*getCityName: Hava durumu bilgilerini almak için bu metodu kullanacağız. WeatherJson ise, sunucudan gelen veriyi saklamak için kullandığımız yapıdır. Bu arayüz ile JSON NESNESİ (YANİ TEK BİR VERİ KÜMESİ) alacağımız için, Call<WeatherJson> tipinde bir metot oluşturduk.*/ Call<WeatherJson> getCityName(); }
MainActivity Sınıfı
Yukarıda yazdığımız tüm sınıf, metot veya kodların tamamının bir araya gelerek belirli bir amaç için kullanılması işlemini bu sınıf içinde yazacağımız kodlar ile gerçekleştireceğiz. Uygulama ilk açıldığı zaman kullanıcıya gösterilecek olan temel sınıfımız budur. Bu sınıfın tüm kodları açıklama satırlarıyla birlikte aşağıdaki gibidir.
package com.mas.smartmirror; import android.app.Activity; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.google.firebase.database.ChildEventListener; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.mas.smartmirror.models.Note; import com.mas.smartmirror.models.WeatherJson; import com.squareup.picasso.Picasso; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends Activity implements ChildEventListener { private FirebaseDatabase mDatabase; private DatabaseReference mReference; private TextView mTvWifi, mTvMsg, mTvActivities, mTvTime, mTvWeather; private ImageView mIvWeather; private static int currentAct = 0; private int i = 0; private static int sizeAct = 0; private String formattedDate; /*Kullanıcıya günlük üç mesaj gösterilecektir. Bu sayıyı arttırabilirsiniz*/ private String[] words = new String[]{ "Merhaba, Nasılsınız?", "Bugün harikasınız!", "Notlarınızı kontrol ediniz!" }; /*activitiesComing: Firebase ortamında kayıtlı olan faaliyetleri tutacak bir liste tanımladık. Yapı olarak Note sınıfını kullandık */ private ArrayList<Note> activitiesComing = new ArrayList<>(); /*HashMap yapısı ile kayıtlı olan faaliyetler key-value çiftleri halinde hash yapısında tutulur.*/ private HashMap<String, Note> meMap = new HashMap<String, Note>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getControls(); getCurrentDate(); getWeather(); mTvTime.setText(Time.getAndSet()); /*Firebase veritabanına erişim sağlanır.*/ mDatabase = FirebaseDatabase.getInstance(); /*Veritabanında bulunan mynotes JSON dizisine erişim sağlanır.*/ mReference = mDatabase.getReference("mynotes"); /*mynotes JSON dizisinde meydana gelen silme, ekleme vb. olayları algılayan bir olay dinleyici tanımladık.*/ mReference.addChildEventListener(this); /*Kayıtlı olan faaliyetleri sırasıyla göstermek için handler ve timer yapılarını kullandık.*/ final Handler mHandlerforActivities = new Handler(); /*Timer ile varsa kayıtlı olan faaliyetler tek tek ve 5 saniye aralıkla kullanıcıya gösterilir.*/ Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { mHandlerforActivities.post(new Runnable() { @Override public void run() { /*checkStateWifi() metodu ile Raspberry Pi 3'ün herhangi bir ağa bağlı olup olmadığı kontrol edilir*/ if (checkStateWifi()) { mTvTime.setVisibility(View.VISIBLE); mTvWeather.setVisibility(View.VISIBLE); mTvActivities.setVisibility(View.VISIBLE); mTvTime.setVisibility(View.VISIBLE); mIvWeather.setVisibility(View.VISIBLE); mTvMsg.setVisibility(View.VISIBLE); /*Eğer ağ bağlantısı varsa mTvWifi kontrolü kullanıcıya gösterilmez.*/ mTvWifi.setVisibility(View.INVISIBLE); /*activitiesComing listesinde bulunan toplam faaliyet sayısı sizeAct değişkenine atanır*/ sizeAct = activitiesComing.size(); /*sizeAct değişkeni sıfır değilse yani listede faaliyet varsa*/ if (sizeAct != 0) { /*currentAct değişkeni -1 değilse*/ if (currentAct == -1) { currentAct = sizeAct - 1; } /*currentAct değişkeni sizeAct değilse*/ if (currentAct == sizeAct) { currentAct = 0; } /*Öncelikle kontroll içeriği temizlenir*/ mTvActivities.setText(""); /*currentAct indis bilgisine sahip olan faaliyet TextView'de gösterilir. activitiesComing listesi Note yapısında bir liste olduğundan dolayı getTitle() ile faaliyetin başlığı, getDescription() ile faaliyetin açıklaması istenir.*/ mTvActivities.setText(activitiesComing.get(currentAct).getTitle() + "\n" + activitiesComing.get(currentAct).getDescription()); /*currentAct indis bilgisine sahip faaliyet mevcut günün tarih bilgisine sahipse, words dizisine ilgili faaliyet hakkında bilgi eklenir. Bu bilgi aşağıda veriln ikinci bir handler ile kullanıcıya gösterilir*/ if (formattedDate.equalsIgnoreCase(activitiesComing.get(currentAct).getDate())) { words[2] = "Bugün saat " + activitiesComing.get(currentAct).getTime() +" da " + activitiesComing.get(currentAct).getTitle()+" var."; } else { words[2] = "Notlarınızı kontrol ediniz!"; } /*Sonraki faaliyete geçmek için currentAct değişkeni bir arttırılır ve hava durumu bilgisi tekrar istenir.*/ currentAct++; getWeather(); } else { /*sizeAct değişkeni sıfır yani kayıtlı herhangi bir faaliyet bulunmuyorsa Faaliyet bulunmadı bilgisi kullanıcıya gösterilir. */ mTvActivities.setText("Faaliyet bulunamadı"); words[2] = "Notlarınızı kontrol ediniz!"; currentAct = 0; } } else { mTvTime.setVisibility(View.INVISIBLE); mTvWeather.setVisibility(View.INVISIBLE); mTvActivities.setVisibility(View.INVISIBLE); mTvTime.setVisibility(View.INVISIBLE); mIvWeather.setVisibility(View.INVISIBLE); mTvMsg.setVisibility(View.INVISIBLE); mTvWifi.setVisibility(View.VISIBLE); } } }); } }, 0, 5000); /*Günlük mesajlar göstermek için handler ve timer yapılarını kullandık.*/ final Handler mHandlerforMessage = new Handler(); Timer timer1 = new Timer(); timer1.schedule(new TimerTask() { @Override public void run() { mHandlerforMessage.post(new Runnable() { @Override public void run() { if (i == -1) { i = words.length - 1; } if (i == words.length) { i = 0; } /*words dizisinde bulunan mesajlar sırasıyla kullanıcıya sunulur.*/ mTvMsg.setText(words[i]); i++; } }); } }, 0, 5000); /*getWiFiIP metodu IP bilgisini öğrenmemizi sağlar. Uygulama açıldığı zaman bu veri kullanıcıya gösterilir.*/ mTvActivities.setText("IP:" + getWiFiIP()); } /*Mevcut günün tarih bilgisi dd.MM.yyyy formatında elde edilir.*/ private void getCurrentDate() { Date c = Calendar.getInstance().getTime(); SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy"); formattedDate = df.format(c); } /*Arayüzde bulunan kontrollere erişimi sağlayan metot*/ private void getControls() { mTvTime = findViewById(R.id.tvTime); mTvActivities = findViewById(R.id.tvActivities); mTvActivities = findViewById(R.id.tvActivities); mTvMsg = findViewById(R.id.tvMessage); mTvWifi = findViewById(R.id.tvWifi); mTvWeather = findViewById(R.id.tvWeather); mIvWeather = findViewById(R.id.ivWeather); } /*Hava Durumu bilgisini alan metot*/ private void getWeather() { /*Hava durumu verisini openweathermap sitesinden aldık. Host Name aşağıdaki gibi olmalıdır.*/ String HOST_NAME = "http://api.openweathermap.org/"; /*Hava durumu verileri openweathermap ortamından JSON olarak alınır. JSON verisini ayrıştırmak için Retrofit kütüphanesinden faydalandık.*/ /*Retroft.Builder(): Retroft nesnesi oluşturmak için kullanılır. baseUrl(): verilerin alınacağı URL bilgisi burada belirtilir. GsonConverterFactory.create(): JSON verilerinin erişilebilir nesneler haline dönüştürmek için kullanılır. Gson: Java nesnelerini JSON haline dönüştürmek için kullanılan bir java kütüphanesidir. */ Retrofit retroft = new Retrofit.Builder() .baseUrl(HOST_NAME) .addConverterFactory(GsonConverterFactory.create()) .build(); /*Yukarıdaki işlemlerden sonra RequestforJsonObject arayüzümüze geçtik. create() metodu ile servisten bir istekte bulunuruz.*/ RequestforJsonObject service = retroft.create(RequestforJsonObject.class); /*Hava durumu bilgileri ayrıntılı olarak alınır.*/ Call<WeatherJson> call = service.getCityName(); /*Bu kısım çok önemlidir. Burada iki adet metot kullanılabilir. enqueue (asynchronously- asenkron): Bu metot verileri asenkron yani eş- zamansız olarak almayı sağlar. execute (synchronously - senkron): Bu metot verileri senkron yani eş zamanlı olarak almayı sağlar. Daha iyi bir performans için asenkron yapıları kullanmak çok önemlidir. Bu metodun içerisinde iki adet metot bulunmaktadır. onResponse(): Sunucudan bir yanıt geldiğinde bu metot otomatik olarak çağrılır. onFailure(): Sunucudan bir yanıt alınmadığı zaman bu metot otomatik olarak çağrılır.*/ call.enqueue(new Callback<WeatherJson>() { @Override public void onResponse(Call<WeatherJson> call, Response<WeatherJson> response) { /*Sıcaklık bilgisi alınır.*/ Double temp = response.body().getMain().getTemp(); /*Alınan sıcaklık bilgisi santigrata çevrilir.*/ int result_temp = (int) (temp - 273); /*Şehir ve sıcaklık bilgisi ilgili kontrole yazılır.*/ mTvWeather.setText(response.body().getName() + " " + result_temp + " °C"); /*Picasso ile hava durumu simgesi ImageView'de gösterilir*/ Picasso.get().load("http://openweathermap.org/img/w/" + response.body().getWeather().get(0).getIcon() + ".png").into(mIvWeather); } @Override public void onFailure(Call<WeatherJson> call, Throwable t) { } }); } /*Raspberry'nin wifi bağlantısı kontrol edilir*/ public boolean checkStateWifi() { ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (mWifi.isConnected()) { return true; } return false; } /*IP bilgisini öğrenmeyi sağlayan metot*/ private String getWiFiIP() { try { WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); int ipAddress = wifiInfo.getIpAddress(); return String.format(Locale.getDefault(), "%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff), (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff)); } catch (Exception ex) { return null; } } /*Firebase ortamından alınan herbir faaliyet Note yapısına ayrıştırılır*/ public Note getNote(DataSnapshot item) { /*Gelen faaliyet bilgisi key ve value bilgisi alınır değişkenlere atanır.*/ String note = item.getValue(String.class); String key = item.getKey(); /*Hem mobil hem de web uygulamasında tarih, saat, başlık ve açıklam verilerini - karakteri ile birleştirdik. Burada - karakteri yardımıyla value kısmının bir dizi olarak parçalanması sağlanır.*/ String[] separated = note.split("-"); /*Dizinin ilk sırasında tarih bilgisi bulunur.*/ String date = separated[0]; /*Dizinin ikinci sırasında zaman bilgisi bulunur.*/ String time = separated[1]; /*Dizinin üçüncü sırasında başlık bilgisi bulunur.*/ String title = separated[2]; /*Dizinin son sırasında açıklama bilgisi bulunur.*/ String description = separated[3]; /*Note sınıfından bir nesne oluşturup yukarıda elde ettiğimiz verileri ilgili parametrelere atadık.*/ Note activity = new Note(); activity.setKey(key); activity.setTime(time); activity.setDate(date); activity.setTitle(title); activity.setDescription(description); /*Son olarak faaliyeti yani Note nesnesini return ile gönderdik.*/ return activity; } /*Firebase ortamında bulunan mynotes JSON dizisine bir veri eklendiğinde bu metot çağrılır.*/ @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { /*Eklenen yeni faaliyet önce map sonra listeye eklenir. */ meMap.put(dataSnapshot.getKey(), getNote(dataSnapshot)); activitiesComing.add(getNote(dataSnapshot)); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { } /*Firebase ortamında bulunan mynotes JSON dizisinden bir veri silindiğinde bu metot çağrılır.*/ @Override public void onChildRemoved(DataSnapshot dataSnapshot) { /*Silinen faaliyet map yapısından silinir.*/ meMap.remove(dataSnapshot.getKey()); /*Daha sonra listede bulunan tüm faaliyetler silinir.*/ activitiesComing.clear(); /*Son olarak map yapısında kalan tüm faaliyetler listeye yeniden eklenir.*/ for (Map.Entry<String, Note> entry : meMap.entrySet()) { activitiesComing.add(entry.getValue()); } } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }
Uygulamayı çalıştırdıktan sonra arayüz aşağıdaki gibi olur.
Sonraki yazıda akıllı telefon uygulamasının kodlarını sizlerle paylaşacağım. İyi çalışmalar diliyorum…