Jak programowo usunąć SMS-a ze skrzynki odbiorczej w systemie Android?

98

W telefonach z systemem Android wiadomości SMS zarejestrowane w aplikacjach są również wysyłane do skrzynki odbiorczej urządzenia. Jednak aby uniknąć bałaganu, dobrze byłoby móc usunąć ze skrzynki odbiorczej wiadomości SMS specyficzne dla aplikacji, aby zmniejszyć potencjalne przepełnienie tych wiadomości.

Pytania w innych grupach dyskusyjnych Google dotyczące uzyskania ostatecznej odpowiedzi na temat programowego sposobu usuwania wiadomości SMS ze skrzynki odbiorczej Androida nie wydają się pilne.

A więc scenariusz:

  • Uruchomienie aplikacji na Androida.
  • zarejestrować typy wiadomości SMS X, Y i Z
  • wiadomości P, Q, X, Y, Z z biegiem czasu są przesyłane do skrzynki odbiorczej
  • Aplikacja na Androida wykrywa odebranie X, Y, Z (prawdopodobnie w ramach zdarzenia przerwania programu)
  • proces X, Y, Z
  • Pożądanie !!! X, Y, Z są usuwane ze skrzynki odbiorczej Androida

Czy to się stało? Czy da się to zrobić?

dmyung
źródło
4
Uważam, że robienie czegokolwiek pożytecznego z możliwościami telefonu w Androidzie jest bardzo nieprzyjemnym doświadczeniem.
BobbyShaftoe
1
dobre pytanie kolego. szukałem czegoś podobnego: D cheers
Harsha MV
3
Najlepiej byłoby, gdyby SMS nie dotarł do skrzynki odbiorczej w pierwszej kolejności: stackoverflow.com/questions/1741628/…
Christopher Orr

Odpowiedzi:

87

„Począwszy od systemu Android 1.6, transmisje przychodzące wiadomości SMS ( android.provider.Telephony.SMS_RECEIVED) są dostarczane jako„ rozsyłanie uporządkowane ”- co oznacza, że ​​można wskazać systemowi, które komponenty powinny odebrać transmisję jako pierwsze.”

Oznacza to, że możesz przechwycić przychodzącą wiadomość i przerwać jej nadawanie.

W AndroidManifest.xmlpliku upewnij się, że masz ustawiony najwyższy priorytet:

<receiver android:name=".receiver.SMSReceiver" android:enabled="true">
    <intent-filter android:priority="1000">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

W twojej BroadcastReceiver, w onReceive()metodzie przed wykonaniem coś z wiadomości, wystarczy zadzwonićabortBroadcast();

EDYCJA: Od KitKat, to już najwyraźniej nie działa.

EDIT2: Więcej informacji o tym, jak to zrobić na KitKat tutaj:

Usuń SMS-y z Androida w wersji 4.4.4 (wiersze, których dotyczy problem = 0 (zero), po usunięciu)

Viktor Fonic
źródło
3
I koniecznie zobacz stackoverflow.com/questions/1741628/ ...
Michael Levy
Czy możesz zobaczyć tutaj stackoverflow.com/questions/25988574/… ?
Gangadhar Nimballi
Najlepsza odpowiedź na to pytanie = D
Mike Brian Olivera
27

Korzystając z sugestii innych, myślę, że udało mi się:

(przy użyciu SDK v1 R2)

Nie jest doskonały, ponieważ muszę usunąć całą rozmowę, ale dla naszych celów jest to wystarczający kompromis, ponieważ przynajmniej będziemy wiedzieć, że wszystkie wiadomości zostaną przejrzane i zweryfikowane. Nasz przepływ prawdopodobnie będzie musiał następnie nasłuchiwać wiadomości, przechwytywać wiadomość, którą chcemy, wykonać zapytanie, aby uzyskać identyfikator wątku ostatnio odebranej wiadomości i wykonać wywołanie delete ().

W naszej działalności:

Uri uriSms = Uri.parse("content://sms/inbox");
Cursor c = getContentResolver().query(uriSms, null,null,null,null); 
int id = c.getInt(0);
int thread_id = c.getInt(1); //get the thread_id
getContentResolver().delete(Uri.parse("content://sms/conversations/" + thread_id),null,null);

Uwaga: nie udało mi się usunąć treści: // sms / inbox / lub content: // sms / all /

Wygląda na to, że wątek ma pierwszeństwo, co ma sens, ale komunikat o błędzie tylko ośmielił mnie, żebym był bardziej zły. Próbując usunąć na sms / inbox / lub sms / all /, prawdopodobnie otrzymasz:

java.lang.IllegalArgumentException: Unknown URL
    at com.android.providers.telephony.SmsProvider.delete(SmsProvider.java:510)
    at android.content.ContentProvider$Transport.delete(ContentProvider.java:149)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:149)

