Über Shared Preferences mit einem Service kommunizieren

Für ein Live Wallpaper mit Settings-Dialog brauche ich einen Callback im Service um Änderungen in den Einstellungen so schnell wie möglich  zu verarbeiten. Periodisches Pollen zur Laufzeit ist nicht nur unelegant sondern braucht auch CPU Zeit. Der einfachste Weg für micht geht über SharedPreferences und einem OnSharedPreferenceChanceListener Interface.

 
private class MyWallpaperEngine extends Engine implements OnHttpRequestResultListener, OnSharedPreferenceChangeListener {
 
        public MyWallpaperEngine() {
		    //get the image
		    SharedPreferences sp = getApplicationContext().getSharedPreferences(
					Constants.NAMESPACE_SETTINGS, Activity.MODE_PRIVATE);
			sp.registerOnSharedPreferenceChangeListener(this);
	}
 
        /* .... */
	@Override
	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
		draw();
 
	}
}

In der implementierten Methode onSharedPreferenceChanged wird einmal neu gezeichnet. Fertig.

Prüfen ob Service aktiv ist

Ich schreibe momentan ein Live Wallpaper für Android, mit einer Activity um Infos zum aktuellen Bild darzustellen. Um komfortabel das Live Wallpaper zu aktivieren brauche ich eine Prüfung, ob das auch läuft. WallpaperService ist von Service abgeleitet und damit ist dies kein Problem:

    /* ..... */
	private boolean isMyServiceRunning(String className) {
	    ActivityManager manager = (ActivityManager) getActivity().getSystemService(Context.ACTIVITY_SERVICE);
	    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
	        if (className.equals(service.service.getClassName())) {
	            return true;
	        }
	    }
	    return false;
	}
    /* ..... */

Nun kann bei Bedarf der Wallpaper Picker angezeigt werden:

	if(isMyServiceRunning(MyWallpaperService.class.getName())){
			Log.d(TAG, "active" );
	} else {
			Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
			startActivity(Intent.createChooser(intent, getActivity().getString(R.string.title_for_wallpaper_chooser)));
	}

Fin.

Links in TextView clickbar machen

Html Markup bringt man am einfachsten auf diesem Weg in TextViews:

/***** .... *****/
TextView mContent;
mContent.setText(Html.fromHtml("<a href=\"http://www.giftimessen.de\">Link</a>"));
/***** .... *****/

Beim Draufdrücken passiert leider noch nicht soviel. Um die Links gangbar zu machen einfach die MovementMethod setzen:

 
mContent.setMovementMethod(LinkMovementMethod.getInstance());

fertig :).

Howto – Daydream / Screensaver App erstellen

Ab Android 4.2 – Api Level 17 bietet Android die Möglichkeit mit Daydream einen Screensaver einzurichten. Daydream startet wenn das Gerät
am Netz hängt oder in einer Dockingstation steckt. Daydream kann man in den Systemeinstellungen unter Display -> Daydream aktivieren.
Ich beschreibe an dieser Stelle wie man einen einfachen DayDream Service implementiert der bei Aktivierung ein Video abspielt, damit alles schön übersichtlich bleibt.

Daydream wird als Service implementiert der von android.service.dreams.DreamService erbt, man hat den vollen Zugriff auf die UI, kann also Layouts, Canvas, 3d und Animationen nutzen.

Manifest

Im Manifest muß nur der Service deklariert werden. Falls ein Settingsmenü benötigt wird muß die Klasse im Meta gesetzt werden gesetzt werden.

<service
    android:name=".VideoDreamService"
    android:exported="true"
    android:label="@string/my_daydream_name" >
    <intent-filter>
        <action android:name="android.service.dreams.DreamService" />
 
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
 
    <meta-data
        android:name="android.service.dream"
        android:resource="@xml/dream_info" />
</service>

Layout

Zum einfacheren Testen benutze ich ein ganz normales Layout mit einer VideoView .

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity" >
 
    <VideoView
        android:id="@+id/videoView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" />
 
