# Hardware-Keys in Android



## schoppenhauer (26. Feb 2014)

Hallo. Ich versuche gerade, mir beizubringen, wie man services schreibt, die auf Key-Events reagieren. Beispielsweise gibt es Apps, die auf das Drücken des Power-Knopfes reagieren, ohne seine sonstige funktion zu ändern.

Ich bin allerdings ein bisschen verwirrt. Ich habe folgende *AndroidManifest.xml*:

[XML]<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="de.uxul.androidspam"
      android:versionCode="1"
      android:versionName="1.0">
  <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" />
  <uses-permission android:name="android.permission.VIBRATE"/>
    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
      <receiver android:name=".RemoteControlReceiver">
	<intent-filter>
	  <action android:name="android.intent.action.MEDIA_BUTTON" />
	</intent-filter>
      </receiver>

      <activity android:name="MainActivity"
		android:label="@string/app_name">
	<intent-filter>
	  <action android:name="android.intent.action.MAIN" />
	  <category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
      </activity>
    </application>
</manifest>
[/XML]

Dann habe ich eine *MainActivity.java*, die ohne die Zeilen 30 und 31 auch compiliert:


```
package de.uxul.androidspam;

import android.app.*;
import android.view.*;
import android.os.*;
import android.content.*;
import android.util.*;
import android.media.*;

public class MainActivity extends Activity
{

    private static final String TAG = "MainActivity";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    /** Called when the user clicks the Send button */
    public void sendMessage(View view) {
	// Do something in response to button
	Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
	v.vibrate(300);
	Log.d(TAG, "Vibrate!");

	AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
	am.registerMediaButtonEventReceiver(RemoteControlReceiver);

	//am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);

    }

}
```

Dann habe ich noch eine *RemoteControlReceiver.java*:


```
package de.uxul.androidspam;

import android.app.*;
import android.view.*;
import android.os.*;
import android.content.*;
import android.util.*;
import android.media.*;

public class RemoteControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
	Log.d("RCR", "QUIEK");
    }
}
```

Wenn ich die genannten zwei Zeilen weglasse, passiert nichts, egal welche Hardwareknöpfe ich drücke. In 
	
	
	
	





```
adb logcat
```
 erscheinen die Tastendrücke, aber nicht mein Log-Text. Die beiden Zeilen compilieren aber auch nicht. am.registerMediaButtonEventReceiver möchte ein PendingIntent. Ich habe aber keine Ahnung, wo ich das herbekomme.

Könnt ihr mir helfen?

Wie gesagt, das ganze ist eher so ein Patchwork-Ding, wo ich mit verschiedenen Sachen experimentiere, der erste Teil ist zum Beispiel direkt aus dem Android-Tutorial. Ich hab mir das aus diversen Quellen zusammengetragen. Unter Anderem:

https://developer.android.com/training/managing-audio/volume-playback.html
https://developer.android.com/reference/android/view/KeyEvent.html
android - Key listener in Service - Stack Overflow


----------



## dzim (27. Feb 2014)

Verstehe das Grundsätzliche Problem, aber nicht, was bei dir jetzt genau nicht klappt. Wird dein BroadcastReceiver benachrichtigt, oder nicht? Ist das das Log-Level von logcat korrekt eingestellt (in Eclipse kann man sich auch nur auf Warning und aufwärts beschränken, vielleicht ist das ja da auch)? Kannst du vielleicht einmal irgend ein Intent vom Reciever aus feuern, der etwas sinnvolles macht? Oder kurz ein Toast anzeigen?
Ich hab mit BroadcastReceivern noch nicht so viel Erfahrung, daher kann ich nur sagen, das dein Code korrekt aussieht, aber leider nicht mehr als das...


----------



## dzim (27. Feb 2014)

Hm... Sag mal: Du hast doch schon deinen Receiver im Manifest registriert, oder? Ist es dann überhaupt noch notwendig, ihn im Code zu registrieren? Vielleicht mal eine der beiden Sachen wegnehmen...


----------



## schoppenhauer (27. Feb 2014)

dzim hat gesagt.:


> Verstehe das Grundsätzliche Problem, aber nicht, was bei dir jetzt genau nicht klappt.


Geht mir genauso, leider.



dzim hat gesagt.:


> Wird dein BroadcastReceiver benachrichtigt, oder nicht?


Wenn ich hdas richtig interpretiere, dann nicht.



dzim hat gesagt.:


> Ist das das Log-Level von logcat korrekt eingestellt (in Eclipse kann man sich auch nur auf Warning und aufwärts beschränken, vielleicht ist das ja da auch)? Kannst du vielleicht einmal irgend ein Intent vom Reciever aus feuern, der etwas sinnvolles macht? Oder kurz ein Toast anzeigen?


Ich benutze kein Eclipse. Das LogLevel sollte debug sein, also das selbe sein wie beim Button, und dessen Ausgabe wird angezeigt.




			
				dzim hat gesagt.:
			
		

> Hm... Sag mal: Du hast doch schon deinen Receiver im Manifest registriert, oder? Ist es dann überhaupt noch notwendig, ihn im Code zu registrieren? Vielleicht mal eine der beiden Sachen wegnehmen...


