Aktuelle CallLogs ausgeben

S

sem_thesis

Fortgeschrittenes Mitglied
0
Hallo zusammen!


Ich arbeite noch immer an meinem CallLog-Service, der aktiviert wird, sobald der Telefon-Zustand (Variable PHONE_STATE) verändert wird.

Dazu habe ich die folgende Listener-Klasse implementiert:
Code:
    //////// Private Class MyPhoneStateListener /////////
    
    private class MyPhoneStateListener extends PhoneStateListener {
        

        // This method is a Listener on the Phone State: Whenever it changes --> a new query is made to Call Logs
          
        public void onCallStateChanged(int state, String incomingNumber){
              
               Log.d(TAG, "STATE HAS CHANGED!");
               
              // Register New Phone Logs  
              
              switch(state){
                  case TelephonyManager.CALL_STATE_IDLE:
                      Log.d(TAG, "Phone is in idle state.");
                      CollectAndSaveLogs();
                      break;
                  case TelephonyManager.CALL_STATE_OFFHOOK:
                      Log.d(TAG, "Phone-User has initiated/accepted a call");
                      break;
                  case TelephonyManager.CALL_STATE_RINGING:
                    Log.d(TAG, "Phone-User has received a call");
                    break;
              }
              
              

              
          }

    }
    
    
    
        public void CollectAndSaveLogs()    {
            
            // Declare all values, which need to be logged
            
            String date = new String();
            String time = new String();        
            String name = new String();
            String phonenumber = new String();
            String duration = new String();
            String callclassification = new String();
            String d = new String();
            
            
            
          // Collect new Phone Logs //
          
          Cursor c = getContentResolver().query(
                       android.provider.CallLog.Calls.CONTENT_URI,
                       null,
                       null,
                       null,
                       null);
            
          c.moveToFirst();
          
           date = formatDate.format(new Date());
           time = formatTime.format(new Date());
            name = c.getString(c.getColumnIndex(CallLog.Calls.CACHED_NAME));
            phonenumber = c.getString(c.getColumnIndex(CallLog.Calls.NUMBER));
            d = c.getString(c.getColumnIndex(CallLog.Calls.DURATION));
            duration = formatDuration(d);
            callclassification = c.getString(c.getColumnIndex(CallLog.Calls.TYPE));
      
              Log.d(TAG, date);
              Log.d(TAG, time);
              Log.d(TAG, name);
              Log.d(TAG, phonenumber);
              Log.d(TAG, duration);
              Log.d(TAG, callclassification);
              
              c.close();
              
          // Write new Phone Logs in Database //
          
          db.open();
          db.insertCallLogs(date, time, name, phonenumber, duration, callclassification);
          db.close();
        
        }
Sobald der Phone in den inaktiven IDLE Zustand gelangt, sollte die Funktion CollectAndSaveLogs()jedesmal NEU aufgerufen werden und die neuesten Daten aufsammeln und in die Datenbank schreiben.

Beim Testen habe ich jedoch gemerkt, dass einfach der erste Call (name, nummer, calltype) gespeichert wird. Der Listener wird immer wieder aufgerufen, doch die Logs geben immer noch die alten Werte an.

