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…