Apps für Android programmieren leicht gemacht!

Raspberry Pi – Bewegungsmelder – „Scharf“ stellen

Auf Anfrage von Timo hin wollen wir uns heute einmal damit beschäftigen, wie wir unsere bereits fertiggestellte Alarmanlage à la Raspberry Pi denn nun eigentlich „scharf“ stellen können und wie wir eben diese dann auch wieder „deaktivieren“.

Der Artikel ist zwar schon ein wenig älter, doch was wir nicht unbedingt bedacht haben, ist dass die selbst gebaute Alarmanlage natürlich auch immer anschlägt, wenn „nur“ wir Zuhause sind. Eine Möglichkeit wäre es natürlich dem Pi einfach den Saft abzudrehen, indem wir das Kabel ziehen oder ein Kabel mit Schalter nutzen. Wer das aber nicht kann oder will, der muss sich eben anders behelfen.

Wir werden klären, wie.
Es sind lediglich 6 kleinere Anpassungen in jeweils 5 Dateien nötig, die uns idealerweise bereits zur Verfügung stehen.

 

Einstellungen.java

Vorerst muss es uns möglich sein die Einstellung, ob die Alarmanlage aktiviert oder deaktiviert ist zu speichern und wieder auszulesen.
Hierzu fügen wir in die Klasse „Einstellungen“ ein:

// Einstellungen, zum aktivieren oder deaktivieren der Alarmanlage
    public static final String active_string = "isActive";
    private SharedPreferences sharedPreferences;
    
    public Einstellungen(Context cxt){
        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(cxt);
    }
    
    public void setActive(boolean active){
        sharedPreferences.edit().putBoolean(Einstellungen.active_string, active).apply();
    }
    
    public boolean isActive(){
        return sharedPreferences.getBoolean(Einstellungen.active_string, true);
    }

Mittels der Befehle „setActive“ und „isActive“ können wir nun auslesen, ob die Alarmanlage scharf ist oder eben nicht. Zu beachten ist noch, dass wie die Klasse „Einstellungen“ erst als Objekt, über den Constructor „Einstellungen(Context)“ initialisieren müssen. (Später dazu mehr.)

 

activity_main.xml

Wir können die Einstellungen nun abspeichern, aber wir müssen auch in der Lage sein die Einstellung zu ändern. Hierfür ist vorerst ein UI-Element, wie ein Button nötig. Ich nutze dazu einen „Switch“, der sieht einfach besser aus.

Öffnet also die Datei „activity_main.xml“ im Ordner res->layouts und fügt am Ende, aber vor „</RelativeLayout>“, ein:

<Switch
        android:text="Alarmanlage scharf stellen."
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/switchAlarm"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true" />

 

MainActivity.java

Super! – Wir haben einen Switch, nun müssen wir auf ihn reagieren und ihn gemäß der aktuellen Einstellung auf an oder aus setzen.
Dazu öffnen wir die Klasse „MainActivity“ und fügen unter „private BroadcastReceiver mRegistrationBroadcastReceiver;“ ein:

// Reagiere auf den Switch, zur Aktivierung/Deaktivierung der Alarmanlage
// und setze den Switch gemäß den Einstellungen auf an/aus.
einst = new Einstellungen(this);
Switch switchAlarm = (Switch) findViewById(R.id.switchAlarm);
switchAlarm.setChecked(einst.isActive());
switchAlarm.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        einst.setActive(isChecked);
    }
});

Uns fehlt nun noch die globale Variable „einst“, diese definieren wir unterhalb der Zeile „private TextView status_textView;“, mittels:

public  Einstellungen einst;

 

GcmListener.java

Öffnet nun die Klasse „GcmListener“. In dieser schicken wir ja die Benachrichtigung raus, wenn die Klasse eine Nachricht von unserem Raspberry Pi erhält.
Der Trick am aktivieren oder deaktivieren der Alarmanlage ist, dass wir nicht wirklich die Alarmanlage ausschalten, sondern nur unsere Benachrichtigungen auf Stumm.
Genau deshalb habe ich in der Einleitung auch das „scharf stellen“ in Anführungszeichen gesetzt.

