Custom ListView für eine Wecker-App

  • Antworten:37
Norman L.
  • Forum-Beiträge: 20

15.09.2016, 14:36:12 via Website

Hallo Forum,

ich will zum Lernen eine einfache Wecker-App programmieren, die folgendes können soll:
- in einem ListView mehrere Alarme erstellen
- diese Alarme sollen sich durch einen Switch an- und ausschalten lassen
- der Wecker soll dann 1 mal am Tag den Alarm wiederholen
- durch ein langes klicken auf den ListView-Eintrag soll (nach einem Dialog) der Eintrag gelöscht werden

So, nun zu der Stelle, an der ich gerade festhänge:

Ich habe eine Custom ListView erstellt (list_view_item.xml). Dort habe ich dann den Switch hinzugefügt.

Um den Switch irgendwie anzusprechen, hab ich einen CustomAdapter erstellt. Der Alarm soll später über den "RingService" ausgelöst werden.

Wie kann ich jetzt den Switch am besten ansprechen? Also wie würdet ihr es machen?

Problem 2: Irgendwie funktioniert der OnItemLongClickListener bei meinem ListView Item nicht

Hier das Projekt auf Github: [github].../pixelfehler1996/Alarm_Clock
(ich kann noch keine Links posten...)

Vielen Dank schon mal im Voraus!

— geändert am 15.09.2016, 14:43:24

Antworten
swa00
  • Forum-Beiträge: 3.704

15.09.2016, 14:50:48 via Website

Hallo Norman,

willkommen im Forum

es gibt bekanntlich viele Verfahren ...

Du kannst z.B. Dieses hier anwenden

http://stackoverflow.com/questions/8527101/how-to-know-which-view-inside-a-specific-listview-item-that-was-clicked

Um den Status des switches ausserhalb abzufragen , übergibst du dem Adapter am besten eine dynamische ArrayStruktur und ermittelst innerhalb des Adapters das true / false flag auf den Switch

Ergo : du baust dir einen onClickListener für das entsprechende View in deinen Adapter ein
.
.

Problem 2 :

Hier baust du dir einen onTouchListener in deinen Adapter ein , reagierst auf DOWN & UP und misst die Zeit dazwischen.

http://stackoverflow.com/questions/23793345/find-duration-between-touch-events-in-android
http://stackoverflow.com/questions/4324362/detect-touch-press-vs-long-press-vs-movement

lg
Stefan

— geändert am 15.09.2016, 15:21:59

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

15.09.2016, 15:04:28 via Website

Danke für die Antwort.

Eine Frage: Was genau meinst du mit "dynamische ArrayStruktur"?

Grüße
Norman

Antworten
swa00
  • Forum-Beiträge: 3.704

15.09.2016, 15:12:48 via Website

Eine Frage: Was genau meinst du mit "dynamische ArrayStruktur"?

Im Prinzip eine ArrayList indem du nicht nur ein Element verwaltest, sondern eine Klassenstruktur

public class mystruct
{
public Boolean mySwitch;
public String myString;
}
ArrayList < mystruct > foo = new ArrayList();

Diese füllst du dann mit add und übergibst die dann dem Adapter
Innerhalb diesem kannst du dann der Struktur die Stati setzen

http://www.vogella.com/tutorials/AndroidListView/article.html

lg
Stefan

— geändert am 15.09.2016, 15:18:19

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

15.09.2016, 16:47:21 via Website

Nur, damit ich das richtig verstehe (mir fehlen da wohl noch einige Java-Kenntnisse):

In der ArrayList sind die Objekte der Klasse "mystruct" gespeichert, die dann jeweils die Eigenschaften "mySwitch" für den Status des Switches und "myString" für - in dem Fall - die dargestellte Uhrzeit besitzen, soweit richtig?

Der Adapter ist dann dafür zuständig aus der Übergebenen Liste an Objekten, die entsprechenden Eigenschaften auszulesen und in der ListView darzustellen. Korrekt?

Und noch eine Frage: erbt meine Custom Adapter-Klasse jetzt am besten von ArrayAdapter oder BaseAdapter? Weil, es wird ja jetzt kein String mehr übergeben, sondern die "mystruct"-Klasse.

— geändert am 15.09.2016, 16:50:00

Antworten
swa00
  • Forum-Beiträge: 3.704

15.09.2016, 17:00:27 via Website

