Bu sitedeki yazıların tamamı F. Oğuz ÖZKEROĞLU isimli şahıs tarafından uydurulmuş bilgilerden oluşmaktadır.

İçeriği kullanmak için kimseden izin alınmasına gerek yoktur.

Şubat 10, 2012

android, java

4 yorum

İçerik: Android üzerinden webservislere erişim, gelen yanıtın parse edilmesi ve kullanılması.
Bu seferki yazım bir yıldan fazla zamandır sıkça kullandığım, Android üzerinden SOAP [0] webservislerine bağlanma ve kullanma konusunda kolaylıklar sağlayan ksoap2 [1] hakkında olacak. E-mail doğrulaması yapan public bir webservisi kullanan örnek bir uygulama üzerinden gideceğim.

Kullanacağım webservisi basit göründüğü için aşağıdaki olarak seçtim (google’da karşıma ilk çıkanlardan biri):

http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx?op=VerifyEmail

Metodların tam listesi:

http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx

Ve servis tanımlaması:

http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx?WSDL

Kullanacağımız metoddan bahsedecek olursak, bir email ve lisans anahtarını parametre olarak alıyor, arka planda kontrolünü yapıyor ve işlem hakkında 4 farklı bilgiyi bize veriyor (ResponseText, ResponseCode, LastMailServer, GoodEmail). Yapmak istediğim uygulama da verdiğim email ve lisans anahtarı bilgilerini bu servise gönderen ve gelen yanıtı aldıktan sonra parse edip, her bir bilgiyi ekranda ayrı ayrı gösterecek.

Servisi test etmek için url’de bir arayüz var ama böyle bir arayüze sahip olmayan servisler için test aracı olarak soapUI [2] ya da SOAPSonar [3] kullanılabilir. SOAPSonar Personal Edition’da test ettiğimde gönderilen istek aşağıdaki gibi oldu:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
			   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			   xmlns:s="http://www.w3.org/2001/XMLSchema"
			   xmlns:tns="http://ws.cdyne.com/">
  <soap:Body>
    <tns:VerifyEmail>
      <tns:email>oguzozkeroglu@hotmail.com</tns:email>
      <tns:LicenseKey>test</tns:LicenseKey>
    </tns:VerifyEmail>
  </soap:Body>
</soap:Envelope>

Aldığım yanıt ise aşağıdaki gibi:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
			   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <VerifyEmailResponse xmlns="http://ws.cdyne.com/">
      <VerifyEmailResult>
        <ResponseText>Verified Email Address</ResponseText>
        <ResponseCode>2</ResponseCode>
        <LastMailServer>mx2.hotmail.com</LastMailServer>
        <GoodEmail>true</GoodEmail>
      </VerifyEmailResult>
    </VerifyEmailResponse>
  </soap:Body>
</soap:Envelope>

Kullanmadığım mail adresimin verified olmasını görmek sevindirdi açıkçası :) Şimdi aynı işlemleri bir Android projesi ile yapalım ve gelen yanıtı anlamlı hale getirip XML taglari arasındaki bilgileri birbirinden ayırıp ekranda gösterelim.

Öncelikle Eclipse’te bir Android projesi yaratalım. Kullanacağımız hiçbir metod ve özellik android-4 üzeri bir sürüm gerektirmiyor, o yüzden minimum ve target sdk seçenekleri 1.6 tutulabilir (en yenisi olsun diye kasmaya gerek yok :)

Yapacağımız ilk iş Manifest dosyasına internete erişim iznini eklemek olursa sonradan gerçekleşmesi muhtemel bir hatayı (java.net.SocketException) en baştan önlemiş oluruz. Eğer uygulamamız internet ile iletişime geçecekse aşağıdaki izni Manifest dosyamıza eklemiş olmamız gerekiyor.

<uses-permission android:name="android.permission.INTERNET" />

Şimdi de gelen yanıttaki bilgileri tutacağımız nesne için bir class yazalım. Gelen XML’deki ada göre bir isimlendirme yaptım, o kısım için daha kolay akılda kalacak ya da daha anlamlı bir isim seçilebilir.