Was soll das? Muss ich den cursor weiterbewegen mit moveToNext()? Oder muss ich eine andere Sortiermethode für den Cursor wählen (ASC und DESC - hab's probiert, aber es hat trotzdem nicht funktioniert).

PS: Code ist im Attachment
 

Anhänge

  • MyCallLog.zip
    59,3 KB · Aufrufe: 65
Zuletzt bearbeitet:
sem_thesis schrieb:
Hallo zusammen!


Ich arbeite noch immer an meinem CallLog-Service, der aktiviert wird, sobald der Telefon-Zustand (Variable PHONE_STATE) verändert wird.

Dazu habe ich die folgende Listener-Klasse implementiert:
Code:
    //////// Private Class MyPhoneStateListener /////////
    
    private class MyPhoneStateListener extends PhoneStateListener {
        

        // This method is a Listener on the Phone State: Whenever it changes --> a new query is made to Call Logs
          
        public void onCallStateChanged(int state, String incomingNumber){
              
               Log.d(TAG, "STATE HAS CHANGED!");
               
              // Register New Phone Logs  
              
              switch(state){
                  case TelephonyManager.CALL_STATE_IDLE:
                      Log.d(TAG, "Phone is in idle state.");
                      CollectAndSaveLogs();
                      break;
                  case TelephonyManager.CALL_STATE_OFFHOOK:
                      Log.d(TAG, "Phone-User has initiated/accepted a call");
                      break;
                  case TelephonyManager.CALL_STATE_RINGING:
                    Log.d(TAG, "Phone-User has received a call");
                    break;
              }
              
              

              
          }

    }
    
    
    
        public void CollectAndSaveLogs()    {
            
            // Declare all values, which need to be logged
            
            String date = new String();
            String time = new String();        
            String name = new String();
            String phonenumber = new String();
            String duration = new String();
            String callclassification = new String();
            String d = new String();
            
            
            
          // Collect new Phone Logs //
          
          Cursor c = getContentResolver().query(
                       android.provider.CallLog.Calls.CONTENT_URI,
                       null,
                       null,
                       null,
                       null);
            
          c.moveToFirst();
          
           date = formatDate.format(new Date());
           time = formatTime.format(new Date());
            name = c.getString(c.getColumnIndex(CallLog.Calls.CACHED_NAME));
            phonenumber = c.getString(c.getColumnIndex(CallLog.Calls.NUMBER));
            d = c.getString(c.getColumnIndex(CallLog.Calls.DURATION));
            duration = formatDuration(d);
            callclassification = c.getString(c.getColumnIndex(CallLog.Calls.TYPE));
      
              Log.d(TAG, date);
              Log.d(TAG, time);
              Log.d(TAG, name);
              Log.d(TAG, phonenumber);
              Log.d(TAG, duration);
              Log.d(TAG, callclassification);
              
              c.close();
              
          // Write new Phone Logs in Database //
          
          db.open();
          db.insertCallLogs(date, time, name, phonenumber, duration, callclassification);
          db.close();
        
        }
Sobald der Phone in den inaktiven IDLE Zustand gelangt, sollte die Funktion CollectAndSaveLogs()jedesmal NEU aufgerufen werden und die neuesten Daten aufsammeln und in die Datenbank schreiben.

Beim Testen habe ich jedoch gemerkt, dass einfach der erste Call (name, nummer, calltype) gespeichert wird. Der Listener wird immer wieder aufgerufen, doch die Logs geben immer noch die alten Werte an.

Was soll das? Muss ich den cursor weiterbewegen mit moveToNext()? Oder muss ich eine andere Sortiermethode für den Cursor wählen (ASC und DESC - hab's probiert, aber es hat trotzdem nicht funktioniert).

PS: Code ist im Attachment
Warte nach dem State-Wechsel nach IDLE einfach etwas (ich benutze 5s als Wartezeit). Wenn der Benutzer auflegt, muss die Schreibe-Datensatz-in-Datenbank-Magie, die ja auch von einem PhoneStateListener getriggert wird, ja auch erstmal laufen.
 
Ok, guter Ansatz.
Hab's mit dem Thread versucht.

Code:
              switch(state){
                  case TelephonyManager.CALL_STATE_IDLE:
                      Log.d(TAG, "Phone is in idle state.");
                try {
                    Thread.sleep(5000);
                      CollectAndSaveLogs();                    
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                      break;
                  case TelephonyManager.CALL_STATE_OFFHOOK:
                      Log.d(TAG, "Phone-User has initiated/accepted a call");
                      break;
                  case TelephonyManager.CALL_STATE_RINGING:
                    Log.d(TAG, "Phone-User has received a call");
                    break;
              }
Geht aber immer noch nicht..
 
Zuletzt bearbeitet:
sem_thesis schrieb:
Ok, guter Ansatz.
Hab's mit dem Thread versucht.

Code:
              switch(state){
                  case TelephonyManager.CALL_STATE_IDLE:
                      Log.d(TAG, "Phone is in idle state.");
                try {
                    Thread.sleep(5000);
                      CollectAndSaveLogs();                    
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                      break;
                  case TelephonyManager.CALL_STATE_OFFHOOK:
                      Log.d(TAG, "Phone-User has initiated/accepted a call");
                      break;
                  case TelephonyManager.CALL_STATE_RINGING:
                    Log.d(TAG, "Phone-User has received a call");
                    break;
              }
Geht aber immer noch nicht..
Ja klar, damit hast du ja auch nur den Thread, der die Notifications verteilt, aufgehalten, aber nicht dafür gesorgt, dass die Datenbankeinträge schon mal passieren können.

Stichworte (du willst ja für deine Semesterarbeit auch noch was lernen :D ) sind AlarmManager und PendingIntent. Die eigentliche Arbeit (Datenbankabfrage, Eintragen der Daten in deine Datenbank) solltest du ohnehin mit einem (Intent-)Service abhandeln (in einem Receiver sollte man nix tun, was lange dauert), und diesen Service kannst du dann über den Intent aufrufen.
 
Hmm... ok ich hatte schon einen BroadcastReceiver, der den Sercice aktiviert, falls der User das so gewünscht hat.
Dann muss ich also einen zweiten Receiver einabauen?

Ok, ich hab's jetzt so gemacht:

1) ServiceEnabler.java: Die Klasse, die den Service aktiviert, falls dies vom User so gewünscht ist.
Code:
public class ServiceEnabler extends BroadcastReceiver {
    
    private static final String TAG = ServiceEnabler.class.getSimpleName();
    boolean prefPhone = true;
    
    
    @Override
    public void onReceive(Context context, Intent intent) 
    {
            if (prefPhone) {
                  Log.d(TAG, "Service is activated!");
                  Intent p = new Intent();
                p.setAction("com.example.mylog.PhoneServiceReceiver");
                context.startService(p);
            }
    }
    
}
2) PhoneServiceReceiver.java: Dies ist die Service-Klasse welche einen Listener erstellt und danach implementiert. Der eigentliche PhoneService wird als PendingIntent mit dem Alarmmanger zur Zeit "SystemClock.elapsedRealtime() + sleep" aufgerufen.

Code:
public class PhoneServiceReceiver extends Service {
    
    // Log-Tag for debugging
    private static final String TAG = PhoneService.class.getSimpleName();

    // Create Telephony + Listener variables
    public TelephonyManager telephony;
    public MyPhoneServiceListener phoneListener;
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        
           // Put a Listener on the Phone-State

        telephony = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
        phoneListener = new MyPhoneServiceListener();
        telephony.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);
        
    }
    
    
    private class MyPhoneServiceListener extends PhoneStateListener {



    // This method is a Listener on the Phone State: Whenever it changes --> a new query is made to Call Logs
        
        public void onCallStateChanged(int state, String incomingNumber)    {
          
            Log.d(TAG, "STATE HAS CHANGED!");
           
            // Register New Phone Logs  
          
            switch(state){
                  case TelephonyManager.CALL_STATE_IDLE:
                      Log.d(TAG, "Phone is in idle state.");
                      
                      // Hier comes the actual Service-Functionality
                      
                      Intent pService = new Intent();
                    pService.setAction("com.example.mylog.PhoneService");

                      int sleep = 5000; // Waiting time before Log-database is accessed
                      int flags = 0;
                      
                      PendingIntent piSync = PendingIntent.getService(getBaseContext(), 0, pService, flags);
                      Log.i(TAG, "schedulePendingSync: " + piSync.toString());
                      AlarmManager alarm = (AlarmManager) getBaseContext().getSystemService(Activity.ALARM_SERVICE);
                      alarm.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + sleep, piSync);

                      // Endof Service-Functionality

                      
                      break;
                  case TelephonyManager.CALL_STATE_OFFHOOK:
                      Log.d(TAG, "Phone-User has initiated/accepted a call");
                      break;
                  case TelephonyManager.CALL_STATE_RINGING:
                      Log.d(TAG, "Phone-User has received a call");
                      break;
          }
           
      }

    }
    
}
3) PhoneService.java: Die eigentliche Service Klasse - liest Logs und schreibt sie in eine Datenbank (enthält 3 Formattierungsfunktionen für Zeit/Datum/Dauer/Typ)
Code:
public class PhoneService extends Service {
    