</RelativeLayout>

Service

In meinem Beispiel wird ein Video ohne Ton in einer Endlosschleife abgespielt.

package de.schmitt.michael.daydream;
 
/******* imports ***********/
/** ...**/
 
public class VideoDreamService extends DreamService {
 
 
	public void onDreamingStarted() {
		super.onDreamingStarted();
 
		setContentView(R.layout.activity_main);
 
 
		VideoView vv = (VideoView)findViewById(R.id.videoView1);
		vv.setVideoURI(Uri.parse("android.resource://" + getPackageName() +"/"+R.raw.video));
 
		Log.i(TAG,  Environment.getExternalStorageDirectory().toString());
		Log.i(TAG,  getApplicationContext().getPackageName() );
		vv.setOnPreparedListener(new OnPreparedListener() {
		    @Override
		    public void onPrepared(MediaPlayer mp) {
		        mp.setLooping(true);
 
		    }
		});
 
		vv.setSoundEffectsEnabled(false);
 
		vv.start();
	}
	public void onDreamingStopped() {
		// Wird aufgerufen wenn der Service beendet wird. 
 
        }
 
}

Links

http://android-developers.blogspot.de/2012/12/daydream-interactive-screen-savers.html

Led Lampen mit WLAN Anbindung

Ein Thema das mich immer wieder fasziniert ist die Vernetzung von Gegenständen des Alltags. Meine Wohnzimmerlampe glänzt nicht nur durch hässliche Form sondern auch durch kaltes Licht und Quecksilber im Leuchtmittel. Deshalb ersetze ich nach und nach Kompaktleuchtstofflampen durch LED Lampen. Der Zufall brachte mich zu diesem Lampenset mit WLAN Anbindung (Affiliate) vom Hersteller LimitlessLED. Die Lampen passen in E27 Sockel, haben 6 Watt Leistung und können mit 230V ~  betrieben werden. Die Grundidee ist die Lampen via Smartphone oder auf anderen Wegen einzuschalten, die Farben zu ändern und dimmen.Neben den 3 Lampen werden eine Fernbedienung und was viel interessanter ist einem Controller mit dokumentierter API geliefert. Apps gibt es auch! Pro Fernbedienung und Controller kann man 4 Lampen steuern. Dabei kann man die Parameter Helligkeit, Farbe regeln.

2013-10-15 16.56.18

 

Impressionen

Inbetriebnahme

Lampen

Jede Lampe muß konfiguriert werden. Dabei spielt es keine Rolle, ob man sie der Fernbedienung oder in der App konfiguriert.

Lampe in Fassung drehen und binnen 2 Sekunden nach Anlegen von Spannung auf der Fernbedienung einen Kanal aktivieren – die Lampe ist nun dem entsprechenden Kanal zugewiesen. Die erfolgreiche Zuweisung  wird durch Blinken quittiert. Um die Lampe aus dem Kanal zu lösen innerhalb von 2 Sekunden nach dem Einschalten 5 mal den entsprechenden Kanal auf der Fernbedienung aktivieren.

Controller

Der Controller wird per Micro-USB Kabel mit Spannung versorgt. Ich nutze ein Ladegerät für Smartphones. Nach dem Booten sollte man ein WLAN mit der SSID milight finden. Nun kann man der Anleitung folgen oder was ich als angenehmer per Webinterface konfigurieren. Dazu ins eigene WLAN verbinden und http://192.168.0.100/home.html aufrufen – ob die IP immer die gleiche ist kann ich nicht sagen. Hier muß in den STA Interface Settings das eigene WLAN konfiguriert werden.

App

Momentan nutze ich die in der Anleitung vorgeschlagene App Es gibt auch noch eine puristischere Version mit
Anbindung an Tasker – die  ohne Anpassungen leider nicht läuft.

LED Lampen ansteuern

Beim Hersteller gibt es eine Dokumentation.  Die Beispiele sind leider ein wenig inkonsistent, da irgendwann die Portnummer von 50000 auf 8899 umgestellt wurde.