public class VerifyEmailResult {
	private String responseText;
	private int responseCode;
	private String lastMailServer;
	private boolean goodEmail;

	public VerifyEmailResult() {
		super();
	}

	public VerifyEmailResult(String responseText, int responseCode,
			String lastMailServer, boolean goodEmail) {
		super();
		this.responseText = responseText;
		this.responseCode = responseCode;
		this.lastMailServer = lastMailServer;
		this.goodEmail = goodEmail;
	}

	public String getResponseText() {
		return responseText;
	}

	public void setResponseText(String responseText) {
		this.responseText = responseText;
	}

	public int getResponseCode() {
		return responseCode;
	}

	public void setResponseCode(int responseCode) {
		this.responseCode = responseCode;
	}

	public String getLastMailServer() {
		return lastMailServer;
	}

	public void setLastMailServer(String lastMailServer) {
		this.lastMailServer = lastMailServer;
	}

	public boolean isGoodEmail() {
		return goodEmail;
	}

	public void setGoodEmail(boolean goodEmail) {
		this.goodEmail = goodEmail;
	}

	@Override
	public String toString() {
		return "VerifyEmailResult [responseText=" + responseText
				+ ", responseCode=" + responseCode + ", lastMailServer="
				+ lastMailServer + ", goodEmail=" + goodEmail + "]";
	}
}

Daha sonra bu classtan oluşturduğumuz nesnenin içini dolduracak, aslında yapmamız gereken işin çoğunu yapacak (webservis ile bağlantı kurup, parametrelerimizi karşıya gönderecek, ordan yanıt bekleyip aldığı yanıtı parse edip sonrasında bilgileri istediğimiz formatta bize verecek) olan classı yazalım.

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

public class EmailValidator {
	private static final String METHOD_NAME = "VerifyEmail";
	private static final String NAMESPACE = "http://ws.cdyne.com/";
	private static final String SOAP_ACTION = "http://ws.cdyne.com/VerifyEmail";
	private static final String URL = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx";