    private static final String TAG = PhoneService.class.getSimpleName();
    public DBAdapter db;


    
    // Specify formatting of data and time --> looks awesome :D
    SimpleDateFormat formatDate = new SimpleDateFormat("dd-MM-yyyy");
    SimpleDateFormat formatTime = new SimpleDateFormat("HH:mm:ss");
    
    
    // Specify formatting for duration --> looks even more awesome :D    
    public String formatDuration(String input) {
        
        long seconds = Long.parseLong(input);
        StringBuffer buf = new StringBuffer();
        Log.d(TAG, input);

        int h = (int) Math.floor(seconds / 3600);
        int m = (int) Math.floor((seconds - h * 3600) / 60);
        int s = (int) seconds - h - m;

        buf
            .append(String.format("%02d", h))
            .append(":")
            .append(String.format("%02d", m))
            .append(":")
            .append(String.format("%02d", s));

        return buf.toString();
    }    
    
    // Specify interpretation of Calltype
    public String callType(int t)    {
        
        if (t == 0)            return "Outgoing Call";
        else if (t == 1)    return "Incoming Call";
        else if (t == 2)    return "Missed Call";
        return null;

    }
    
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
            
          // Create Database
        db = new DBAdapter(getApplicationContext());
        
        
        // Declare all values, which need to be logged
        