Die Lampen werden per UDP Kommandos gesteuert. Dabei werden immer 3 Byte übertragen.

Byte 1 ist das Kommando an welches immer der Suffix 0x00  0x55 angehängt werden muß.

In PHP kann mit diesen Zeilen alle Kanäle einschalten:

&lt;?php
define("CONTROLLER_IP", "192.168.0.100");
function executeCommand( $ip, $command, $suffix1=00, $suffix2='55'){
	fwrite(fsockopen("udp://$ip", 8899), chr(hexdec($command)) . chr(hexdec($suffix1)) . chr(hexdec($suffix2)));
}
 
executeCommand(CONTROLLER_IP, 42);

Wer php5-cli installiert hat kann das Script auch auf der Kommandozeile nutzen. Für Freunde der Bash gibt es auch ein Script (geklaut bei Smileytechadventures)

#!/bin/bash 
 
if [ -z "$1" ] ; then 
    echo "You must enter a parameter: "  
    echo "  e.g. $0 allon" 
    exit 1 
fi 
 
incmd="$1" 
ipaddress="192.168.0.201" 
portnum="50000" 
 
allon="\x35\00\x55" 
alloff="\x39\00\x55" 
zone1on="\x38\00\x55" 
zone1off="\x3B\00\x55" 
zone2on="\x3D\00\x55" 
zone2off="\x33\00\x55" 
zone3on="\x37\00\x55" 
zone3off="\x3A\00\x55" 
zone4on="\x32\00\x55" 
zone4off="\x36\00\x55" 
 
eval incmd=\$$incmd 
 
echo -n -e "$incmd" &gt;/dev/udp/"$ipaddress"/"$portnum"

Ein wenig Heimautomatisierung ist auch schnell betrieben. Dieses Skript prüft, ob die Sonne untergegangen ist und schaltet in diesem Fall alle Lampen ein.

#!/usr/bin/php
&lt;?php define("CONTROLLER_IP", "192.168.0.100"); define("ZEIT_FUER_BETT", "23:59"); function executeCommand( $ip, $command, $suffix1=00, $suffix2='55'){ fwrite(fsockopen("udp://$ip", 8899), chr(hexdec($command)) . chr(hexdec($suffix1)) . chr(hexdec($suffix2))); } function getIp(){ return file_get_contents("http://icanhazip.com/"); } function getLatLongByIp($ip){ $geoplugin = unserialize( file_get_contents('http://www.geoplugin.net/php.gp?ip='.$ip) ); if ( is_numeric($geoplugin['geoplugin_latitude']) &amp;&amp; is_numeric($geoplugin['geoplugin_longitude']) ) { $ret['lat']= $geoplugin['geoplugin_latitude']; $ret['lon']=$geoplugin['geoplugin_longitude']; return $ret; } } $ret= getLatLongByIp(getIp()); $dateSunset=date_sunset(time(), SUNFUNCS_RET_TIMESTAMP,$ret['lat'], $ret['lon'], 90, 1); $dateSunrise=date_sunrise(time(), SUNFUNCS_RET_TIMESTAMP, $ret['lat'], $ret['lon'], 90, 1); //Zu einer festen Uhrzeit ausschalten if(date("H:i")&gt;= date(ZEIT_FUER_BETT)){
echo "schlafen";
	executeCommand(CONTROLLER_IP, 41);
	exit;
}  
//Nach Sonnenuntergang und vor Aufgang
if ( time() &gt;=$dateSunset &amp;&amp; $dateSunset &gt;$dateSunrise){
 
	executeCommand(CONTROLLER_IP, 42);
}else {
	executeCommand(CONTROLLER_IP, 41);
}

Das kleine Skript fühlt sich auf kleinen Linuxrechnern im eigenen Netzwerk sehr wohl und sollte per Cronjob alle 1 Minuten angestoßen werden.

*/1 *    * * *   /home/pi/sunrise.php