Wie gesagt, anscheinend reicht das nicht. Die Zeilen 30 und 31 gehen so nicht, aus genannten Gründen. Ich nehme also an, dass man das Ding aktivieren muss, ich habe nur keine Ahnung, woher ich ein PendingIntent dazu bekommen soll.


----------



## dzim (27. Feb 2014)

> Ich benutze kein Eclipse. Das LogLevel sollte debug sein, also das selbe sein wie beim Button, und dessen Ausgabe wird angezeigt.


Einen Toast kannst du trotzdem anzeigen lassen:

```
Toast.makeText(context, "Ein Toast auf die Toasts!", Toast.LENGTH_SHORT).show();
```
anstelle oder zusätzlich zu dem Log...



> Die beiden Zeilen compilieren aber auch nicht. am.registerMediaButtonEventReceiver möchte ein PendingIntent. Ich habe aber keine Ahnung, wo ich das herbekomme.


Ok, die Zeile habe ich beim ersten Mal glatt überlesen. Aber: du hast dir schon mal die Doku zu Gemüte geführt, oder??? Da findet man schon, wie man einen PendingIntent erstellen kann, aber das ist eigentlich unwichtig, weil du eines übersehen hast. Es gibt zwei Register-Methoden: Die erste für einen PendingIntent, die Zweite für einen ComponentName. Die Doku sagt beim Zweiten auch noch, das es für BroadcastReceiver ist...

Beispielcode (nicht getestet, aber soll auch nur die beiden unterschiedlichen Ansätze erläutern):

```
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
		
am.registerMediaButtonEventReceiver(new ComponentName("<dein.android.package>", BroadCastReceiver.class)); // package can be a context as well
		
Intent intent = new Intent(context, BroadCastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(contenxt, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
am.registerMediaButtonEventReceiver(pendingIntent);
```
Und du, mein Freund, verwendest am besten das Erste! Und da du es im Code registrierst, verwendest du anstelle des Strings für den Package-Namen gefälligst den Context! I.O.?


----------



## schoppenhauer (27. Feb 2014)

dzim hat gesagt.:


> Einen Toast kannst du trotzdem anzeigen lassen:
> 
> ```
> Toast.makeText(context, "Ein Toast auf die Toasts!", Toast.LENGTH_SHORT).show();
> ...


Ok, habe ich jetzt gemacht.



dzim hat gesagt.:


> Ok, die Zeile habe ich beim ersten Mal glatt überlesen. Aber: du hast dir schon mal die Doku zu Gemüte geführt, oder??? Da findet man schon, wie man einen PendingIntent erstellen kann, aber das ist eigentlich unwichtig, weil du eines übersehen hast.


Das Zeug wirkt halt alles wahnsinnig überabstrahiert. Kann durchaus sein dass ich einiges übersehen habe.



dzim hat gesagt.:


> Und du, mein Freund, verwendest am besten das Erste! Und da du es im Code registrierst, verwendest du anstelle des Strings für den Package-Namen gefälligst den Context! I.O.?


Okeh. Mein Kot sieht jetzt wie folgt aus, und geht immernochnicht:

[XML]<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="de.uxul.androidspam"
      android:versionCode="1"
      android:versionName="1.0">
  <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" />
  <uses-permission android:name="android.permission.VIBRATE"/>
    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">


      <activity android:name="MainActivity"
		android:label="@string/app_name">
	<intent-filter>
	  <action android:name="android.intent.action.MAIN" />
	  <category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
      </activity>
    </application>
</manifest>[/XML]


```
package de.uxul.androidspam;

import android.app.*;
import android.view.*;
import android.os.*;
import android.content.*;
import android.util.*;
import android.media.*;
import android.widget.*;

public class RemoteControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
	Log.d("RCR", "QUIEK");
	Toast.makeText(context, "Ein Toast auf die Toasts!", Toast.LENGTH_SHORT).show();
    }
}
```



```
package de.uxul.androidspam;

import android.app.*;
import android.view.*;
import android.os.*;
import android.content.*;
import android.util.*;
import android.media.*;
import android.widget.*;


public class MainActivity extends Activity
{

    private static final String TAG = "MainActivity";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    /** Called when the user clicks the Send button */
    public void sendMessage(View view) {
	// Do something in response to button
	Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
	v.vibrate(300);
	Log.d(TAG, "Vibrate!");
	Context c = view.getContext();
	Toast.makeText(view.getContext(), "Ein Toast auf die Toasts 2!", Toast.LENGTH_SHORT).show();
	AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
	am.registerMediaButtonEventReceiver(new ComponentName(c, RemoteControlReceiver.class));
    }
}
```

Es geht auch nicht mit dem intent-filter in XML.


----------



## dzim (28. Feb 2014)

Der Doku des AudioManagers nach (AudioManager @AndroidDeveloper) muss dein BroadcastReceiver ebenfalls im Manifest registriert werden.

Lars Vogel erklärt das BroadcastReceiver und PendingIntends noch mal vielleicht etwas verständlicher auf seiner Seite. Ich denke du musst deinen Receiver nur noch im Manifest registrieren und kannst dabei auf spezielle *intent-filter* verzichten. Da ich aber noch nie mit dem AudioManager gespielt habe, weiss ich das natürlich nicht zu 100%, aber das ist mein _educated guess_.

Probier es also noch einmal mit dem Registrieren des BroadcasReceiver *ohne* einen Intent Filter.


----------