        String date = new String();
        String time = new String();        
        String name = new String();
        String phonenumber = new String();
        String duration = new String();
        String callclassification = new String();
        String d = new String();
        int e;
        
        
        
        // Collect new Phone Logs //
      
        Cursor c = getContentResolver().query(
                   android.provider.CallLog.Calls.CONTENT_URI,
                   null,
                   null,
                   null,
                   android.provider.CallLog.Calls.DATE + " ASC");
        
        c.moveToFirst();
      
        date = formatDate.format(new Date());
        time = formatTime.format(new Date());
            name = c.getString(c.getColumnIndex(CallLog.Calls.CACHED_NAME));
            phonenumber = c.getString(c.getColumnIndex(CallLog.Calls.NUMBER));
            d = c.getString(c.getColumnIndex(CallLog.Calls.DURATION));
            duration = formatDuration(d);
            e = c.getColumnIndex(CallLog.Calls.TYPE);
            callclassification = callType(e);
  
          Log.d(TAG, date);
          Log.d(TAG, time);
          Log.d(TAG, name);
          Log.d(TAG, phonenumber);
          Log.d(TAG, duration);
          Log.d(TAG, callclassification);
          
          c.close();
          
          // Write new Phone Logs in Database //
      
          db.open();
          db.insertCallLogs(date, time, name, phonenumber, duration, callclassification);
          db.close();
}
}
Oke... und Eclipse zeigt keine (Syntax-)Fehler an. Die App crasht bei einem Anruf sofort.
Wo liegt der konzeptienelle Fehler?


PS: Im AndroidManifest.xml habe ich PhoneServiceReceiver und PhoneService als Services und ServiceEnabler als Receiver eingetragen. Muss ich sonst was eintragen?
 

Anhänge

  • MyCallLog.zip
    62,4 KB · Aufrufe: 61
Zuletzt bearbeitet:
sem_thesis schrieb:
2) PhoneServiceReceiver.java: Dies ist die Service-Klasse welche einen Listener erstellt und danach implementiert. Der eigentliche PhoneService wird als PendingIntent mit dem Alarmmanger zur Zeit "SystemClock.elapsedRealtime() + sleep" aufgerufen.
Der Service-Teil davon sieht mir überflüssig aus ... warum registrierst du den Listener nicht direkt im Manifest (halt nicht als PhoneStateListener, sondern als BroadcastReceiver auf android.intent.action.PHONE_STATE)?

Code:
                      Intent pService = new Intent();
                    pService.setAction("com.example.mylog.PhoneService");

                      int sleep = 5000; // Waiting time before Log-database is accessed
                      int flags = 0;
                      
                      PendingIntent piSync = PendingIntent.getService(getBaseContext(), 0, pService, flags);
                      Log.i(TAG, "schedulePendingSync: " + piSync.toString());
                      AlarmManager alarm = (AlarmManager) getBaseContext().getSystemService(Activity.ALARM_SERVICE);
                      alarm.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + sleep, piSync);