	public static VerifyEmailResult verifyEmail (String eMail, String licenseKey) {
		VerifyEmailResult object = new VerifyEmailResult();

		SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
		request.addProperty("email", eMail);
		request.addProperty("LicenseKey", licenseKey);

		SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
		envelope.dotNet = true;
		envelope.setOutputSoapObject(request);

		HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
		androidHttpTransport.debug = true;

		try {
			androidHttpTransport.call(SOAP_ACTION, envelope);
			SoapObject response = (SoapObject) envelope.getResponse();

			if (response.hasProperty("ResponseText")) {
				if (response.getPropertyAsString("ResponseText") == null) {
					object.setResponseText(null);
				} else {
					object.setResponseText(response.getPropertyAsString("ResponseText"));
				}
			}

			if (response.hasProperty("ResponseCode")) {
				if (response.getPropertyAsString("ResponseCode") == null) {
					object.setResponseCode(-1);
				} else {
					object.setResponseCode(Integer.parseInt(response.getPropertyAsString("ResponseCode")));
				}
			}

			if (response.hasProperty("LastMailServer")) {
				if (response.getPropertyAsString("LastMailServer") == null) {
					object.setLastMailServer(null);
				} else {
					object.setLastMailServer(response.getPropertyAsString("LastMailServer"));
				}
			}

			if (response.hasProperty("GoodEmail")) {
				if (response.getPropertyAsString("GoodEmail") == null) {
					object.setGoodEmail(false);
				} else {
					object.setGoodEmail(Boolean.parseBoolean(response.getPropertyAsString("GoodEmail")));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return object;
	}
}

Bu classı yazdığımızda IDE org.ksoap2.* importlarını yapamadığı için uyarı verecektir çünkü standart Java ve Android SDK içinde o classlar bulunmamaktadır. Peki şimdi ne yapmak gerekir? Yazının konusu olan ksoap2 kütüphanesini indirip [4] projemize eklemeliyiz. Benim indirip kullandığım dosyanın ismi: ksoap2-android-assembly-2.6.0-jar-with-dependencies.jar. JAR dosyasını projeme eklemek için de kullandığım yöntem: Proje klasörü içinde libs isminde bir klasör oluşturup projeye eklemek istediğim jar dosyalarını oraya attıktan sonra Eclipse Package Explorer’da proje üzerinde sağ click -> Properties -> Java Build Path -> Libraries -> Add JARs -> sonrasında da klasörün içindeki istenilen JAR dosyalarını eklenmesi..

Bu işlemler sıkıntısız şekilde tamamlandıktan sonra projede herhangi bir hata kalmaması gerekiyor.

Şimdi yukardaki kodda ne yaptığımıza gelelim.

	private static final String METHOD_NAME = "VerifyEmail";
	private static final String NAMESPACE = "http://ws.cdyne.com/";
	private static final String SOAP_ACTION = "http://ws.cdyne.com/VerifyEmail";
	private static final String URL = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx";

Burda metod ismi, namespace, soap action ve servisin bulunduğu adresi belirttik.

VerifyEmailResult object = new VerifyEmailResult();

Parse edilen verileri tutacağımız geçici nesnemizi yarattık.

		SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
		request.addProperty("email", eMail);
		request.addProperty("LicenseKey", licenseKey);

		SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
		envelope.dotNet = true;
		envelope.setOutputSoapObject(request);

		HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
		androidHttpTransport.debug = true;

Burda da servise yapacağımız isteği oluşturup parametreleri ekliyoruz. Sonrasında SOAP envelope nesnesini oluşturup, bir .net servisini çağıracağımızı belirtiyoruz.

			androidHttpTransport.call(SOAP_ACTION, envelope);
			SoapObject response = (SoapObject) envelope.getResponse();

Burda servisi çağırıp gelen yanıtı SoapObject türünden bir nesneye atıyoruz.

XML’den farklı olarak ksoap2 nesneleri kendine özel bir formatta tutulur ve bunlar üzerinde işlem yapılır. Örneğin yukarıda XML halini yazmış olduğum isteğe karşılık gelen bizim request nesnemizin içeriği aşağıdaki gibi olacaktır:

VerifyEmail{
    email=oguzozkeroglu@hotmail.com;
    LicenseKey=test;
}

Gelen yanıta karşılık gelen response nesnemizin içeriği de aşağıdaki gibi olacaktır:

anyType{
    ResponseText=Verified Email Address;
    ResponseCode=2;
    LastMailServer=mx4.hotmail.com;
    GoodEmail=true;
}

Bu format parse işlemlerinde kolaylık sağlıyor. anyType XML’deki root’a yani VerifyEmailResult‘a karşılık geliyor gibi düşünebiliriz. Diğerleri zaten isminden anlaşılıyor. Herhangi bir özelliğe erişmek için

response.getPropertyAsString("OzellikIsmi");

kullanılabilir. (response.getProperty() ise geri bir String değil SoapObject döndüreceği için ekranda görüntülemede ya da nesnenin özelliklerini atamada sıkıntı çıkarabilir. Özelliğe o nesneden ulaşmak istediğimizde ise aşağıdaki gibi bir yapı kullanabiliriz.

String.valueOf(response.getProperty("OzellikIsmi"));

ksoap2′nin 2.5.8 sürümüne kadar böyle erişiliyordu. Parse işlemi için response nesnemizin öyle bir özelliği varsa ve null değilse kontrollerinden sonra atama yapıp en son nesneyi geri döndürüyoruz.

			if (response.hasProperty("ResponseText")) {
				if (response.getPropertyAsString("ResponseText") == null) {
					object.setResponseText(null);
				} else {
					object.setResponseText(response.getPropertyAsString("ResponseText"));
				}
			}
            /** ... */

Bu iki classı yazdıktan sonra ana sayfaya ait olan layout sayfasına (XML), bir adet Button ve 4 adet TextView ekledim. İşimiz tasarımla değil arka plandaki işlerle olduğu için tasarıma pek takılmayalım, dandik durduğunun farkındayım :)



Artık elimizdeki malzemeleri kullanmanın zamanı geldi. Ana sayfada aşağıdaki global tanımlamaları ekledim

	VerifyEmailResult object = null;
	final String eMail = "oguzozkeroglu@hotmail.com";
	final String licenseKey = "test";
    mThread thread = null;

Butonu tanımlayıp işlevsellik kazandıralım

		Button btnInvokeService = (Button) findViewById(R.id.btnInvoke);
		btnInvokeService.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				thread = new mThread();
				thread.start();
			}
		});