Bitte sucht euch nun die Zeile „if (from.startsWith(„/topics/bewegung“)) {“ heraus und alle möglichen anderen If-Abfragem die so ähnlich aussehen und schreibt diese mit der zusätzlichen Bedingung, der aktivierten Benachrichtigungen, um.
In der anfänglichen App sieht dass dann so aus:

// Alte Abfrage
if (from.startsWith("/topics/bewegung")) {

// Neue Abfrage + Auslesen der Einstellung
Einstellungen einst = new Einstellungen(this);
if (from.startsWith("/topics/bewegung") && einst.isActive()) {

 

build.gradle (Module: app)

Leider hat Schönheit ihren Preis. So auch bei unserem schnieken Switch, denn der benötigt mindestens die API Version 14.
Das ist immerhin noch Android 4.

Wem das reicht, der kann den Switch in der App bestehen lassen und geht in die Datei „build.gradle (Module: app)“.
Dort steht eine Zeile mit dem Inhalt „minSdkVersion“ dort hinter sollte also mindestens eine 14 stehen. Mehr geht natürlich immer.

Marvin

Ich bin 23 Jahre jung und studiere zurzeit Wirtschaftsinformatik an der Georg-August-Universität in Göttingen. Ich bin ein Mensch, der sich neben der Programmierung noch für tausend andere Dinge interessiert, die mal mehr und mal weniger verrückt sind. Vor allem aber bin ich Feuer und Flamme mit der Programmierung von eigenen kleinen Apps und Programmen, die mein Leben bereichern.

7 Kommentare

*Pflichtfeld

  • Hallo Marvin,
    vielen vielen Dank für deine Mühe. Mit der ActivityMain.java meinst du sicherlich die MainActivity.java, oder?
    Das „status_textView = status;“ finde ich in der Datei nicht. Wie muss ich dann das und den Rest dort einfügen?
    Vielen Dank für die Hilfe… 🙂

    • Hallo Timo,

      entschuldige die verdrehten Worte. Es ist natürlich MainActivity.java gemeint.
      Die Zeile existiert tatsächlich nicht, auch da ist mir ein kleiner Fehler unterlaufen. Füge es unter die Zeile „private BroadcastReceiver mRegistrationBroadcastReceiver;“ statt „[…] status_textView = […]“. Ich hatte diesen Artikel anhand der Grundapp + die Statusanzeige geschrieben.

      Beide Fehler sind auch in dem Artikel verbessert. Vielen Dank für den Hinweis.

      Gruß

      • Ich bin´s nochmal 😉
        Ich habe das jetzt so alles eingefügt…

        Hier ist der Anfang der Main Activity.java:
        public class MainActivity extends AppCompatActivity {

        private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
        private boolean isReceiverRegistered = false;
        private BroadcastReceiver mRegistrationBroadcastReceiver;
        // Reagiere auf den Switch, zur Aktivierung/Deaktivierung der Alarmanlage
        // und setze den Switch gemäß den Einstellungen auf an/aus.
        einst = new Einstellungen(this);
        Switch switchAlarm = (Switch) findViewById(R.id.switchAlarm);
        switchAlarm.setChecked(einst.isActive());
        switchAlarm.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        einst.setActive(isChecked);
        }
        });

        [……]

        Also so gibt der Compiler mir mehrere Fehlermeldungen aus… Vielleicht liegt es auch daran, dass ich die public Einstellungen einst; falsch eingefügt habe…
        Wo genau muss diese globale Variable hin bzw. definiert werden, denn die „private TextView status_textView;“ gibt´s wieder nicht im der Datei???

        Vielen Dank und ich hoffe ich störe nicht, denn Java ist für mich „Neuland“… 😉

      • Hallo Timo,

        du störst natürlich nicht.

        Als kleine Erläuterung: Globale Variablen sind Variablen, die außerhalb irgendwelcher Funktionen definiert wurden. Allerdings müssen sie immer in einer Klasse definiert sein.
        Initialisiert oder auch beschrieben werden diese in unserem Falle erst, wenn wir innerhalb der Funktion „onCreate“ sind.
        Wir schreiben also unter die Zeile „private BroadcastReceiver mRegistrationBroadcastReceiver;“ nur „Einstellungen einst;“. Damit definieren wir eine Variable namens einst, vom Datentyp Einstellungen. Der Datentyp ist also die Klasse „Einstellungen.java“. Wir haben sie nur definiert, aber noch nicht initialisiert.

        Die Zeilen,
        // Reagiere auf den Switch, zur Aktivierung/Deaktivierung der Alarmanlage
        // und setze den Switch gemäß den Einstellungen auf an/aus.
        einst = new Einstellungen(this);
        Switch switchAlarm = (Switch) findViewById(R.id.switchAlarm);
        switchAlarm.setChecked(einst.isActive());
        switchAlarm.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        einst.setActive(isChecked);
        }
        });
        müssen dann in die Funktion „onCreate“, da wir die Variable „einst“ erst dann initialisieren können. Das können wir erst in der Funktion „onCreate“, da wir „this“ in diesem Fall einen Context benutzten und der Context ist frühestens in der Funktion „onCreate“ verfügbar. onCreate ist auch die erste Funktion, die Android aufruft, wenn die App startet und geladen ist.
        Gleiches gilt z.B. auch für die Zeile „Switch switchAlarm = (Switch) findViewById(R.id.switchAlarm);“, da die App erst weiß, dass in der Datei „acitivity_main.xml“ ein Switch namens switchAlarm existiert.

        Ich weiß, das klingt im ersten Moment erschlagend, aber das wird schon 😛
        Lies dir am besten nochmal alle Kapitel 2 Artikel der Kategorie „Lernen“ durch, dort habe ich bereits alles erläutert.
        Für den Anfang solltest du die eben besagten Zeilen ans Ende der Funktion „onCreate“ einfügen, aber noch bevor sie mittels } geschlossen wird.

        Gruß

      • Danke Marvin, das mit der globalen Variable ist passt schon super.
        Ich habe also die anderen Zeilen ans Ende der „on create“ Funktion gepackt.
        Der Compiler meckert, immer noch :/ :

        Einstellungen.java:
        error: cannot find symbol class SharedPreferences
        error: cannot find symbol class Context
        error: cannot find symbol variable PreferenceManager

        MainActivity.java:

        error: cannot find symbol class Switch
        […]

        gcmlistener.java:

        error: cannot find symbol variable einst

        Das sind die Compilerfehler… Im Netz finde ich nichts zu denen 🙁

        Hier ist nochmal die Funktion:
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView status = (TextView) findViewById(R.id.status_text);
        // Prüfe, ob auf dem Gerät der Google PlayService installiert ist.
        // Dieser wird für den Empfang der GCM Nachrichten benötigt.
        if (checkPlayServices()) {
        // Erstelle einen Listener, der immer dann ausgeführt wird, wenn die App bei GCM registriert wurde oder ein Fehler bei der Registrierung auftrat.
        mRegistrationBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
        SharedPreferences sharedPreferences =
        PreferenceManager.getDefaultSharedPreferences(context);
        boolean sentToken = sharedPreferences
        .getBoolean(Einstellungen.SENT_TOKEN_TO_SERVER, false);
        if (sentToken) {
        status.setText(„Status: Verbunden“);
        } else {
        status.setText(„Status: Fehlerhaft“);
        }

        }
        };
        // Starte den oben beschriebenen Listener.
        registerReceiver();
        // Starte einen IntentService, um die App bei GCM zu registrieren.
        Intent intent = new Intent(this, RegistrationIntentService.class);
        startService(intent);
        }
        // Reagiere auf den Switch, zur Aktivierung/Deaktivierung der Alarmanlage
        // und setze den Switch gemäß den Einstellungen auf an/aus.
        einst = new Einstellungen(this);
        Switch switchAlarm = (Switch) findViewById(R.id.switchAlarm);
        switchAlarm.setChecked(einst.isActive());
        switchAlarm.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        einst.setActive(isChecked);
        }
        });
        }

        Ich hoffe, ich kann noch mal auf deinen netten Rat zählen 😉

      • Hallo Timo,

        Entschuldige die späte Antwort.
        Kurze Zwischenfrage: Sind die besagten fehlenden Symbols zufällig im Code rot? – Wenn ja, dann versuch mal auf die Wörter zu klicken und dann wahlweise im Popup „Import …“ zu wählen, oder mit Alt+Enter zu arbeiten.
        Es sieht fast so aus, als wären nicht die nötigen Imports gemacht worden.
        Imports dienen dazu eine Klasse in einer anderen Klasse verwendbar zu machen.

        In der Klasse EInstellung muss z.B. ganz oben die Zeile „import android.content.Context;“ stehen.
        So sollte es aussehen, um Imports ausführen zu lassen.

        Gruß