Sieht soweit ok aus. Den Intent würde ich aber so initialisieren:
Code:
Intent pService = new Intent(context, PhoneService.class);
Oke... und Eclipse zeigt keine (Syntax-)Fehler an. Die App crasht bei einem Anruf sofort.
Wo liegt der konzeptienelle Fehler?
Es wäre hilfreich, wenn du den Backtrace (aus dem Logcat) posten könntest ;)
 
Jetzt habe ich die folgende Fehlermeldung bekommen
E/AndroidRuntime( 1063): FATAL EXCEPTION: main
E/AndroidRuntime( 1063): java.lang.RuntimeException: Unable to create service com.example.mylog.PhoneService: java.lang.NullPointerException: println needs a message
E/AndroidRuntime( 1063): at android.app.ActivityThread.handleCreateService(ActivityThread.java:1955)
E/AndroidRuntime( 1063): at android.app.ActivityThread.access$2500(ActivityThread.java:117)
E/AndroidRuntime( 1063): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:985)
E/AndroidRuntime( 1063): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 1063): at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 1063): at android.app.ActivityThread.main(ActivityThread.java:3683)
E/AndroidRuntime( 1063): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 1063): at java.lang.reflect.Method.invoke(Method.java:507)
E/AndroidRuntime( 1063): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
E/AndroidRuntime( 1063): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
E/AndroidRuntime( 1063): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 1063): Caused by: java.lang.NullPointerException: println needs a message
E/AndroidRuntime( 1063): at android.util.Log.println_native(Native Method)
E/AndroidRuntime( 1063): at android.util.Log.d(Log.java:137)
E/AndroidRuntime( 1063): at com.example.mylog.PhoneService.onCreate(PhoneService.java:110)
E/AndroidRuntime( 1063): at android.app.ActivityThread.handleCreateService(ActivityThread.java:1945)
E/AndroidRuntime( 1063): ... 10 more
CallClassification wird gar nicht geloggt!
Es werden immer noch die alte Nummer/Name/Dauer im Log angezeigt.
 
Zuletzt bearbeitet:
maniac103 schrieb:
Der Service-Teil davon sieht mir überflüssig aus ... warum registrierst du den Listener nicht direkt im Manifest (halt nicht als PhoneStateListener, sondern als BroadcastReceiver auf android.intent.action.PHONE_STATE)?

Verstehe nicht ganz was du meinst.

Das Manifest-File sieht bei mir so aus:
Code:
......
       <!-- Service Declaration Area -->
        
        <service android:name ="com.example.mylog.PhoneServiceReceiver">
            <intent-filter>
                <action android:name="com.example.mylog.PhoneServiceReceiver">
                </action>
            </intent-filter>
        </service>
 
         <service android:name ="com.example.mylog.PhoneService">
            <intent-filter>
                <action android:name="com.example.mylog.PhoneService">
                </action>
            </intent-filter>
        </service>
               
        <!-- Receiver Declaration Area -->
        
        <receiver android:name=".ServiceEnabler">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
        </receiver>

    </application>
 
Hello again!

Ich hab' einen ganz dummen Fehler im Code entdeckt, aus dem ich nicht schlau werden:

Code:
    // Specify interpretation of Call-Type
    public String callType(String t)    {
        
        String g = null;

        if (t == "1")    g = "Incoming Call";
        if (t == "2")    g = "Outgoing Call";
        if (t == "3")    g = "Missed Call";

        return g;
    }




public onStartCommand(Intent intent, int flags, int startId) {

        // Collect new Phone Logs //
          
        Cursor c = getContentResolver().query(
                       android.provider.CallLog.Calls.CONTENT_URI,
                       null,
                       null,
                       null,
                       android.provider.CallLog.Calls.DATE+ " DESC");
         c.moveToFirst();

         String t = c.getString(c.getColumnIndex(CallLog.Calls.TYPE));
         String callclassification = callType(t);


         // Log new Phone Logs

          Log.d(TAG, callclassification);



return startId;
}