Butona basındığında tanımladığımız thread çalışacak. Peki thread ne yapacak?

		private class mThread extends Thread {
		@Override
		public void run() {
			object = EmailValidator.verifyEmail(eMail, licenseKey);
			handler.sendEmptyMessage(0);
		}

		private Handler handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				thread.interrupt();
				setTexts();
			}
		};
	}

Çağırdığımızda EmailValidator classımızın verifyEmail metodunu çağıracak ve işlem bittikten sonra setTexts metodu çağrılacak. O metod ne yapacak?

	private void setTexts() {
		TextView tv1 = (TextView) findViewById(R.id.tv1);
		TextView tv2 = (TextView) findViewById(R.id.tv2);
		TextView tv3 = (TextView) findViewById(R.id.tv3);
		TextView tv4 = (TextView) findViewById(R.id.tv4);

		tv1.setText("ResponseText   ->" + object.getResponseText());
		tv2.setText("ResponseCode   ->" + object.getResponseCode());
		tv3.setText("LastMailServer ->" + object.getLastMailServer());
		tv4.setText("GoodEmail      ->" + object.isGoodEmail());
	}

Basit bir şekilde servisten dönen bilgileri oluşturduğumuz TextView‘lara atayacak ve ekranda görünmesini sağlayacak. Butona basıldıktan kısa bir süre sonra aşağıdakine benzer bir şeyler ortaya çıkmalı:



Orada görünen ResponseText ve ResponseCode listesinin tamamını metodlardan biri veriyor. Ben çıktıyı buraya da yazayım.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
			   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <ReturnCodesResponse xmlns="http://ws.cdyne.com/">
      <ReturnCodesResult>
        <anyType xsi:type="xsd:string">0-Invalid Email Address</anyType>
        <anyType xsi:type="xsd:string">1-Not Used</anyType>
        <anyType xsi:type="xsd:string">2-Verified Email Address (Treat as a good email)</anyType>
        <anyType xsi:type="xsd:string">3-Mail Server will accept email (Treat as a good email)</anyType>
        <anyType xsi:type="xsd:string">4-User not found</anyType>
        <anyType xsi:type="xsd:string">5-Email Domain not found</anyType>
        <anyType xsi:type="xsd:string">6-Not Used</anyType>
        <anyType xsi:type="xsd:string">7-SMTP/Timeout Error (Treat as a good email)</anyType>
        <anyType xsi:type="xsd:string">8-Domain valid. Email server(s) down</anyType>
        <anyType xsi:type="xsd:string">9-License Key allowance exceeded</anyType>
      </ReturnCodesResult>
    </ReturnCodesResponse>
  </soap:Body>
</soap:Envelope>

Umarım yararlı bir yazı olmuştur. Kullandığım yöntemler bu işi yapmanın en iyi yöntemleri olmayabilir, araçlar ve kullandığım terimler doğru olmayabilir ama bu haliyle benim işimi gördüler bugüne kadar. Herhangi bir eleştiri / düzeltme / ekleme yapmak isteyen olursa konuya yorum bırakabilir ya da mail atabilir.


*0 -> http://en.wikipedia.org/wiki/SOAP
*1 -> http://code.google.com/p/ksoap2-android/
*2 -> http://www.soapui.org/
*3 -> http://www.crosschecknet.com/products/soapsonar.php
*4 -> http://code.google.com/p/ksoap2-android/wiki/HowToUse?tm=2

Aralık 14, 2011