Aby uzyskać dodatkowe informacje, umieść to w swoim manifeście dla swojego odbiorcy:

<receiver android:name=".intent.MySmsReceiver">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED"></action>
    </intent-filter>
</receiver>

Zauważ, że tag odbiornika nie wygląda tak:

<receiver android:name=".intent.MySmsReceiver" 
    android:permission="android.permission.RECEIVE_SMS">

Kiedy miałem te ustawienia, Android dał mi szalone wyjątki uprawnień, które nie pozwalały android.phone na przekazywanie odebranych SMS-ów zgodnie z moim zamiarem. Więc NIE umieszczaj tego atrybutu uprawnienia RECEIVE_SMS w swojej intencji! Mam nadzieję, że ktoś mądrzejszy ode mnie może mi powiedzieć, dlaczego tak się stało.

dmyung
źródło
Czy możesz zobaczyć tutaj stackoverflow.com/questions/25988574/… ?
Gangadhar Nimballi
24

Tak, miałem grać, i to jest możliwe, aby usunąć odebrany SMS. Niestety nie wszystko jest proste :(

Mam odbiornik, który odbiera przychodzące wiadomości SMS. Sposób, w jaki działa routing przychodzący SMS na Androida polega na tym, że fragment kodu odpowiedzialny za dekodowanie wiadomości wysyła Broadcast (używa sendBroadcast()metody - która niestety NIE jest wersją, która pozwala po prostu zadzwonić abortBroadcast()) za każdym razem, gdy nadejdzie wiadomość.

Mój odbiorca może, ale nie musi, zostać wywołany przed systemowym odbiornikiem SMS, aw każdym przypadku odebrana transmisja nie ma właściwości, która mogłaby odzwierciedlać _idkolumnę w tabeli SMS.

Jednak nie będąc osobą, której można tak łatwo zatrzymać, wysyłam sobie (za pośrednictwem Handlera) opóźnioną wiadomość z SmsMessage jako dołączonym obiektem. (Przypuszczam, że możesz też opublikować Runnable ...)

handler.sendMessageDelayed(handler.obtainMessage(MSG_DELETE_SMS, msg), 2500);

Opóźnienie ma na celu zapewnienie, że do czasu nadejścia wiadomości wszyscy odbiorcy rozgłoszeń zakończą pracę i wiadomość zostanie bezpiecznie umieszczona w tabeli SMS.

Kiedy otrzymam wiadomość (lub Runnable), oto co robię:

case MSG_DELETE_SMS:
    Uri deleteUri = Uri.parse("content://sms");
    SmsMessage msg = (SmsMessage)message.obj;

    getContentResolver().delete(deleteUri, "address=? and date=?", new String[] {msg.getOriginatingAddress(), String.valueOf(msg.getTimestampMillis())});

Używam adresu źródłowego i pola znacznika czasu, aby zapewnić bardzo wysokie prawdopodobieństwo usunięcia TYLKO interesującej mnie wiadomości. Gdybym chciał być jeszcze bardziej paranoikiem, mógłbym dołączyć msg.getMessageBody()treść jako część zapytania.

Tak, wiadomość JEST usunięta (hura!). Niestety pasek powiadomień nie jest aktualizowany :(

Po otwarciu obszaru powiadomień zobaczysz wiadomość tam dla Ciebie ... ale kiedy ją dotkniesz, aby ją otworzyć - zniknęła!

Dla mnie to nie jest wystarczająco dobre - chcę, aby wszystkie ślady wiadomości zniknęły - nie chcę, aby użytkownik myślał, że istnieje TXT, gdy go nie ma (spowodowałoby to tylko raporty o błędach).

Wewnętrznie w systemie operacyjnym połączenia telefoniczne MessagingNotification.updateNewMessageIndicator(Context), ale ja ta klasa została ukryta przed API i nie chciałem replikować całego tego kodu tylko po to, aby wskaźnik był dokładny.

Doug
źródło
Doug, to jest świetne. A propos, czy wiesz, czy konieczne jest wysłanie wiadomości do Handler? Zastanawiam się, czy SMS jest wstawiony do systemowej bazy danych SMS-ów przed wywołaniem i z odbiorników transmisji? Prawdopodobnie będę musiał zajrzeć do systemu operacyjnego, aby zobaczyć, gdzie następuje wstawienie, ale przypuszczam, że wstawienie SMS-a do jakiejś bazy danych ma miejsce przed poinformowaniem odbiorców transmisji. Masz wgląd w to, czy już to sprawdziłeś?
Hamy
Czy możesz zobaczyć tutaj stackoverflow.com/questions/25988574/… ?
Gangadhar Nimballi
11
public boolean deleteSms(String smsId) {
    boolean isSmsDeleted = false;
    try {
        mActivity.getContentResolver().delete(Uri.parse("content://sms/" + smsId), null, null);
        isSmsDeleted = true;

    } catch (Exception ex) {
        isSmsDeleted = false;
    }
    return isSmsDeleted;
}

użyj tego uprawnienia w AndroidManifiest

<uses-permission android:name="android.permission.WRITE_SMS"/>
Atif Mahmood
źródło
1
Jak otrzymujemy identyfikator sms?
Dev01
@ Dev01Jeśli chcesz usunąć konkretny sms, musisz mieć jego identyfikator, prawda? a jeśli nie, musisz sprawdzić identyfikator za pomocą adresu lub smsbody
Dr. aNdRO
6

Lepiej jest użyć _id i thread_id do usunięcia wiadomości.

Thread_id to coś przypisanego do wiadomości pochodzących od tego samego użytkownika. Jeśli więc użyjesz tylko thread_id, wszystkie wiadomości od nadawcy zostaną usunięte.

Jeśli użyjesz kombinacji _id, thread_id, usunie to dokładnie tę wiadomość, którą chcesz usunąć.

Uri thread = Uri.parse( "content://sms");
int deleted = contentResolver.delete( thread, "thread_id=? and _id=?", new String[]{String.valueOf(thread_id), String.valueOf(id)} );
Naresh Kavali
źródło
Czy możesz zobaczyć tutaj stackoverflow.com/questions/25988574/… ?
Gangadhar Nimballi
5

Musisz znaleźć identyfikator URI wiadomości. Ale kiedy już to zrobisz, myślę, że powinieneś być w stanie android.content.ContentResolver.delete (...) to.

Oto więcej informacji .

Neil
źródło
2

Myślę, że na razie nie można tego doskonale zrobić. Istnieją 2 podstawowe problemy:

  1. Jak możesz upewnić się, że SMS jest już w skrzynce odbiorczej, gdy próbujesz go usunąć?
    Zwróć uwagę, że SMS_RECEIVED nie jest zamówioną transmisją.
    Tak więc rozwiązaniem Dmyunga jest próba szczęścia; nawet opóźnienie w odpowiedzi Douga nie jest gwarancją.

  2. SmsProvider nie jest bezpieczny dla wątków. (Patrz http://code.google.com/p/android/issues/detail?id=2916#c0 )
    Fakt, że więcej niż jeden klient żąda usunięcia i wstawienia do niego w w tym samym czasie spowoduje uszkodzenie danych lub nawet natychmiastowy wyjątek w czasie wykonywania.

an0
źródło
2

Nie mogłem zmusić go do pracy przy użyciu rozwiązania dmyung, dał mi wyjątek podczas uzyskiwania identyfikatora wiadomości lub identyfikatora wątku.

W końcu użyłem następującej metody, aby uzyskać identyfikator wątku:

private long getThreadId(Context context) {
    long threadId = 0;

    String SMS_READ_COLUMN = "read";
    String WHERE_CONDITION = SMS_READ_COLUMN + " = 0";
    String SORT_ORDER = "date DESC";
    int count = 0;

    Cursor cursor = context.getContentResolver().query(
                    SMS_INBOX_CONTENT_URI,
          new String[] { "_id", "thread_id", "address", "person", "date", "body" },
                    WHERE_CONDITION,
                    null,
                    SORT_ORDER);

    if (cursor != null) {
            try {
                count = cursor.getCount();
                if (count > 0) {
                    cursor.moveToFirst();
                    threadId = cursor.getLong(1);                              
                }
            } finally {
                    cursor.close();
            }
    }


    return threadId;
}

Wtedy mogłem go usunąć. Jednak, jak powiedział Doug, powiadomienie nadal tam jest, nawet komunikat jest wyświetlany podczas otwierania panelu powiadomień. Dopiero stukając w wiadomość mogłem faktycznie zobaczyć, że jest pusty.

Więc wydaje mi się, że jedynym sposobem, aby to zadziałało, byłoby przechwycenie wiadomości SMS, zanim zostanie ona dostarczona do systemu, zanim dotrze nawet do skrzynki odbiorczej. Jednak bardzo wątpię, że jest to wykonalne. Proszę popraw mnie jeżeli się mylę.

Stelian Iancu
źródło
Cóż, źle o przechwytywaniu wiadomości przed skrzynki: może pojawić się nadawanie android.provider.Telephony.SMS_RECEIVED, wystarczy tylko android.permission.RECEIVE_SMS permssion. Możesz nawet łatwo zatrzymać dotarcie wiadomości do skrzynki odbiorczej, wywołując abortBroadcast (), ponieważ jest to uporządkowana transmisja. PS Musi być zabawnie do poprawienia 2 lata później :)
lapis
2

Użyj tej funkcji, aby usunąć określony wątek wiadomości lub zmodyfikować go zgodnie ze swoimi potrzebami:

public void delete_thread(String thread) 
{ 
  Cursor c = getApplicationContext().getContentResolver().query(
  Uri.parse("content://sms/"),new String[] { 
  "_id", "thread_id", "address", "person", "date","body" }, null, null, null);

 try {
  while (c.moveToNext()) 
      {
    int id = c.getInt(0);
    String address = c.getString(2);
    if (address.equals(thread)) 
        {
     getApplicationContext().getContentResolver().delete(
     Uri.parse("content://sms/" + id), null, null);
    }

       }
} catch (Exception e) {

  }
}

Wywołaj tę funkcję poniżej:

delete_thread("54263726");//you can pass any number or thread id here

Nie zapomnij dodać poniżej uprawnień do głównego festiwalu Androida:

<uses-permission android:name="android.permission.WRITE_SMS"/>
Ashish Sahu
źródło
1

Po prostu wyłącz powiadomienia dla domyślnej aplikacji sms. Przetwarzaj własne powiadomienia dla wszystkich wiadomości tekstowych!

Noah Seidman
źródło
1

Po prostu wypróbuj poniższy kod, aby usunąć wszystkie SMS-y, które są w telefonie (odebrane lub wysłane)

Uri uri = Uri.parse("content://sms");

ContentResolver contentResolver = getContentResolver();

Cursor cursor = contentResolver.query(uri, null, null, null,
  null);



while (cursor.moveToNext()) {

 long thread_id = cursor.getLong(1);
 Uri thread = Uri.parse("content://sms/conversations/"
   + thread_id);
 getContentResolver().delete(thread, null, null);
}
Rajamohan Sugumaran
źródło
1

Zaktualizuj również plik manifestu, aby usunąć sms, którego potrzebujesz do zapisu.

<uses-permission android:name="android.permission.WRITE_SMS"/>
Wasif Kirmani
źródło
1

abortBroadcast();Metoda Now może służyć do ograniczenia przychodzącej wiadomości do skrzynki odbiorczej.

Arun Badole
źródło
0

Przykład usunięcia jednego SMS-a, a nie rozmowy:

getContentResolver().delete(Uri.parse("content://sms/conversations/" + threadID), "_id = ?", new String[]{id});
embo
źródło
0
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    SMSData sms = (SMSData) getListAdapter().getItem(position);
    Toast.makeText(getApplicationContext(), sms.getBody(),
            Toast.LENGTH_LONG).show();
    Toast.makeText(getApplicationContext(), sms.getNumber(),
            Toast.LENGTH_LONG).show();

    deleteSms(sms.getId());

}

public boolean deleteSms(String smsId) {
    boolean isSmsDeleted = false;
    try {
        MainActivity.this.getContentResolver().delete(
                Uri.parse("content://sms/" + smsId), null, null);
        isSmsDeleted = true;

    } catch (Exception ex) {
        isSmsDeleted = false;
    }
    return isSmsDeleted;
}
sharma_kunal
źródło
0

Spróbuj tego jestem w 100% pewien, że to zadziała dobrze: - // po prostu wpisz tutaj adres konwersji, aby usunąć całą konwersję według adresu (nie zapomnij dodać uprawnień do odczytu i zapisu w mainfest) Oto kod:

String address="put address only";

Cursor c = getApplicationContext().getContentResolver().query(Uri.parse("content://sms/"), new String[] { "_id", "thread_id", "address", "person", "date", "body" }, null, null, null);

try {
    while (c.moveToNext()) {
        int id = c.getInt(0);
        String address = c.getString(2);
        if(address.equals(address)){
        getApplicationContext().getContentResolver().delete(Uri.parse("content://sms/" + id), null, null);}
    }
} catch(Exception e) {

}
Ashish Sahu
źródło
0

Użyj jednej z tych metod, aby wybrać ostatnio otrzymany SMS i usunąć go, tutaj w tym przypadku otrzymuję najwięcej smsów i zamierzam usunąć za pomocą wątku i wartości identyfikatora sms,

try {
    Uri uri = Uri.parse("content://sms/inbox");
    Cursor c = v.getContext().getContentResolver().query(uri, null, null, null, null);
    int i = c.getCount();

    if (c.moveToFirst()) {
    }
} catch (CursorIndexOutOfBoundsException ee) {
    Toast.makeText(v.getContext(), "Error :" + ee.getMessage(), Toast.LENGTH_LONG).show();
}
Pir Fahim Shah
źródło