Der Log wird nicht ausgeführt - was ist an der convertType Funktion falsch gelaufen?

Referenz: http://developer.android.com/reference/android/provider/CallLog.Calls.html
 
Zuletzt bearbeitet:
Ok, habe das Problem selber gelöst (ist ja eine Semesterarbeit, also muss ich ja selber auch was tun :winki:

string == string vergleicht anscheinend Objekte, aber keine String.

Ich habe die Funktion convertType folgendermassen umgeschrieben:

Code:
    // Specify interpretation of Call-Type
       public String callType(String t)    {
        
        String g = null;

        if ("1".equals(t))    g = "Incoming Call";
        else if ("2".equals(t))    g = "Outgoing Call";
        else if ("3".equals(t))    g = "Missed Call";

        return g;   
    }
 
sem_thesis schrieb:
Ok, habe das Problem selber gelöst (ist ja eine Semesterarbeit, also muss ich ja selber auch was tun :winki:
Richtiger Ansatz ;)

Ich habe die Funktion convertType folgendermassen umgeschrieben:

Code:
    // Specify interpretation of Call-Type
       public String callType(String t)    {
        
        String g = null;

        if ("1".equals(t))    g = "Incoming Call";
        else if ("2".equals(t))    g = "Outgoing Call";
        else if ("3".equals(t))    g = "Missed Call";

        return g;   
    }
Da besteht aber noch Optimierungspotenzial ;)
Da ja der Anruftyp laut Doku Integer ist, würde ich ihn mit c.getInt() auslesen und dann mit den - genau zu diesem Zweck vorhandenen - Konstanten direkt vergleichen.
 
sem_thesis schrieb:
Verstehe nicht ganz was du meinst.
Ist doch eigentlich ganz einfach. Momentan hast du
- einen Service, der einen
- PhoneStateListener registriert, der wiederum
- einen Service aufruft (welcher die Daten verarbeitet)

Einfacher wäre es, wenn du
- im Manifest einen BroadcastReceiver auf
android.intent.action.PHONE_STATE registrierst, der dann
- den Datenverarbeitungsservice aufruft

Ergebnis: 1 dauerhaft laufenden Service gespart

Das Manifest-File sieht bei mir so aus:
Code:
......
        <!-- Receiver Declaration Area -->
        
        <receiver android:name=".ServiceEnabler">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
        </receiver>

    </application>
Das ist doch Quatsch. Wenn ein Anruf eingeht, startet dein Receiver, schaut nach, ob das Logging aktiv ist und startet einen Service, der einen Anruflistener registriert (?)

In meiner App schaue ich im PhoneState-BroadcastReceiver nach, ob das ganze eingeschaltet ist, und starte den Datenverarbeitungsservice nur, wenn das der Fall ist.
 
maniac103 schrieb:
In meiner App schaue ich im PhoneState-BroadcastReceiver nach, ob das ganze eingeschaltet ist, und starte den Datenverarbeitungsservice nur, wenn das der Fall ist.

Ok. Punkt akzeptiert.

Ich bin mir sowieso am Überlegen, ob es eine gute Idee ist den PHONE_STATE Listener zu gebrauchen.
Ich habe bis jetzt 3 Services implementiert: GPS (benutzt ACTION_BOOT_COMPLETED Listener), Contacts-Service (hat keinen Listener) und CallLog-Service (benutzt PHONE_STATE Listener).

Macht es von Batterie/Speicher sparen nicht mehr Sinn periodische Services zu haben, die Daten regulär (GPS: je 2h, Contacts-Service: jede Woche, CallLog-Service: jeden Tag) sammeln?
Da könnte man auf einen Listener verzichten und einfach einen Receiver einbauen, der direkt nach Serviceinstallation (+ Bedingung: User hät Präferenz eingeschaltet) den Service startet?

Ist so was möglich?
 
sem_thesis schrieb:
Ich bin mir sowieso am Überlegen, ob es eine gute Idee ist den PHONE_STATE Listener zu gebrauchen.
Ich habe bis jetzt 3 Services implementiert: GPS (benutzt ACTION_BOOT_COMPLETED Listener), Contacts-Service (hat keinen Listener) und CallLog-Service (benutzt PHONE_STATE Listener).