Hallo Norman,

ich leite bei so etwas immer von einem BaseAdapter ab.
So wie ich es vorgeschlagen habe, ist es zwar ein wenig "erweiternd" - du bist aber bei weitem flexibler

Innerhalb von BaseAdapter deklarierst du z.b

private ArrayList mData = new ArrayList();

dann erweiterst/änderst du das Ganze mit

public void addItem(final myStruct item)
{
mData.add(item);
notifyDataSetChanged(); <-- nur wenn unbedingt notwendig , sonst macht der das bei jedem Add
}

und

@Override
public myStruct getItem(int position) {return (myStruct) mData.get(position);}

im getView kannst du dann folgendes machen

myStruct pg_tmp = (myStruct) mData.get(position);

und kannst somit auf alle element zugreiffen

in der Activity addest du die Einträge recht simple

myStruct foo = new myStruct();
foo.mySwitch = false;
foo.myString ="Erster Eintrag";
adapter.addItem (foo);

Kannst also rein theoretisch die oben erwähnte ArrayList weglassen

— geändert am 15.09.2016, 22:57:59

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

16.09.2016, 08:10:22 via Website

Du bist echt super hilfreich, danke! :)

— geändert am 16.09.2016, 08:28:35

Antworten
Norman L.
  • Forum-Beiträge: 20

16.09.2016, 08:28:49 via Website

Aber irgendwas ist noch verkehrt. Die neue Uhrzeit wird bei "showDialog" noch nicht hinzugefügt.

MainActivity:

Calendar calendar;
Button btnShowDialog;
String timeString;
ArrayList alarmList;
CustomAdapter customAdapter;
ListView alarmListView;
Intent alarmIntent;
AlarmManager alarmManager;
PendingIntent pendingIntent;
SharedPreferences sharedPreferences;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    sharedPreferences = getSharedPreferences("com.example.laudien.wecker", MODE_PRIVATE);

    // Initialize variables
    btnShowDialog = (Button) findViewById(R.id.btnShowDialog);
    calendar = Calendar.getInstance();
    alarmListView = (ListView) findViewById(R.id.listView);
    timeString = "";
    alarmIntent = new Intent(MainActivity.this, RingService.class);
    alarmManager = (AlarmManager)MainActivity.this.getSystemService(Context.ALARM_SERVICE);

    // initialize ListView
    Set set = sharedPreferences.getStringSet("alarmList", null);
    if(set != null)
        alarmList = new ArrayList(set);
    else
        alarmList = new ArrayList<CustomItemStructure>();
    customAdapter = new CustomAdapter(MainActivity.this, alarmList, R.layout.list_view_item);
    alarmListView.setAdapter(customAdapter);
}

@Override
protected void onPause() {
    super.onPause();
    Set set = new HashSet();
    set.addAll(alarmList);
    sharedPreferences.edit().putStringSet("alarmList", set).apply();
}

public void showDialog(View view){
    new TimePickerDialog(MainActivity.this, new TimePickerDialog.OnTimeSetListener() {
        @Override
        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            Calendar timeCalendar = Calendar.getInstance();
            timeCalendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
            timeCalendar.set(Calendar.MINUTE, minute);
            timeString = DateUtils.formatDateTime(MainActivity.this, timeCalendar.getTimeInMillis(), DateUtils.FORMAT_SHOW_TIME);

            CustomItemStructure structure = new CustomItemStructure();
            structure.alarmSwitch = true;
            structure.alarmTimeInMillis = timeCalendar.getTimeInMillis();
            customAdapter.add(structure);

            Log.i("TimePicker", "The time was set to " + timeString);
        }
    }, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), DateFormat.is24HourFormat(MainActivity.this)).show();
}`

CustomAdapter:

public class CustomAdapter extends BaseAdapter {

Context userContext;
int layoutID;
private ArrayList<CustomItemStructure> alarmList = new ArrayList<>();

public CustomAdapter(Context context, ArrayList<CustomItemStructure> arrayList, @LayoutRes int resource) {
    layoutID = resource;
    userContext = context;
    alarmList = arrayList;
}

@Override
public int getCount() {
    return 0;
}

@Override
public Object getItem(int position) {
    return null;
}

@Override
public long getItemId(int position) {
    return 0;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    CustomItemStructure itemNow = alarmList.get(position);
    TextView alarmTextView = (TextView) convertView.findViewById(R.id.alarmTextView);
    Switch alarmSwitch = (Switch) convertView.findViewById(R.id.alarmSwitch);

    alarmTextView.setText(DateUtils.formatDateTime(userContext, itemNow.alarmTimeInMillis, DateUtils.FORMAT_SHOW_TIME));
    alarmSwitch.setChecked(itemNow.alarmSwitch);

    return convertView;
}
public void add(CustomItemStructure item){
    alarmList.add(item);
}

}`