Inzwischen habe ich die Lampen seit einer Woche in Betrieb und viel Spaß damit. Die Lichtausbeute ist ausreichend, der Stromverbrauch in Ordnung. Der Preis ist mit 99 EUR schon sehr happig, aber es macht auch sehr sehr viel Spaß Skripte für die Lampen zu schreiben.

Facebook SDK für Android einrichten

Quelle: Facebook Developers

 

  •  SDK runterladen 
  • Zip entpacken, alles nach Eclipse exportieren (ohne in den Workspace zu kopieren)
  •  Evtl. Project -> Clean ausführen
  • Developer Account anlegen
  • Key Hash mit dem keytool  (liegt in <JDK Folder>/bin) erzeugen
  • (Linux, Mac) keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64
  • (Windows) keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android\debug.keystore | openssl sha1 -binary | openssl base64
  • Im Profil Key Hash unter Sample App Settings eintragen.

 

Beispiele Testen.

Chromecast endlich eingetroffen

Mein Bekannter ist endlich aus USA zurück und hat mir einen Chromecast mitgebracht. Die Einrichtung war äußerst simpel: Chromecast per USB mit Strom versorgen und an den Fernseher anschließen, Konfiguration via PC, Windows7.

 

Chromecast

 

 

Das Gerät lässt sich von allen Android Geräten ansteuern die im gleichen WLAN wie der Dongle sind. Bis jetzt habe ich Youtube, Google Music und – Play Movies gestest.

Die Bedienung ist simpel: In den Apps taucht ein neues Menü auf mit dem man die Wiedergabe auf einen Chromecast umlenken kann. Dabei wird nicht gestreamt wie bei Miracast sondern es werden Steueranweisungen  vom Android Gerät an den Dongle übergeben. Die Architektur nennt sich Google Cast. Dieser holt sich dann die Medien aus dem Internet. Das hat den Vorteil, daß man Rechenzeit auf dem Telefon spart, aber das Streamen von lokalen Dateien ist nicht ohne weiteres möglich.

.

Die PC Version des Chromebrowsers bietet die Möglichkeit einzelne Tabs an Chromecast zu übertragen. Webseiten sind kein Problem,  Videos schauen macht aufgrund des Streamingkonzepts und der hohen Latenz  (meiner DSL Leitung) wenig Sinn.

 

Für die Streaming-Architektur gibt es ein SDK mit Dokumentation, welches explizit als Preview deklariert ist. Ich werde noch ein wenig abwarten bevor ich mir das anschaue. Vor knapp einem Monat sperrte Google per Chromecast-Update die App Allcast aus mit der es möglich war lokal vorliegende Dateien abzuspielen. Google weiß anscheinend selbst noch nicht, wie restriktiv der Chromecast werden soll.

 

 

Skeleton für Webviews in Android

package de.schmitt.michael.webview;
 
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
 
@SuppressLint("SetJavaScriptEnabled")
public class ActivityMain extends Activity {
	protected WebView wv;
	private final static String URL="http://www.google.de/";
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
 
		 WebView w = new WebView(this);
		 w.setWebViewClient(new WebViewClient());
		 w.getSettings().setJavaScriptEnabled(true);
		 w.loadUrl(URL);
		 setContentView(w);
 
 
	}
}

Mehr zum Thema in der Android Dokumentation

Google Cloud Messaging (GCM) Tutorial

In diesem Beitrag geht es darum wie man Nachrichten via GCM an Apps verschicken kann.

Viele Android Apps sind klassische Client/Server Anwendungen. Der Client tätigt Requests an einen Server und bekommt im besten Fall eine vernünftige Antwort. In anderer Richtung ist dies jedoch nicht so einfach. Wenn der Server Daten an 1 bis n Clients senden möchte ist das in diesem Szenario nicht so einfach. Mobiltelefone sind meist nicht über eine statische IP zu erreichen, es ist nicht sichergestellt, daß das Gerät mit dem Internet verbunden ist wenn eine Nachricht versendet wird. Wenn der Client regelmäßig den Server anpollt kostet das Bandbreite und elektische Leistung.

Continue reading