Macht es von Batterie/Speicher sparen nicht mehr Sinn periodische Services zu haben, die Daten regulär (GPS: je 2h, Contacts-Service: jede Woche, CallLog-Service: jeden Tag) sammeln?
Da könnte man auf einen Listener verzichten und einfach einen Receiver einbauen, der direkt nach Serviceinstallation (+ Bedingung: User hät Präferenz eingeschaltet) den Service startet?

Ist so was möglich?
Klar, möglich ist so was (Service durch AlarmManager starten lassen). Ob es sinnvoll ist, kann ich dir aber nicht sagen, weil das von deiner Applikation abhängt. In meiner App synchronisiere ich das Anrufprotokoll in den Kalender, und da ist IMO der Listener sinnvoller (ich weiß da ja, wann neue Einträge kommen; die Einträge sollen außerdem möglichst schnell im Kalender aufschlagen).
Ein generelles 'besser' oder 'schlechter' kann man da nicht festlegen.
 
Ich habe mich für die Listener-Variante für den CallLogs Service entschieden.

maniac103 schrieb:
Einfacher wäre es, wenn du
1) im Manifest einen BroadcastReceiver auf
android.intent.action.PHONE_STATE registrierst, der dann
2) den Datenverarbeitungsservice aufruft

1.1) AndroidManifest.xml
Code:
<receiver android:name="com.socialmine.mergedservices.PhoneServiceReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
</receiver>

1.2) PhoneServiceReceiver.java
Code:
    @Override
    public void onReceive(Context context, Intent intent) 
    {
            // Define context globally to allow access from private class MyPhoneStateListener
        
            cName = context;
            
            // Put a Listener on the Phone-State

            telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            phoneListener = new MyPhoneStateListener();
            telephony.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);
            Log.d(TAG, "Listener created");

        
    }

1.3) MyPhoneStateListener
Code:
    //////// Private Class MyPhoneStateListener /////////
    
    private class MyPhoneStateListener extends PhoneStateListener {
        

          
          public void onCallStateChanged(int state,String incomingNumber){
              

              Log.d(TAG, "STATE HAS CHANGED!");

            switch(state)    {
                  case TelephonyManager.CALL_STATE_IDLE:
                      Log.d(TAG, "Phone is inactive - IDLE state");
                      
                    // Here comes the actual Service-Functionality
                    
                    Intent pService = new Intent();
                    pService.setAction("com.socialmine.mergedservices.PhoneService");

                    // Waiting time before Log-database is accessed (access only after new changes are stored)
                    int sleep = 5000; 
                    
                    PendingIntent piSync = PendingIntent.getService(cName, 0, pService, 0);
                    Log.i(TAG, "schedulePendingSync: " + piSync.toString());
                    AlarmManager alarm = (AlarmManager) cName.getSystemService(Activity.ALARM_SERVICE);
                    alarm.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + sleep, piSync);

                    // End of Service-Functionality

                      break;
                  case TelephonyManager.CALL_STATE_OFFHOOK:
                      Log.d(TAG, "Initiated/Accepted Call");
                      break;
                  case TelephonyManager.CALL_STATE_RINGING:
                    Log.d(TAG, "RINGING");
                      break;
              }
                          
              
          }

    }

2) Phoneservice.java

Code:
Datenverarbeitungs-Code von vorher



Ok - was nun passiert, ist, dass bei JEDEM Phone_STATE Wechsel (offhook, ringing, idle) der Receiver startet und eine neue Instanz der MyPhoneStateReceiver-Klasse erstellt wird.
Somit bekomme ich für jeden Anruf 2-3 Mal Logging + Datenbank-speicherung .. ausserdem crasht die App auch noch...

Ich will aber, dass die Verarbeitung nur EINMAL pro Gespräch geschieht.
Kann ich den Receiver direkt auf CALL_STATE_IDLE schalten?
 