CustomItemStructure:

public class CustomItemStructure{
public boolean alarmSwitch;
public long alarmTimeInMillis;
}`

— geändert am 16.09.2016, 08:29:57

Antworten
swa00
  • Forum-Beiträge: 3.704

16.09.2016, 08:39:06 via Website

auf den ersten blick sehe ich , dass du es nicht so ganz amchst , wie ich oben geschrieben habe , denn jetzt vermischst du zwei ArrayList ..

einmal die , die du aus den Prefs ausliest, die übergibst du den Baseadpater - alles klar,
aber da hast du wiederum eine new Arraylist stehen

zweimal new kommt nicht gut :-)

und dann sehe ich nirgendwo , das du "add" vom Baseadapter benutzt - jetzt hast du ordentlich mischmasch drin :-)

Mache es so , wie ich oben beschrieben habe , dann liesst du die prefs aus und übergribst mit additem ( oder add)
Nimm nur EINE Arraylist , entweder ausserhalb oder innerhalb Baseadapter , nicht beides

EDIT : und getCount gibt bei dir immer 0 zurück

Und dann übergibst du dem Baseadapter nach dem Pref nicht deine Structur sondern ein Stringset

Tip : mach dir erst mal ein DemoProjekt , wo du ausschliesslich mit der List und Baseadapter rumspielst um genau zu erfahren , wie das Ganze funktioniert ..

— geändert am 16.09.2016, 08:55:50

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

16.09.2016, 09:16:18 via Website

Okay, das werd ich auch machen (mit dem DemoProjekt).

Im Prinzip hast du mir ja alles gegeben was ich brauche^^

Trotzdem eine Frage: Ich hab noch nicht so recht verstanden, was dieses "inflate" bedeutet, bzw. was ein "Inflater" genau macht.

Antworten
swa00
  • Forum-Beiträge: 3.704

16.09.2016, 09:21:35 via Website

Du kannst einer Reihe in einem ListView ein eigenes Layout hinterlegen.
Das wäre bei dir der Fall , einmal Text , den Switch und irgendwas was anderes dazu ( also z.b. noch einen Button)

in getView kannst du dann sowas hier machen

myView = mInflater.inflate(R.layout.myRowView, null);

Und hast dadurch dann Zugriff auf die Layoutelemente ( findViewById ) der Reihe

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

16.09.2016, 10:02:34 via Website

Hast du ein einfaches Beispiel von einem funktionierenden CustomAdapter, der vom BaseAdapter extended?

— geändert am 16.09.2016, 10:02:51

Antworten
Norman L.
  • Forum-Beiträge: 20

16.09.2016, 10:08:01 via Website

Nö, da extended man ja immer ArrayAdapter. Oder hab ich was übersehen?

EDIT: Schon gut, ich habs hinbekommen! Mir fehlte nur noch das "notifyDataSetChanged()" bei der "add"-Methode im CustomAdapter!

— geändert am 16.09.2016, 10:20:27

Antworten
swa00
  • Forum-Beiträge: 3.704

16.09.2016, 10:18:02 via Website

und wie wäre es mit dem Punkt 2.3 ? :-)

Also ich finde X Beispiele im netz ..... mir ist es schon fast ein Rätsel, warum du keine findest :-)
http://abhiandroid.com/ui/baseadapter-tutorial-example.html

Hinweis : ArrayAdapter leitet sich von Basadapter ab (extends)

— geändert am 16.09.2016, 10:19:17

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
swa00
  • Forum-Beiträge: 3.704

16.09.2016, 10:22:27 via Website

EDIT: Schon gut, ich habs hinbekommen! Mir fehlte nur das "notifyDataSetChanged()" bei der "add"-Methode im CustomAdapter!

Das Notify führt man am besten erst dann aus , wenn die Daten alle drin sind .
Notify bewirkt, dass ein Refresh durchgeführt wird . Haust du da X einträge hintereinander rein,
gibt es Performanceprobleme

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

16.09.2016, 11:31:58 via Website

Wie würdest du denn die ganzen Alarme, die ja in einer ArrayList aus "CustomItemStructure"-Klassen gespeichert sind, am besten dauerhaft speichern? SharedPreferences oder doch schon eine kleine SQLite Datenbank? Oder JSON?

Antworten
swa00
  • Forum-Beiträge: 3.704

16.09.2016, 11:35:23 via Website

JSON ist eher eine Übergabeform.

Wenn du es vernünftig machen magst , dann würde ich zu einer sql raten .
Eine schöne lokale Datei, die ist auch noch da , wenn man die App deiinstalliert und das Backup ist auch
recht einfach zu gestalten ..

Um den Bogen zu überspannen , könnte man diese SQL auch noch im G-Drive unter dem Account sichern :-)

— geändert am 16.09.2016, 11:36:41

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

19.09.2016, 13:39:41 via Website

Soo...
Datenbank funktioniert (finally!)

Jetzt möchte ich den Alarm in der richtigen Zeit und jeden Tag klingeln lassen. Da muss ich beim AlarmManager "setRepeating", "RTC_WAKEUP" und "INTERVAL_DAY" nehmen, wenn ich das richtig verstanden habe, korrekt?

Antworten
swa00
  • Forum-Beiträge: 3.704

19.09.2016, 13:57:09 via Website

Hallo Norman ,

herzlichen Glückwunsch , du machst ja echte Fortschritte :-)

Hier wird das recht gut erklärt
https://developer.android.com/training/scheduling/alarms.html

lg
Stefan

Liebe Grüße - Stefan
[ App - Entwicklung ]

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

19.09.2016, 14:19:08 via Website

Um den Alarm zu canceln, brauche ich ja den Pending intent davon:

alarmMgr.cancel(alarmIntent);

Also brauche ich für jeden Alarm einen neuen PendingIntent. Wie kann ich den denn jedes mal anders bezeichnen?

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

19.09.2016, 14:31:09 via Website

Solagne es immer das lgeiche intent ist, musst du garnichts machen:
https://developer.android.com/reference/android/app/AlarmManager.html#cancel(android.app.PendingIntent)
Danach werden mit cancel alle Alarme bei welchen dieses Intent dahintersteckt abgebrochen.
Hast du mehrere verschiedenen Intents musst du diese halt in mehreren Varaible deklarieren und dann mehrere cancel machen.

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

19.09.2016, 14:58:40 via Website

Der Intent ist eben nicht immer der gleiche, sondern bei jedem Alarm ein anderer, das ist es ja. Macht ja keinen Sinn, alle Alarme zu canceln, wenn nur einer aus der Liste gelöscht wird.

Ich hab das jetzt so gelöst, dass ich den PendingIntent ganz einfach in meiner CustomItemStructure (solche Objekte sind in der ArrayList für den ListView der Alarm-Liste) hinzugefügt habe:

public class CustomItemStructure{
public boolean alarmSwitch;
public long alarmTimeInMillis;
public PendingIntent pendingIntent;

public CustomItemStructure(long timeInMillis, boolean switchState, Context context){
    alarmTimeInMillis = timeInMillis;
    alarmSwitch = switchState;
    Intent intent = new Intent(context, RingService.class);
    pendingIntent = PendingIntent.getService(context, 0, intent, 0);
}

}

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

19.09.2016, 15:10:06 via Website

Genau, sollte auch funktionieren.
Ist die frage was passiert nach einem App neustart?
Speicherst du dir die Objekte irgendwo?

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

19.09.2016, 15:17:11 via Website

Hmm... Stimmt.
Bisher speichere ich Alarmzeit und Button-Status in einer SQLite Datenbank. Die PendingIntents von den Objekten wären ja dann weg. Vorschläge?

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

19.09.2016, 15:27:42 via App

Klassenname speichern und dynamisch wiederherstellen

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

19.09.2016, 15:37:16 via Website

Pascal P.

Klassenname speichern und dynamisch wiederherstellen

Ich verstehe nicht ganz

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

19.09.2016, 17:49:46 via Website

Naja du kannst über den Klassennamen das Intent ja wiederherstellen, dann hast du das problem nicht:

String intentName = "de.mein.packacke.meineKlasse";
Class<?> cls = Class.forName(intentName);   //Catch Exception if class not exists
Intent intent = new Intent(context,cls);
 PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAGs);

So hast du ein PendingIntent wieder. Du musst nur den vollen Klassennamen pit Package und die Intentflags in deiner DB speichern, dann kannst du meit leicher abwandlung meines Code das Intent wiederherstellen.

Beim laden benötigst du dann allerdings fürs Intent einen Context

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

20.09.2016, 08:58:53 via Website

Ich weiß nicht, welchen Klassennamen du da meinst (???). Die Klasse der Objekte, die in die ArrayList geladen werden (also CustomItemStructure)? Wäre ja dann immer der gleiche - warum also jedes mal speichern?

Und ansonsten unterscheiden sich die einzelnen (Pending-)Intents durch die Flags?

Sorry, ich verstehe nur Bahnhof gerade :D:D

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

20.09.2016, 12:21:45 via Website

Dann muss ich das wohl etwas genauer ausführen:

Du hast Pending Intents, welcher du an einen AlarmManager übergibst und diese zusätzlich ion einer Variable deiner CustomItemStructure Klasse speicherst.

Wenn du jetzt die App schließt, wird die Liste der CustomItemStructure gespeichert, aber ohne PendingIntent, d.h. nach einem neustart der App kannst du den Alarm nicht mehr abbrechen.

Um das Problem zu lösen musst du die Pending Intents wiederherstellen.
Dazu benötigst du meinen Code oben. Den genannten Klassennamen ist das Intent Ziel, d.h. wenn das PendingIntent einer Instanz von CustomItemStructure auf die Activity "ActivityTest1" Zeigt, dann musst der Klassenname "de.meinPackage.ActivityTest1" heißen, damit aus diesem String das Intent auf diese Klasse (indem fall Activity) wiederhergestellt werden kann.

Zusammengefasst: Du hast deine Intent "Ziele", diese werden mit vollem Klassennamen als string gespeichert um später aus dem String wieder ein Intent mit diesem bestimten Ziel erstellen zu können. Damit kannst du dann auch nach einem App neustart noch alarme beenden. Du musst nur wissen, welche noch aktiv sind.

Jetzt etwas klarer geworden?

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

20.09.2016, 13:22:00 via Website

Aber beendet dann nicht der AlarmManager, wenn man nur einen Alarm löscht, gleich alle Alarme? Weil die Intents haben ja alle das Ziel "RingService.class".

Also unterscheidet man dann nach Flags oder wie?

— geändert am 20.09.2016, 13:25:41

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

20.09.2016, 13:30:06 via Website

Das ist das was ich dich oben gefragt habe:
Da hast du gemeint:

Der Intent ist eben nicht immer der gleiche, sondern bei jedem Alarm ein anderer, das ist es ja. Macht ja keinen Sinn, alle Alarme zu canceln, wenn nur einer aus der Liste gelöscht wird.

Da alle das Zeil RingService.class haben, kannst du nur alle oder garkeinen löschen.
Das ist natürlich nicht gut...

Du kannst eine "ignorieren" liste führen, wo du dir die Zeitpunkte sepicherst, welche Ignoriert werden sollen.

Noch einfacher: Du gibtst dem PendingIntent eine Einduetige ID:http://stackoverflow.com/questions/8877365/cancelling-a-single-alarm-when-you-have-multiple-alarms
Dann kannst du jeden Alarm individuell canceln, abe die Login zum speichern (mit id) brauchst du trotzdem

— geändert am 20.09.2016, 13:30:36

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

20.09.2016, 15:08:28 via Website

Okay, dann habe ich dich oben falsch verstanden, tut mir leid.

Jetzt ist nur noch die Frage, wie diese eindeutige ID ermittelt wird. Ich wollte eben einfach die Position in der ArrayList nehmen, aber das ist Käse, denn sobald ein Alarm gelöscht wird, ändern sich alle Positionen in der ArrayList, die darunter liegen... Ok, das hab ich jetzt gelöst! Hab einfach die Uhrzeit an sich genommen.

Dann hab ich auch noch nicht ganz verstanden, wie das mit der Uhrzeit funktioniert. Wenn ich dem AlarmManager eine Uhrzeit gebe, bei der er das erste mal klingeln soll, sind das dann die Millisekunden ab 1970 (= Systemzeit + Differenz zum ersten mal Klingeln) oder aber die Millisekunden ab Tagesbeginn?

Weil irgendwie kommt der Alarm nicht direkt. Hab eine Minute später als jetzt gerade gesetzt und es kam nichts.

Calendar timeCalendar = Calendar.getInstance();
            timeCalendar.setTimeInMillis(System.currentTimeMillis());
            timeCalendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
            timeCalendar.set(Calendar.MINUTE, minute);
customAdapter.addAlarm(timeCalendar.getTimeInMillis(), true, MainActivity.this);

Dann später im CustomAdapter wirds hinzugefügt:

AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, item.alarmTimeInMillis,
            AlarmManager.INTERVAL_DAY, item.pendingIntent);

— geändert am 20.09.2016, 15:50:02

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

20.09.2016, 15:48:11 via App

Als ID würde ich auch die Zeit nehmen oder das orgendwie verrechnen.
Die Alarm Trigger Zeit sind die millisekunden mach dem 1.1.1970. Dazu einfach System.currentMillis() plus die Zeitdifferenz in ms.

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.

Antworten
Norman L.
  • Forum-Beiträge: 20

21.09.2016, 08:35:09 via Website

@Pascal kannst du nicht mal auf meine Github Seite gehen und mit sagen, warum kein Alarm kommt? Du siehst das sicherlich direkt. Wäre super nett von dir :)

Hab schon alles probiert, aber ich finde den Fehler einfach nicht :( Bin so kurz davor...^^

github/pixelfehler1996/Alarm_Clock

Antworten
Norman L.
  • Forum-Beiträge: 20

21.09.2016, 14:05:48 via Website

Ich hab es doch noch zum laufen bekommen!!! :)

Der Fehler war, dass sich der Service, der beim Alarm gestartet wird, nicht selbst beendet. Außerdem brauchte ich noch die Permission: WAKE_LOCK

Allerdings hab ich es jetzt nochmal geändert und über den BroadcastReceiver gelöst. Also:
System sendet Broadcast an -> BroadcastReceiver -> Wecker klingelt

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

21.09.2016, 14:19:19 via Website

Habe mir am einfachsten mal das ganze Projekt geladen und analysiert, dabei sind mir ein paar sachen aufgefallen:

  1. Dein PendingIntent erzeugst du mit PendingIntent.getBroadcast(...), das funktioniert aber nicht da deine RingService Klasse kein BroadcastReceiver ist sondern ein Service. Dafür muss das PendingIntent mit getService(...) erstellt werden.
  2. Du verwendest beim AlarmManager ein setRepeating, ist das Absicht? Wenn ja dann ok, denn der Alarm wird dann immer "gemeldet" nach diesem Intervall. Zudem würde das Ausschalten nichts bringen.
  3. Bei mir wirft nach den Änderungen 1&2 der MediaPlayer im Service errors. Wenn ich den auskommentiere funktioniert alles, an der enstsprechenden Uhrzeit, kommt der Log eintrag.

PS: Ein neuer Alarm per Button wird der Dialog immer mit der App StartZeit angezeigt, da du deine Calendar.getInstance nur in der onCreate beim starten ausführst.

Ich hoffe du kommst damit weiter ;)

LG Pascal //It's not a bug, it's a feature. :) ;)

Norman L.swa00

Antworten
Norman L.
  • Forum-Beiträge: 20

21.09.2016, 14:47:17 via Website

Du verwendest beim AlarmManager ein setRepeating, ist das Absicht? Wenn ja dann ok, denn der Alarm wird dann immer "gemeldet" nach diesem Intervall. Zudem würde das Ausschalten nichts bringen.

Ja, ist Absicht, weil der Wecker ja am nächsten Tag auch wieder klingeln soll. Geht der dann nicht mit alarmManager.cancel(...) abzubrechen?

Bei mir wirft nach den Änderungen 1&2 der MediaPlayer im Service errors. Wenn ich den auskommentiere funktioniert alles, an der enstsprechenden Uhrzeit, kommt der Log eintrag.

Wenn ich über den BroadcastReceiver gehe, funktioniert das wieder.

PS: Ein neuer Alarm per Button wird der Dialog immer mit der App StartZeit angezeigt, da du deine Calendar.getInstance nur in der onCreate beim starten ausführst.

Stimmt, danke :)

Ich hoffe du kommst damit weiter ;)

Aber sowas von, vielen Dank! ;)

Antworten