2014-12-29 9 views
9

Sto provando a creare un'app per Android che scrive messaggi nella casella di posta inviata del sistema. Questi messaggi non devono essere inviati tramite la rete GSM al destinatario, l'idea è solo di scriverli nel provider di contenuti inviati.Android KitKat (API 19) - Come scrivere messaggi nel provider di contenuti SMS, senza inviarli, dall'app non predefinita?

Per ora, ho questo codice:

File manifesto

<uses-permission android:name="android.permission.READ_SMS"/> 
<uses-permission android:name="android.permission.WRITE_SMS"/> 

Java Class

private final String SENT_SMS_CONTENT_PROVIDER_URI_OLDER_API_19 = "content://sms/sent"; 

ContentValues values = new ContentValues(); 
values.put("address", mNumber); 
values.put("body", mMessage); 

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
    mContext.getContentResolver().insert(Telephony.Sms.Sent.CONTENT_URI, values); 
else mContext.getContentResolver().insert(Uri.parse(SENT_SMS_CONTENT_PROVIDER_URI_OLDER_API_19), values); 

Per un dispositivo con una versione API inferiore a 19, questo l'implementazione funziona bene. Per queste versioni precedenti di sdk, è necessario solo accedere al fornitore di contenuti definito dal contenuto uri : // sms/inviato.

Per le versioni di sdk più recenti, questo non funziona. Apparentemente, Android ha cambiato il suo modo di gestire il modulo SMS nella versione KitKat. Secondo il prossimo articolo, solo l'applicazione SMS predefinita può scrivere e aggiornare il nuovo Content Provider SMS (android.provider.Telephony.Sms.Sent - il contenuto precedente: // sms/inviato non è disponibile):

Considerando il comportamento di questa app, non ha senso trasformarla nell'app SMS predefinita. Questa app non ha bisogno di leggere messaggi SMS dal fornitore di contenuti e non dovrebbe inviare alcun messaggio da SmsManager.getDefault(). SendTextMessage. L'unica cosa che dovrebbe fare è scrivere alcuni messaggi nel provider di posta.

Come si può capire, non è accettabile e praticabile richiedere all'utente di modificare l'app predefinita da estrarre e quindi tornare alla precedente app di SMS, ogni volta che è necessario scrivere un messaggio nella Posta inviata (questo è suggerito nella sezione "Consigli per il backup degli SMS & ripristini" nel Blogspot degli sviluppatori Android).

Il prossimo articolo rivela alcuni modi per visualizzarla OP_WRITE_SMS opzione:

Purtroppo, il codice successivo smesso di lavorare per Android 4.4.2:

Intent intent = new Intent(); 
intent.setClassName("com.android.settings", "com.android.settings.Settings"); 
intent.setAction(Intent.ACTION_MAIN); 
intent.addCategory(Intent.CATEGORY_DEFAULT); 
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 
intent.putExtra(":android:show_fragment", "com.android.settings.applications.AppOpsSummary"); 
startActivity(intent); 

I Sono fuori di soluzioni per superare questo problema.

+0

La cartella inviata non dovrebbe riflettere solo i messaggi inviati (o almeno tentati) e non "falsi" che non lo sono stati? –

risposta

15

La classe SmsWriteOpUtils utilizza la riflessione per accedere ai metodi del servizio AppOpsManager al fine di abilitare/disabilitare l'accesso in scrittura di un'applicazione SMS non predefinita al provider SMS in Livello API 19 (KitKat). Una volta impostato, la modalità di accesso di un'app verrà conservata fino a quando non viene ripristinata o l'app viene disinstallata.

Abilitare l'accesso in scrittura di un'applicazione consente a quell'app tutti i metodi standard di interazione con il provider SMS, inclusi insert() e delete().

Si noti che questa classe non esegue il controllo del livello API e che è ancora necessaria l'autorizzazione WRITE_SMS.

import android.app.AppOpsManager; 
import android.content.Context; 
import android.content.pm.PackageManager; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

public final class SmsWriteOpUtils { 
    private static final int WRITE_OP_CODE = 15; 

    public static boolean isWriteEnabled(Context context) { 
     int result = checkOp(context); 
     return result == AppOpsManager.MODE_ALLOWED; 
    } 

    public static boolean setWriteEnabled(Context context, boolean enabled) { 
     int mode = enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED; 
     return setMode(context, mode); 
    } 

    private static int checkOp(Context context) { 
     try { 
      Method checkOpMethod = AppOpsManager.class.getMethod("checkOp", 
                   Integer.TYPE, 
                   Integer.TYPE, 
                   String.class); 

      AppOpsManager appOpsManager = 
       (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 
      int uid = context.getApplicationInfo().uid; 
      String packageName = context.getPackageName(); 

      return checkOpMethod.invoke(appOpsManager, WRITE_OP_CODE, uid, packageName); 
     } 
     catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { 
      e.printStackTrace(); 
     } 
     return -1; 
    } 

    private static boolean setMode(Context context, int mode) { 
     try { 
      Method setModeMethod = AppOpsManager.class.getMethod("setMode", 
                   Integer.TYPE, 
                   Integer.TYPE, 
                   String.class, 
                   Integer.TYPE); 

      AppOpsManager appOpsManager = 
       (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 
      int uid = context.getApplicationInfo().uid; 
      String packageName = context.getPackageName(); 

      setModeMethod.invoke(appOpsManager, WRITE_OP_CODE, uid, packageName, mode); 

      return true; 
     } 
     catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { 
      e.printStackTrace(); 
     } 
     return false; 
    } 
} 

Utilizzo Esempio:

boolean canWriteSms; 

if(!SmsWriteOpUtils.isWriteEnabled(getApplicationContext())) { 
    canWriteSms = SmsWriteOpUtils.setWriteEnabled(getApplicationContext(), true); 
} 
... 

NB: Per regolare le applicazioni utente, questo funziona solo al livello API 19 (KitKat). Il buco è stato patchato nelle versioni successive.

+1

Questo ha funzionato perfettamente! Ho provato su un dispositivo Android 4.4.4 che ho anche qui. Cercherò di eseguire questo oltre la versione di Lollipop nei prossimi giorni. Grazie! – supertreta

+0

Ancora non riesci a cancellare sms – xnagyg

+0

@xnagyg Ti sei ricordato dell'autorizzazione 'WRITE_SMS'? Puoi inserire un messaggio? Quali sono i valori restituiti per i metodi 'public static'? Il tuo codice di cancellazione funziona su una versione precedente a KitKat? Su quale dispositivo e versione di Android stai testando? Ho un banco di prova limitato, ma tutto funziona per me e, a quanto pare, l'OP. –

Problemi correlati