android

(Yorum yapılmamış)

Android cihazın wifi durumuna erişip, ağa bağlı mı değil mi kontrolü yapan ve wifi durumunu etkinliştirip / devre dışı bırakan basit bir sayfa için aşağıdaki metodlar kullanılabilir. Metodların çalışabilmesi için wifi durumuna erişim ve wifi durumunu değiştirebilme izinlerini manifest dosyasına eklemek gerekir.

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

WifiManager’dan bir nesne oluşturup, bu nesnenin özelliklerini ve metodlarını kullanacağız.

WifiManager wifi = null;

Nesneyi global tanımlamakta fayda var. İki metod içerisinde de bu nesne kullanılacak.
Aşağıdaki metod çağrıldığında önce wifi durumu kontrol ediliyor. Eğer aktif ise önce devre dışı bırakılıyor, sonrasında kullanıcıya wifi’ın devre dışı bırakıldığına dair bir uyarı mesajı gösteriliyor. Eğer devre dışı bırakılıyorsa sadece kullanıcıya wifi’ın devre dışı bırakılıyor olduğuna dair bir mesaj gösteriliyor. Diğer durumlarda ise -örneğin wifi zaten devre dışı ise- hiçbir iş yapılmıyor.

private void disableWifi() {
		try {
			wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
			if (wifi.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
				wifi.setWifiEnabled(false);
				showNotification("Wifi is disabled!");
			} else if (wifi.getWifiState() == WifiManager.WIFI_STATE_DISABLING){
				showNotification("Wifi is disabling!");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

Aşağıdaki metod da yukarıdakinin tam tersi işleri yapıyor.

private void enableWifi() {
		try {
			wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
			if (wifi.getWifiState() == WifiManager.WIFI_STATE_DISABLED) {
				wifi.setWifiEnabled(true);
				showNotification("Wifi is enabled!");
			} else if (wifi.getWifiState() == WifiManager.WIFI_STATE_ENABLING){
				showNotification("Wifi is enabling!");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

Metodlarda kullanılan showNotification() metodu ise sadece parametre olarak aldığı stringi Toast mesajı olarak ekranda gösteriyor. İçeriği de aşağıdaki gibi:

private void showNotification(String message) {
		Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
	}

Java :: Email Regex           

Üzerinde çalıştığım bir Android projesinde EditText alanına girilen e-mail değerinin gerçekten de bir e-mail adresi mi olduğunun kontrol edilmesi gerekiyordu. Bunun için aşağıdaki kodu bulup kullandım.

boolean isEmailValid(String email) {
		String expression = "^[\\w\\-]([\\.\\w])+[\\w]+@([\\w\\-]+\\.)+[A-Z]{2,4}$";
		CharSequence inputStr = email;

		Pattern pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(inputStr);
		return matcher.matches();
	}

Girilen tüm değerleri doğru test ediyor mu bilemem ama benim işimi görecek kadar iyi çalışıyor. Aşağıdaki kod bloğu ile test ettiğimde;

		ArrayList<String> addresses = new ArrayList<String>();
		addresses.add(" ");
		addresses.add("test");
		addresses.add("test.com");
		addresses.add("test@com");
		addresses.add("test@test.com");
		addresses.add("test@test.com.");
		addresses.add("test@test.com.tr");
		addresses.add("test.test@test.com.tr");
		addresses.add("test@türkçe.karakter.tr");

		for (int i = 0; i < addresses.size(); i++) {
			System.out.println(addresses.get(i) + " :: "
					+ isEmailValid(addresses.get(i)));
		}

şu aşağıdaki çıktıyı verdi:
:: false
test :: false
test.com :: false
test@com :: false
test@test.com :: true
test@test.com. :: false
test@test.com.tr :: true
test.test@test.com.tr :: true
test@türkçe.karakter.tr :: false

Yeniden anlamış olduk ki regular expressions hayat kurtarır :)
Kaynak: zparacha.com

İlki bir önceki yazım olan ve yalnızca 2 yazıdan oluşan muhteşem yazı dizime (Eclipse & Android SDK kullanırken karşılaştığım hatalar) kaldığım yerden devam ediyorum. Bu sefer uygulamayı cihaz üzerinde çalıştırmak istediğimde aldığım hata ve google amcanın yardımıyla kavuştuğum çözümden bahsedeceğim. Hata aşağıdaki gibi:
Android Launch!
adb is running normally.
Performing com.test.Splash activity launch
Uploading Test.apk onto device 'HT9A4LV01328'
Failed to install Test.apk on device 'HT9A4LV01328': timeout
Launch canceled!

ADB çalışıyor, upload tamam ama uygulama yüklenemeden timeout’a düşüyor. Çözüm için de Eclipse -> Preferences -> Android -> DDMS -> ADB connection time out (ms) yolunu izliyoruz ve orda default 5.000 olarak gelen değeri biraz artırıyoruz. (misal 10.000, yetmezse 15.000 vs)
Eclipse adb connection timeout error
Yeniden denediğimizde Console çıktısını aşağıdaki gibi görüyorsak olay tamamdır.
Android Launch!
adb is running normally.
Performing com.test.Splash activity launch
Uploading Test.apk onto device 'HT9A4LV01328'
Installing Test.apk...
Success!
Starting activity com.test.Splash on device HT9A4LV01328

Android SDK ve Eclipse ADT Plugin’ini güncelledikten sonra daha önce yaratmış olduğum AVD’lerden bir tanesini çalıştırmak istediğimde aşağıdaki gibi bir hata aldım.
invalid command-line parameter: Files.
Hint: use '@foo' to launch a virtual device named 'foo'.
please use -help for more information

Eclipse avd error

Google amcaya sordum “n’oluyoruz la?” diye, aynı problemle karşılaşmış insanlar gösterdi bana. Meğerse güncellemeden sonra Eclipse artizlik yapıp Windows’un boşluk, parantez vs karakterleri içeren dosya yollarını beğenmiyormuş. O güne kadar sorunsuz çalışan kısım, yani Eclipse -> Window -> Preferences -> Android -> SDK Location kısmında yazılı olan
C:\Program Files (x86)\Android\android-sdk

artık çalışmayacakmış.
Eclipse AVD error
“E çözüm neymiş peki?” diye baktığımda da o yolu şu aşağıdakilerden biri ile değiştirmek olduğunu öğrendim.
C:\PROGRA~1\Android\android-sdk
C:\PROGRA~2\Android\android-sdk

İlki şuna;
C:\Program Files\Android\android-sdk

ikincisi de şuna;
C:\Program Files (x86)\Android\android-sdk

ilaçmış. İlgililere duyrulur.

Haziran 26, 2011

android, java

(Yorum yapılmamış)

Android’in veri saklama yöntemleri ile alakalı daha önce Android :: Shared Preferences yazısını yazmıştım. O listedeki external storage cihazın harici hafızasına, yani şimdilik sd karta tekabül ediyor.

Üzerinde çalıştığım projelerin birinde mp3 dosyalarını saklamam gerekiyordu. Sayısı birden fazla ve her birinin ortalama boyutu 10 mb civarında mp3 dosyalarını dahili hafızaya ya da uygulama paketi içinde bir yerlere yazsam kısa süre sonra cihaz kullanılmaz hale gelebilir. Bu durumda çözüm olarak dosyaları sd kart üzerine yazmaya karar verdim (Umarım doğru bir karar vermişimdir :)

Başta direkt sd kart üzerine yazıp uygulamayı çalıştırdım ama kısa süre sonra kart çöplüğe döndü. Her şeyi ana dizin içine atıyordum. İşte tam burda kart üzerinde bir klasör oluşturma ve dosyaları oraya yazma / oradan okuma zorunda kaldım. Bu yazıda da bu olayı nasıl yaptığımı anlatmaya çalışacağım.

Bunun için Java’nın “File” class’ından bir obje yaratıp, parametre olarak yol + klasör ismi verdim ve sonrasında file.mkdir() metodu ile klasörü oluşturdum. Kod aşağıdaki gibi:

	File mFile = new File("/sdcard/altKlasor");
	mFile.mkdir();

Tabi bu kodun çalışması için yine her zamanki gibi AndroidManifest.xml dosyasına harici hafızaya yazma iznini eklememiz gerekiyor.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Bu sayede direkt sd kart altında klasörü oluşturduk. Aşağıda Eclipse’teki File Explorer’ın kod çalışmadan önceki ve sonraki hali var.

Önce:

Sonra:

Kodu çalıştırdık işimizi gördük ama burda ufak bir sıkıntı var. Sd kartın yolunu elle girdik. İlerde yolun değişebilmesi ya da sd kart yerine başka bir harici hafıza kullanılabilmesi durumlarına karşılık daha iyi bir yöntem kullanmak gerekti. Bunun için de harici hafızanın direkt yolunu alan aşağıdaki kodu buldum.

	String extStorageDirectory = Environment.getExternalStorageDirectory().toString();

Bu şekilde biraz daha bağımsızlık ve esneklik kazanmış olduk. Dosyayı oluşturan kodun tamamı aşağıdaki gibi şekillendi.

	String strNewFolder = "/altKlasor";
	String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
	File mFile = new File(extStorageDirectory + strNewFolder);
	mFile.mkdir();

Belki birinin daha işine yarar..

Mayıs 15, 2011

android, java

13 yorum

WebView içinde bir web sayfasını göstermek için aşağıdaki kod kullanılıyor.

	WebView wv;
	wv = (WebView) findViewById(R.id.webview);
	wv.loadUrl("http://www.google.com");

Tabi bunu kullanabilmek için uygulamamıza internete erişim iznini vermemiz gerekiyor. Bunun için de AndroidManifest.xml dosyası içinde uygun yere aşağıdaki satırı ekliyoruz.

<uses-permission android:name="android.permission.INTERNET" />

Çalıştırdığımızda aşağıdaki gibi bir görüntü elde ediyoruz.

Buraya kadar bir sıkıntı yok zaten bilinen şeyler. Peki kendi hazırladığımız bir html sayfasını nasıl gösteririz?

Bunun için html içeriğimizi oluşturup bir stringe atıyoruz ve WebView içinde o stringi gösteriyoruz.

String strHtml = "<html><head></head>"
				+ "<body text=\"#444444\" bgcolor=\"#CCCCCC\">"
				+ "<br /><br /><center><h2>" + "Static html content in Android"
				+ "</h2></center></body></html>";

		wv.loadData(strHtml, "text/html", "UTF-8");

Artık internet erişimine gerek kalmadığı için Manifest dosyamızdan o izni kaldırabiliriz. Bu string içinde CSS de kullanılabilir. Bunlar yeterli değil, JavaScript de olsun diyorsanız aşağıdaki satırı WebView tanımlamasında sonra eklemeniz gerekir.

wv.getSettings().setJavaScriptEnabled(true);

Örnek uygulamada kullandığım java dosyası:

package com.oguz.example;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.WebView;

public class WebViewTest extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(
				WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
		setContentView(R.layout.webwiew_test);

		WebView wv = (WebView) findViewById(R.id.webkitWebView1);

		String strHtml = "<html><head></head>"
				+ "<body text=\"#444444\" bgcolor=\"#CCCCCC\">"
				+ "<br /><br /><center><h2>"
				+ "Static html content in Android</h2>"
				+ "<script type=\"text/javascript\">"
				+ "document.write(Date());</script>"
				+ "</center></body></html>";

		wv.getSettings().setJavaScriptEnabled(true);
		wv.loadData(strHtml, "text/html", "UTF-8");
	}
}

XML dosyası ise:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<android.webkit.WebView android:id="@+id/webkitWebView1"
		android:layout_width="fill_parent"
		android:layout_height="fill_parent">
	</android.webkit.WebView>
</LinearLayout>

Nisan 12, 2011

android, java

2 yorum

Android’in iPhone’dan farklı olarak menü tuşuna sahip olması ve bu menü tuşunun işlevini uygulamamız için özelleştirebiliyor olmamız bayağı faydalı bir şey. Yine bir önceki yazımda olduğu gibi projem üzerinde çalışırken kullanmak zorunda kaldığım options menü ve alt menülerden bahsedeceğim.

Peki nedir bu options menü?


Şu yukardaki görselde de görüldüğü üzere cihazın “menu” tuşuna basıldığında aşağıdan açılan menüdür. Seçenek sayısı 6 taneye kadarsa hepsi orda görünür, 6′dan fazla ise 6. seçenek “more” olur ve diğer seçenekleri onu kullanarak görebiliriz. Ayrıca burda görünen menülerin de altında menüler olabilir. Bunlar da submenülerdir. Xml dosyasında tanımlayarak yapmak daha yaygın olsa da ben daha az tercih edilen yöntemle yapacağım. Bunun için eleştiriye açığım tabi.
Öncelikle menünün olmasını istediğimiz Activity’nin onCreateOptionsMenu() metodunu override etmemiz gerekiyor. İçerisine de menü elemanlarını eklememiz lazım. Bunların da alt menülerinin olabileceğini görelim.

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		SubMenu mMenu = menu.addSubMenu("Seçenek 0");

		mMenu.add(1, 11, 0, "Alt seçenek 1");
		mMenu.add(1, 12, 0, "Alt seçenek 2");
		mMenu.add(1, 13, 0, "Alt seçenek 3");
		mMenu.add(1, 14, 0, "Alt seçenek 4");

		menu.add(0, 2, 0, "Seçenek 1");
		menu.add(0, 3, 0, "Seçenek 2");
		menu.add(0, 4, 0, "Seçenek 3");
		menu.add(0, 5, 0, "Seçenek 4");
		menu.add(0, 6, 0, "Seçenek 5");

		return true;
	}

Activity içine yukardaki kodu eklediğimizde onCreateOptionsMenu() metodunu override etmiş oluyoruz ve içine 1 container menü ile 5 tane de seçenek eklemiş oluyoruz. Container menü diye bir terim yok belki de onu ben uydurdum ama işlev olarak benziyorlar çünkü ilk seçenek (Seçenek 0) 4 ayrı alt seçenek içeriyor. Menüye eleman eklerken 4 parametre kullandım. Bunları da aşağıdaki görsel özetliyor.

Uygulamayı bu haliyle çalıştırıp cihazın/emülatörün “menü” tuşuna bastıktan sonra aşağıdakine benzer bir görüntü elde etmemiz gerekir.

Sıfırıncı seçeneği seçtikten sonra da aşağıdaki gibi.

Menümüzü ve alt menüsünü görüntülemede sıkıntı yok. Şimdi bu seçeneklerden herhangi biri seçildiğinde Android’e ne yapması gerektiğini söyleyelim. Bunun için de onOptionsItemSelected() metodunu override etmemiz gerekecek. Aşağıdaki kod ile de bunu yapalım.

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case 11:
			secenek11Tiklandi();
			return true;
		case 12:
			secenek12Tiklandi();
			return true;
		case 13:
			secenek13Tiklandi();
			return true;
		case 14:
			secenek14Tiklandi();
			return true;
		case 2:
			secenek2Tiklandi();
			return true;
		case 3:
			secenek3Tiklandi();
			return true;
		case 4:
			secenek4Tiklandi();
			return true;
		case 5:
			secenek5Tiklandi();
			return true;
		case 6:
			secenek6Tiklandi();
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

Bundan sonra da artık gidip aşağıdaki metodları oluşturmalı ve içeriğini ne yapılmasını istediğimiz şekilde doldurmalıyız:

  • secenek11Tiklandi()
  • secenek12Tiklandi()
  • secenek13Tiklandi()
  • secenek14Tiklandi()
  • secenek2Tiklandi()
  • secenek3Tiklandi()
  • secenek4Tiklandi()
  • secenek5Tiklandi()
  • secenek6Tiklandi()