sem_thesis schrieb:
Ok - was nun passiert, ist, dass bei JEDEM Phone_STATE Wechsel (offhook, ringing, idle) der Receiver startet und eine neue Instanz der MyPhoneStateReceiver-Klasse erstellt wird.
Somit bekomme ich für jeden Anruf 2-3 Mal Logging + Datenbank-speicherung .. ausserdem crasht die App auch noch...
Wenn du dir den Code anschaust, ist das von dir beobachtete Verhalten doch auch logisch, oder? ;)

Ich will aber, dass die Verarbeitung nur EINMAL pro Gespräch geschieht.
Kann ich den Receiver direkt auf CALL_STATE_IDLE schalten?
Ja klar, sag ich doch schon die ganze Zeit :)
Mal ein Beispiel aus meiner App:

Code:
public class PhoneEventReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

        if (state != null && state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
            /* es wurde aufgelegt, tue die nötigen Dinge */
        }
    }
}
Das Ganze noch im Manifest anmelden:

Code:
        <receiver android:label="@string/receiver_name" android:name="PhoneEventReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
        </receiver>
Fertig.
 
Achso - ja, ich hab' nicht gewusst, dass man einen Listener DIREKT auf den IDLE-State stellen kann.

Ja, so funktioniert das natürlich tadellos - danke :)

Ja klar, sag ich doch schon die ganze Zeit :)

Der Grund wieso ich einen 'Zwischen-Service' drin hatte, war, dass dieser ausser dem Listener noch eine CallHistory-Log Funktionalität hatte.

Ich habe also in der onCreate()-Method noch die alten Logs gesammelt.

Nun habe ich einfach eine Zusatzklasse 'CallHistory.java' erstellt, welche alle Logs sammelt.

Die rufe ich jetzt in der MainActivity mit
Code:
        Intent cHistory = new Intent();
        cHistory.setAction("com.example.mylog.CallHistory");
        getBaseContext().startService(cHistory);

auf.

Vorher hat das CallHistory-Loggen funktioniert - nun crasht die App einfach mit einem Nullpointer-Error.
:confused2:
 

Anhänge

  • MyCallLog.zip
    93,9 KB · Aufrufe: 59
Ok - inzwischen bin ich schon schlauer geworden, was die Angelegenheit betrifft.

Ich weiss jetzt glaube ich, wieso die App crasht.

Im CallLog hat man ja nicht nur bekannte, sondern auch unbekannte Nummern von unbekannten Anrufern.

Code:
        Cursor c = getContentResolver().query(
                   android.provider.CallLog.Calls.CONTENT_URI,
                   null,
                   null,
                   null,
                   android.provider.CallLog.Calls.DATE + " DESC");
        
        if(c.moveToFirst()) {
            
            // Iteration-variable;
            int i = 1;

            // Check: Still valid entry left?
            while(!c.isAfterLast()) {        
                
                // Collect History
                long tmp = c.getLong(c.getColumnIndex(CallLog.Calls.DATE));
                Date d = new Date(tmp);
                date = formatdate.format(d);
                time = formattime.format(d);
                name = c.getString(c.getColumnIndex(CallLog.Calls.CACHED_NAME));
                phonenumber = c.getString(c.getColumnIndex(CallLog.Calls.NUMBER));
                tmp1 = c.getString(c.getColumnIndex(CallLog.Calls.DURATION));
                duration = formatDuration(tmp1);
                 tmp2 = c.getInt(c.getColumnIndex(CallLog.Calls.TYPE));
                 callclassification = callType(tmp2);
  
                                
                  // Move to the next entry
                c.moveToNext();    
                i++;
            }
            
        }

        c.close();
Die Cursorquery schlägt fehlt --> die App crasht


Kann man dieses Problem irgendwie mit einem Exception auffangen?
 
Ok - muss diesen Thread wieder zum Leben erwecken.
Das Problem ist nicht neu - das hat sicher jeder schon mal erlebt!!
(Auch wenn ich bisher nichts dazu auf StackOverflow gefunden habe).


Was macht man, wenn der Cursor auf null zeigt? Exception-Handling für NullPointer?? :|
 

Ähnliche Themen

Muecke1982
Antworten
8
Aufrufe
1.027
Skyhigh
Skyhigh
S
Antworten
17
Aufrufe
3.479
jogimuc
J
Zurück
Oben Unten