OTP (token) debe leerse automáticamente desde el mensaje

Estoy trabajando en una aplicación Android, en la que el servidor envía un OTP y el usuario necesita introducir este OTP en la aplicación, para registrar mi aplicación. Lo que quiero es que mi App debe ser capaz de leer automáticamente el OTP enviado por el servidor. ¿Cómo puedo lograr esto? Toda ayuda o orientación a este respecto sería muy apreciada.

Pregunta hecha hace 8 años, 8 meses, 13 días - Por devepicenter


11 Respuestas:

  • Le recomendaré que no use bibliotecas de terceros para la búsqueda automática de OTP desde SMS Inbox. Esto se puede hacer fácilmente si usted tiene comprensión básica del receptor de radiodifusión y cómo funciona. Prueba el siguiente enfoque:

    1. Crear una sola interfaz i.e SmsListner
    package com.wnrcorp.reba;
    public interface SmsListener {
        public void messageReceived(String messageText);
    }
    
    1. Crear un solo receptor de radiodifusión i.e SmsReceiver
    package com.wnrcorp.reba;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.telephony.SmsMessage;
    
    public class SmsReceiver extends BroadcastReceiver {
        private static SmsListener mListener;
        Boolean b;
        String abcd, xyz;
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle data = intent.getExtras();
            Object[] pdus = (Object[]) data.get("pdus");
            for (int i = 0; i < pdus.length; i++) {
                SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
                String sender = smsMessage.getDisplayOriginatingAddress();
                // b=sender.endsWith("WNRCRP");  //Just to fetch otp sent from WNRCRP
                String messageBody = smsMessage.getMessageBody();
                abcd = messageBody.replaceAll("[^0-9]", ""); // here abcd contains otp 
                which is in number format
                //Pass on the text to our listener.
                if (b == true) {
                    mListener.messageReceived(abcd); // attach value to interface 
                    object
                } else {}
            }
        }
        public static void bindListener(SmsListener listener) {
            mListener = listener;
        }
    }
    
    1. Añadir Listener i.e receptor de transmisión en archivo de manifiesto android
    
        
            
        
    
    

    y añadir permiso

    
    
    1. La actividad donde va a buscar otp automáticamente cuando se recibe en la bandeja de entrada. En mi caso voy a buscar otp y poner en campo de texto.
    public class OtpVerificationActivity extends AppCompatActivity {
        EditText ed;
        TextView tv;
        String otp_generated, contactNo, id1;
        GlobalData gd = new GlobalData();
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_otp_verification);
                ed = (EditText) findViewById(R.id.otp);
                tv = (TextView) findViewById(R.id.verify_otp);
                /*This is important because this will be called every time you receive 
                 any sms */
                SmsReceiver.bindListener(new SmsListener() {
                    @Override
                    public void messageReceived(String messageText) {
                        ed.setText(messageText);
                    }
                });
                tv.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                InputMethodManager imm =
                                    (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                                imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
                            } catch (Exception e) {}
                            if (ed.getText().toString().equals(otp_generated)) {
                                Toast.makeText(OtpVerificationActivity.this, "OTP Verified 
                                    Successfully!", Toast.LENGTH_SHORT).show();           
                                }
                            });
                    }
                }
    }
    

    Archivo de diseño para OtpVerificationActivity

    xml version="1.0" encoding="utf-8"?
    
        
            
                
                
                
            
        
    
    

    Capturas de pantalla para la verificación OTP Actividad donde usted busca OTP tan pronto como mensajes recibidos enter image description here

    Respondida el Jun 16, 2015 a las 08:24 - por binarybard

    Votos positivos: 0 | Votos negativos: 0

  • Puedes intentar usar una biblioteca sencilla como

    Después de instalar a través de gradle y agregar permisos iniciar SmsVerifyCatcher en método como onCrear actividad:

        smsVerifyCatcher = new SmsVerifyCatcher(this, new OnSmsCatchListener() {
        @Override
        public void onSmsCatch(String message) {
            String code = parseCode(message);//Parse verification code
            etCode.setText(code);//set code in edit text
            //then you can send verification code to server
        }
    });
    

    Además, anular los métodos de ciclo de vida de actividad:

      @Override
    protected void onStart() {
        super.onStart();
        smsVerifyCatcher.onStart();
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        smsVerifyCatcher.onStop();
    }
    
    /**
     * need for Android 6 real time permissions
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        smsVerifyCatcher.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
    
    
    public String parseCode(String message) {
        Pattern p = Pattern.compile("\\b\\d{4}\\b");
        Matcher m = p.matcher(message);
        String code = "";
        while (m.find()) {
            code = m.group(0);
        }
        return code;
    }
    

    Respondida el Jun 16, 2015 a las 08:30 - por techwizkid

    Votos positivos: 0 | Votos negativos: 0

  • Como Google ha restringido el uso de permiso READ_SMS aquí es la solución sin el permiso READ_SMS.

    SMS Retriever API

    Función básica es evitar el uso de permiso crítico Android READ_ SMS y realizar tareas utilizando este método. Lenta son pasos que necesitabas.

    Correo Enviar OTP al número del usuario, comprobar SMS Retriever API capaz de obtener mensaje o no

    SmsRetrieverClient client = SmsRetriever.getClient(SignupSetResetPasswordActivity.this);
    Task task = client.startSmsRetriever();
    task.addOnSuccessListener(new OnSuccessListener() {
        @Override
        public void onSuccess(Void aVoid) {
            // Android will provide message once receive. Start your broadcast receiver.
            IntentFilter filter = new IntentFilter();
            filter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
            registerReceiver(new SmsReceiver(), filter);
        }
    });
    task.addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            // Failed to start retriever, inspect Exception for more details
        }
    });
    

    Código del receptor de radiodifusión

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.os.Bundle;
    
    import com.google.android.gms.auth.api.phone.SmsRetriever;
    import com.google.android.gms.common.api.CommonStatusCodes;
    import com.google.android.gms.common.api.Status;
    
    public class SmsReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
                Bundle extras = intent.getExtras();
                Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
    
                switch (status.getStatusCode()) {
                    case CommonStatusCodes.SUCCESS:
                        // Get SMS message contents
                        String otp;
                        String msgs = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
    
                        // Extract one-time code from the message and complete verification
                        break;
                    case CommonStatusCodes.TIMEOUT:
                        // Waiting for SMS timed out (5 minutes)
                        // Handle the error ...
                        break;
                }
            }
        }
    }
    

    Paso final. Registrar este receptor en su Manifiesto

    
        
            
        
    
    

    Su SMS debe como abajo.

    <#> Your OTP code is: 6789
    QWsa8754qw2 
    

    Aquí QWsa8754qw2 es su propia aplicación 11 carácter hash code. Sigue esto. enlace

    • No más de 140 bytes
    • Empieza con el prefijo
    • Termina con una cadena de 11 caracteres que identifica tu app

    Importar com.google.android.gms.auth.api.phone.SmsRetriever, No te olvides de añadir esta línea a tu compilación de aplicaciones. gris:

    implementation "com.google.android.gms:play-services-auth-api-phone:16.0.0"
    

    Respondida el Jun 16, 2015 a las 08:40 - por bytebard

    Votos positivos: 0 | Votos negativos: 0

  • He implementado algo así. Pero aquí es lo que hice cuando el mensaje entra, recuperé sólo el código de seis dígitos, lo empaqué en una intención y lo envié a la actividad o fragmento que lo necesita y verifica el código. El ejemplo te muestra la manera de conseguir los sms ya. Vea el siguiente código para ilustrar cómo enviar usando LocalBrodcastManager y si su mensaje contiene más textos E.g Saludos, estandarizarlo para ayudarle mejor. E.g "Su código de verificación es: 84HG73" usted puede crear un regex patrón como este ([0-9]){2}([A-Z]){2}([0-9]){2} que significa dos entradas, dos letras [capital] y dos entradas. ¡Buena suerte!

    Después de descartar toda la información no necesaria del mensaje

     Intent intent = new Intent("AddedItem");
     intent.putExtra("items", code);
     LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent); 
    

    Y el Fragmento/Actividad que lo recibe

    @Override
    public void onResume() {
        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, new IntentFilter("AddedItem"));
        super.onResume();
    }
    
    @Override
    public void onPause() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
    }
    

    Y el código significaba manejar la carga útil que recogiste

     private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction()) {
                final String message = intent.getStringExtra("message");
                //Do whatever you want with the code here
            }
        }
    };
    

    Eso ayuda un poco. Lo hice mejor usando Callbacks

    Respondida el Jun 16, 2015 a las 08:50 - por htmlhero

    Votos positivos: 0 | Votos negativos: 0

  • Lo siento por la respuesta tardía, pero todavía sentía que publicar mi respuesta si ayuda. Funciona para 6 dígitos OTP.

        @Override
        public void onOTPReceived(String messageBody)
        {
            Pattern pattern = Pattern.compile(SMSReceiver.OTP_REGEX);
            Matcher matcher = pattern.matcher(messageBody);
            String otp = HkpConstants.EMPTY;
            while (matcher.find())
            {
                otp = matcher.group();
            }
            checkAndSetOTP(otp);
        }
    Adding constants here
    
    public static final String OTP_REGEX = "[0-9]{1,6}";
    

    Para el oyente de SMS uno puede seguir la siguiente clase

    public class SMSReceiver extends BroadcastReceiver
    {
        public static final String SMS_BUNDLE = "pdus";
        public static final String OTP_REGEX = "[0-9]{1,6}";
        private static final String FORMAT = "format";
    
        private OnOTPSMSReceivedListener otpSMSListener;
    
        public SMSReceiver(OnOTPSMSReceivedListener listener)
        {
            otpSMSListener = listener;
        }
    
        @Override
        public void onReceive(Context context, Intent intent)
        {
            Bundle intentExtras = intent.getExtras();
            if (intentExtras != null)
            {
                Object[] sms_bundle = (Object[]) intentExtras.get(SMS_BUNDLE);
                String format = intent.getStringExtra(FORMAT);
                if (sms_bundle != null)
                {
                    otpSMSListener.onOTPSMSReceived(format, sms_bundle);
                }
                else {
                    // do nothing
                }
            }
        }
    
        @FunctionalInterface
        public interface OnOTPSMSReceivedListener
        {
            void onOTPSMSReceived(@Nullable String format, Object... smsBundle);
        }
    }
    
        @Override
        public void onOTPSMSReceived(@Nullable String format, Object... smsBundle)
        {
            for (Object aSmsBundle : smsBundle)
            {
                SmsMessage smsMessage = getIncomingMessage(format, aSmsBundle);
                String sender = smsMessage.getDisplayOriginatingAddress();
                if (sender.toLowerCase().contains(ONEMG))
                {
                    getIncomingMessage(smsMessage.getMessageBody());
                } else
                {
                    // do nothing
                }
            }
        }
    
        private SmsMessage getIncomingMessage(@Nullable String format, Object aObject)
        {
            SmsMessage currentSMS;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && format != null)
            {
                currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format);
            } else
            {
                currentSMS = SmsMessage.createFromPdu((byte[]) aObject);
            }
    
            return currentSMS;
        }
    

    Respondida el Jun 16, 2015 a las 08:59 - por binarybard1cc8

    Votos positivos: 0 | Votos negativos: 0

  • Con el SMS Retriever API, se puede leer OTP sin declarar android.permission.READ_SMS.

    1. Iniciar el recuperador de SMS
        private fun startSMSRetriever() {
            // Get an instance of SmsRetrieverClient, used to start listening for a matching SMS message.
            val client = SmsRetriever.getClient(this /* context */);
    
            // Starts SmsRetriever, which waits for ONE matching SMS message until timeout
            // (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
            // action SmsRetriever#SMS_RETRIEVED_ACTION.
            val task: Task = client.startSmsRetriever();
    
            // Listen for success/failure of the start Task. If in a background thread, this
            // can be made blocking using Tasks.await(task, [timeout]);
            task.addOnSuccessListener {
                Log.d("SmsRetriever", "SmsRetriever Start Success")
            }
    
            task.addOnFailureListener {
                Log.d("SmsRetriever", "SmsRetriever Start Failed")
            }
        }
    
    1. Recibir mensajes vía Radiodifusión
        public class MySMSBroadcastReceiver : BroadcastReceiver() {
    
            override fun onReceive(context: Context?, intent: Intent?) {
                if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action && intent.extras!=null) {
                    val extras = intent.extras
                    val status = extras.get(SmsRetriever.EXTRA_STATUS) as Status
    
                    when (status.statusCode) {
                        CommonStatusCodes.SUCCESS -> {
                            // Get SMS message contents
                            val message = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
                            Log.e("Message", message);
                            // Extract one-time code from the message and complete verification
                            // by sending the code back to your server.
                        }
                        CommonStatusCodes.TIMEOUT -> {
                            // Waiting for SMS timed out (5 minutes)
                            // Handle the error ...
                        }
                    }
                }
            }
    
        }   
    
    
        /**Don't forgot to define BroadcastReceiver in AndroidManifest.xml.*/       
        
            
                
            
        
    
    1. Envíe el código de una sola vez del mensaje de verificación a su servidor

    Asegúrese de que su formato SMS es exactamente como abajo:

    <#> Your ExampleApp code is: 123ABC78
    fBzOyyp9h6L
    
    1. No más de 140 bytes
    2. Empieza con el prefijo
    3. Termina con una cadena de 11 caracteres que identifica tu app

      Puede calcular el hash app con el siguiente código:

      import android.content.Context
      import android.content.ContextWrapper
      import android.content.pm.PackageManager
      import android.util.Base64
      import android.util.Log
      import java.nio.charset.StandardCharsets
      import java.security.MessageDigest
      import java.security.NoSuchAlgorithmException
      import java.util.*
      
      /**
       * This is a helper class to generate your message hash to be included in your SMS message.
       *
       * Without the correct hash, your app won't recieve the message callback. This only needs to be
       * generated once per app and stored. Then you can remove this helper class from your code.
       *
       * For More Detail: https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
       *
       */
      public class AppSignatureHelper(private val context: Context) : ContextWrapper(context) {
      
          companion object {
              val TAG = AppSignatureHelper::class.java.simpleName;
      
              private const val HASH_TYPE = "SHA-256";
              const val NUM_HASHED_BYTES = 9;
              const val NUM_BASE64_CHAR = 11;
          }
      
          /**
           * Get all the app signatures for the current package
           * @return
           */
          public fun getAppSignatures(): ArrayList {
              val appCodes = ArrayList();
      
              try {
                  // Get all package signatures for the current package
                  val signatures = packageManager.getPackageInfo(
                      packageName,
                      PackageManager.GET_SIGNATURES
                  ).signatures;
      
                  // For each signature create a compatible hash
                  for (signature in signatures) {
                      val hash = hash(packageName, signature.toCharsString());
                      if (hash != null) {
                          appCodes.add(String.format("%s", hash));
                      }
                  }
              } catch (e: PackageManager.NameNotFoundException) {
                  Log.e(TAG, "Unable to find package to obtain hash.", e);
              }
              return appCodes;
          }
      
          private fun hash(packageName: String, signature: String): String? {
              val appInfo = "$packageName $signature";
              try {
                  val messageDigest = MessageDigest.getInstance(HASH_TYPE);
                  messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8));
                  var hashSignature = messageDigest.digest();
      
                  // truncated into NUM_HASHED_BYTES
                  hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
                  // encode into Base64
                  var base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP);
                  base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
      
                  Log.e(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
                  return base64Hash;
              } catch (e: NoSuchAlgorithmException) {
                  Log.e(TAG, "hash:NoSuchAlgorithm", e);
              }
              return null;
          }
      }       
      

    Grado requerido :

    implementation "com.google.android.gms:play-services-auth-api-phone:16.0.0"
    

    Referencias:
    https://developers.google.com/identity/sms-retriever/overview
    https://developers.google.com/identity/sms-retriever/request
    https://developers.google.com/identity/sms-retriever/verify

    Respondida el Jun 16, 2015 a las 09:08 - por codemasterx

    Votos positivos: 0 | Votos negativos: 0

  • Sí, esto es posible ahora en los navegadores también. Chrome liberación esta característica en la versión 84 y superior. Con la ayuda de WEBOTP API, podemos detectar OTP en la web para dispositivos móviles.

    Aquí está un código integrado Web-OTP con aplicaciones Angular PWA: https://github.com/Rohit3230/webOtpAutoReadByAngular

    Vaya a buscar URL de trabajo en vivo para la aplicación PWA angular. https://rohit3230.github.io/webOtpAutoReadByAngular/

    Respondida el Jun 16, 2015 a las 09:18 - por devguru123

    Votos positivos: 0 | Votos negativos: 0

  • Sé que es tarde pero para mantener esto simple para otros, aquí está la solución que he escrito anteriormente. Utilice esta biblioteca Enlace. No tendrás que meterte con nada. Después de agregar dependencia simplemente use este método.

    OtpFetcher.getInstance().verifyOtpByMatchingString(this, "OTP", 21000, object : OtpListener {
                override fun onReceived(messageItem: MessageItem) {
                    Toast.makeText(this@MainActivity, "" + messageItem, Toast.LENGTH_SHORT).show()
                }
    
                override fun onTimeOut() {
                    Toast.makeText(this@MainActivity, "TimeOut", Toast.LENGTH_SHORT).show()
    
                }
            })
    

    Usted tendrá que pasar Contexto, Búsqueda de texto de su mensaje por ejemplo

    Usted está esperando OTP en su mensaje Paso "OTP" y tiempo fuera por cuánto tiempo quiere escuchar OTP y eso es todo. Usted recibirá su mensaje en formato simple en OnRecieved CallBack.

    Respondida el Jun 16, 2015 a las 09:25 - por codecrusader

    Votos positivos: 0 | Votos negativos: 0

  • **activity_main.xml**
    
    xml version="1.0" encoding="utf-8"?
    
    
        
    
    
    
    
    
    **MainActivity.java**
    import android.content.BroadcastReceiver;
    import android.content.IntentFilter;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    public class MainActivity extends AppCompatActivity {
    
        private BroadcastReceiver broadcastReceiver;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            broadcastReceiver =new MyBroadcastReceiver();
        }
    
    @Override
        protected void onStart()
    {
        super.onStart();
        IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
        registerReceiver(broadcastReceiver,intentFilter);
    }
    @Override
        protected void onStop()
    {
        super.onStop();
        unregisterReceiver(broadcastReceiver);
    }
    
    }
    
    
    **MyBroadcastReceiver.java**
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.widget.Toast;
    
    /**
     * Created by mukundwn on 12/02/18.
     */
    
    public class MyBroadcastReceiver extends BroadcastReceiver {
    
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context,"hello received an sms",Toast.LENGTH_SHORT).show();
        }
    }
    
    
    **Manifest.xml**
    
    xml version="1.0" encoding="utf-8"?
    
    
        
        
        
    
        
            
                
                    
    
                    
                
            
            
            
                
            
            
        
    
    
    

    Respondida el Jun 16, 2015 a las 09:31 - por algorithmarchitect

    Votos positivos: 0 | Votos negativos: 0

  • Para lograr que tu aplicación Android sea capaz de leer automáticamente el OTP enviado por el servidor, puedes implementar la lectura automática del mensaje SMS en tu aplicación.

    Aquí te dejo los pasos generales que puedes seguir para lograr esto:

    • Solicitar permiso: Debes solicitar el permiso READ_SMS en el archivo AndroidManifest.xml para que tu aplicación tenga permiso para leer los mensajes SMS entrantes.
    xml
    
    
    
    • Implementar un BroadcastReceiver: Necesitas implementar un `BroadcastReceiver` que se encargue de detectar los mensajes SMS entrantes y extraer el OTP de ese mensaje.

    ```java public class SmsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) { for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) { String messageBody = smsMessage.getMessageBody(); // Extraer el OTP del mensaje y utilizarlo en tu aplicación

    Respondida el Jun 17, 2015 a las 08:19 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Automatizar la lectura de OTP mediante expresiones regulares:

    • Crea un detector de patrones: Utiliza una expresión regular para detectar el OTP en el mensaje del servidor. Por ejemplo:
    java
    Pattern pattern = Pattern.compile("OTP: ([0-9]{6})");
    
    • Extrae el OTP: Busca el mensaje con la expresión regular anterior y extrae el OTP del grupo de captura:
    java
    Matcher matcher = pattern.matcher(message);
    if (matcher.find()) {
        String otp = matcher.group(1);
        // Aquí tienes el OTP extraído automáticamente
    }
    

    Otros métodos:

    API de servicios de SMS: Algunos proveedores de SMS ofrecen API que permiten acceder automáticamente a los mensajes entrantes. Aplicaciones de asistencia: Utiliza aplicaciones de asistencia de Android que pueden interceptar y procesar mensajes SMS. Lectura de notificaciones: Escucha las notificaciones de mensajes SMS y extrae el OTP de su contenido.

    Consejos adicionales:

    Asegúrate de que el servidor envía el OTP en un formato constante y predecible. Considera utilizar un retraso para dar tiempo a que llegue el mensaje SMS. Maneja los casos en los que el OTP no se encuentre en el mensaje. * Sigue las mejores prácticas de seguridad para evitar el acceso no autorizado a los mensajes SMS.

    Respondida el Jun 17, 2015 a las 08:31 - por Gemini

    Votos positivos: 0 | Votos negativos: 0