Programación Android, leer y escribir archivos en memoria interna. Lanzar alarmas

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0

android-archivador

En entradas anteriores se ha visto cómo recoger y usar los recursos de nuestra aplicación, y en esta ocasión vamos a manejar archivos en memoria interna del dispositivo.

En el ejercicio siguiente haremos una aplicación con la que introduciremos 5 tiempos distintos, los guardaremos en un archivo de texto y posteriormente los iremos leyendo para lanzar la alarma pasado el tiempo indicado. Antes partiremos de un estado inicial, en el que todos los tiempos estén marcados a 5 minutos.

Para utilizar como interfaz gráfica podríamos usar algo similar a lo siguiente:

Interfaz gráfica
Interfaz gráfica

El archivo xml del layout que corresponde a esta interfaz es:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
         android:layout_marginLeft="14dp"
        android:layout_marginTop="14dp"
        android:text="@string/tvTitle" />

    <TextView
        android:id="@+id/tv1Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvTitle"
        android:layout_marginLeft="14dp"
        android:layout_marginTop="14dp"
        android:text="@string/tv1Alarma" />

    <TextView
        android:id="@+id/tv2Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tv1Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginTop="14dp"
        android:text="@string/tv2Alarma" />

    <TextView
        android:id="@+id/tv3Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tv2Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginTop="14dp"
        android:text="@string/tv3Alarma" />

    <TextView
        android:id="@+id/tv4Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tv3Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginTop="14dp"
        android:text="@string/tv4Alarma" />

    <TextView
        android:id="@+id/tv5Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tv4Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginTop="14dp"
        android:text="@string/tv5Alarma" />

    <EditText
        android:id="@+id/et1Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/tv1Alarma"
        android:layout_alignBottom="@+id/tv1Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginRight="10dp"
        android:maxLength="2"
        android:layout_toRightOf="@+id/tv1Alarma"
        android:ems="10"
        android:inputType="number" />

    <EditText
        android:id="@+id/et2Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/tv2Alarma"
        android:layout_alignBottom="@+id/tv2Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginRight="10dp"
        android:maxLength="2"
        android:layout_toRightOf="@+id/tv2Alarma"
        android:ems="10"
        android:inputType="number" />

    <EditText
        android:id="@+id/et3Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/tv3Alarma"
        android:layout_alignBottom="@+id/tv3Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginRight="10dp"
        android:maxLength="2"
        android:layout_toRightOf="@+id/tv3Alarma"
        android:ems="10"
        android:inputType="number" />

    <EditText
        android:id="@+id/et4Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/tv4Alarma"
        android:layout_alignBottom="@+id/tv4Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginRight="10dp"
        android:maxLength="2"
        android:layout_toRightOf="@+id/tv4Alarma"
        android:ems="10"
        android:inputType="number" />

    <EditText
        android:id="@+id/et5Alarma"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/tv5Alarma"
        android:layout_alignBottom="@+id/tv5Alarma"
        android:layout_marginLeft="14dp"
        android:layout_marginRight="10dp"
        android:maxLength="2"
        android:layout_toRightOf="@+id/tv5Alarma"
        android:ems="10"
        android:inputType="number" />

    <Button
        android:id="@+id/btActivar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tv5Alarma"
        android:layout_below="@+id/et5Alarma"
        android:layout_marginTop="14dp"
        android:text="@string/btActivar"
        android:onClick="onClick" />

    <TextView
        android:id="@+id/tvTiempoRestante"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/btActivar"
        android:layout_below="@+id/btActivar"
        android:layout_marginTop="20dp"
        android:text="Tiempo Restante"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/tvAlarmRestantes"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tvTiempoRestante"
        android:layout_below="@+id/tvTiempoRestante"
        android:layout_marginTop="15dp"
        android:text="Alarmas Restantes"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

Una vez que ya tenemos nuestra interfaz lista, empecemos con la lógica de nuestra Activity. En primer lugar necesitaremos utilizar los EditText que mostrarán los tiempos de las alarmas, para ello crearemos las instancias de los objetos y en nuestro método onCreate haremos una llamada a un método que los inicializará:

	private EditText et1Alarma, et2Alarma, et3Alarma, et4Alarma, et5Alarma;
	private TextView tvTiempoRestante, tvAlarmRestantes;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		instanciarComponentes();
	}

	private void instanciarComponentes() {
		et1Alarma = (EditText) findViewById(R.id.et1Alarma);
		et2Alarma = (EditText) findViewById(R.id.et2Alarma);
		et3Alarma = (EditText) findViewById(R.id.et3Alarma);
		et4Alarma = (EditText) findViewById(R.id.et4Alarma);
		et5Alarma = (EditText) findViewById(R.id.et5Alarma);
		tvAlarmRestantes = (TextView) findViewById(R.id.tvAlarmRestantes);
		tvTiempoRestante = (TextView) findViewById(R.id.tvTiempoRestante);
	}

Una vez instanciados los componentes que vamos a utilizar, pasaremos instanciar una serie de variables que necesitaremos para almacenar las alarmas, controlar si ya hay una alarma en curso, controlar por qué alarma vamos, y el nombre del archivo txt que utilizaremos. Las declararemos junto con los componentes anteriores, al principio de la clase:

	private final static String FILE = "alarmas.txt";

	private ArrayList<Integer> tiempos;
	private int numAlarmas, numAlarmaActual;

	boolean alarmaFinalizada;

También escribiremos un método que instancie estas variables, al que se llamará también desde onCreate:

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		instanciarComponentes();

		instanciarVariables();
	}

	private void instanciarVariables() {
		tiempos = new ArrayList<Integer>();
		numAlarmas = 0;
		numAlarmaActual = 0;

		alarmaFinalizada = true;
	}

Y por último tenemos que establecer el tiempo inicial, para eso comprobaremos si el archivo de alarmas ya existe, si existe leemos las alarmas y las mostramos en los EditText, de lo contrario lo creamos. Para ello crearemos un nuevo método, que también llamaremos desde onCreate, a su vez este nuevo método hará uso de otros dos métodos, uno que rellenará los EditText, y otro que creará el archivo de texto de ser necesario:


@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		instanciarComponentes();

		instanciarVariables();

		establecerTiempoInicial();
	}

	private void establecerTiempoInicial() {
		// Comprobamos si existe el archivo
		if (existsFile(FILE)) {
			// En el caso de que exista, intentamos rellenar los EditText, si no
			// se rellenan de forma correcta, el archivo.txt estaba corrupto.
			if (!rellenarEditTexts()) {
				// Avisamos al usuario de que el archivo era corrupto.
				Toast.makeText(AlarmasMain.this,
						"Archivo corrupto, reiniciando parámetros...",
						Toast.LENGTH_LONG).show();
				// Creamos de nuevo el archivo.
				crearArchivoAlarmas();
				// Rellenamos los EditText con los valores asignados por defecto
				// en el archvio txt.
				rellenarEditTexts();
			}

		} else {
			// En el caso de que no existiera el archivo txt lo creamos y rellenamos
			// los EditText.
			crearArchivoAlarmas();
			rellenarEditTexts();
		}
	}

	private void crearArchivoAlarmas() {
		try {
			// Creamos un objeto OutputStreamWriter, que será el que nos permita
			// escribir en el archivo de texto. Si el archivo no existía se creará
			// automáticamente.
			// La ruta en la que se creará el archivo será /ruta de nuestro programa/data/data/
			OutputStreamWriter outSWMensaje = new OutputStreamWriter(
					openFileOutput(FILE, Context.MODE_PRIVATE));
			// Escribimos los 5 tiempos iniciales en el archivo.
			outSWMensaje.write("5\n5\n5\n5\n5\n");
			// Cerramos el flujo de escritura del archivo, este paso es obligatorio,
			// de no hacerlo no se podrá acceder posteriormente al archivo.
			outSWMensaje.close();
		} catch (Exception e) {
			Log.e(TAG, e.getMessage());
		}
	}

	private boolean rellenarEditTexts() {
		try {
			// Creamos un objeto InputStreamReader, que será el que nos permita
			// leer el contenido del archivo de texto.
			InputStreamReader archivo = new InputStreamReader(
					openFileInput(FILE));
			// Creamos un objeto buffer, en el que iremos almacenando el contenido
			// del archivo.
			BufferedReader br = new BufferedReader(archivo);
			// Por cada EditText leemos una línea y escribimos el contenido en el
			// EditText.
			String texto = br.readLine();
			et1Alarma.setText(texto);

			texto = br.readLine();
			et2Alarma.setText(texto);

			texto = br.readLine();
			et3Alarma.setText(texto);

			texto = br.readLine();
			et4Alarma.setText(texto);

			texto = br.readLine();
			et5Alarma.setText(texto);
			// Cerramos el flujo de lectura del archivo.
			br.close();
			return true;

		} catch (Exception e) {
			return false;
		}
	}

El método que comprueba si existe o no el archivo sería así:

	public boolean existsFile(String fileName) {
		for (String tmp : fileList()) {
			if (tmp.equals(fileName))
				return true;
		}
		return false;
	}

Por último sólo nos falta implementar el botón, para que inicie el proceso de lanzar nuestras alarmas una vez que haya transcurrido el tiempo indicado, este botón comprobará si los EditText contienen texto, actualizará el archivo de texto con los valores de las alarmas para una siguiente ejecución, y se iniciará el proceso de lanzamiento de alarmas:

	public void onClick(View v) {

		if (et1Alarma.getText().length() != 0
				&& et2Alarma.getText().length() != 0
				&& et3Alarma.getText().length() != 0
				&& et4Alarma.getText().length() != 0
				&& et5Alarma.getText().length() != 0) {

			tiempos.add(Integer.parseInt(et1Alarma.getText().toString()));
			tiempos.add(Integer.parseInt(et2Alarma.getText().toString()));
			tiempos.add(Integer.parseInt(et3Alarma.getText().toString()));
			tiempos.add(Integer.parseInt(et4Alarma.getText().toString()));
			tiempos.add(Integer.parseInt(et5Alarma.getText().toString()));

			actualizarArchivoAlarmas();

			lanzarAlarma();
		}
	}

Por último los métodos actualizarArchivoAlarmas y lanzarAlarma harían lo siguiente:

	private void actualizarArchivoAlarmas() {
		try {
			OutputStreamWriter outSWMensaje = new OutputStreamWriter(
					openFileOutput(FILE, Context.MODE_PRIVATE));
			// Por cada tiempo escrito en los EditText escribimos una línea
			// en el archivo de alarmas.
			for (int i : tiempos) {
				outSWMensaje.write(i + "\n");
			}

			outSWMensaje.close();
		} catch (Exception e) {
			Log.e(TAG, e.getMessage());
			Toast.makeText(this, "No se pudo crear el archivo de alarmas";,
					Toast.LENGTH_LONG).show();
		}
	}

	private void lanzarAlarma() {
		// Sacamos el tiempo restante para lanzar la alarma actual.
		int i = tiempos.get(numAlarmaActual);
		// Controlamos la alarma que tiene que sonar
		numAlarmaActual++;
		// Variable para controlar cuándo ha finalizado una alarma, para poder
		// lanzar la siguiente.
		alarmaFinalizada = false;
		// Actualizamos el texto de las alarmas restantes.
		tvAlarmRestantes.setText("Alarmas restantes: " + numAlarmas);
		// Instanciamos un nuevo contador, que se lanzará con el tiempo debido.
		CountDownTimer timer = new CountDownTimer(i * 60 * 1000, 100) {

			@Override
			public void onTick(long millisUntilFinished) {
				// Actualizamos el tiempo restante para la nueva alarma.
				tvTiempoRestante.setText(String
						.valueOf(millisUntilFinished / 1000) + "s");
			}

			@Override
			public void onFinish() {
				// Al finalizar reproducimos un archivo de audio guardado en la
				// carpeta raw.
				MediaPlayer mp = MediaPlayer.create(AlarmasMain.this, R.raw.r2d2);
				mp.start();
				// Creamos un AlertDialog que avise de la llegada de la alarma.
				AlertDialog.Builder alertDialog;
				alertDialog = new AlertDialog.Builder(AlarmasMain.this);
				alertDialog.setTitle("Alarma");
				alertDialog.setMessage("Es la hora!!");
				// Creamos un botón en el AlertDialog, que será el que nos permita
				// reanudar el tiempo de la siguiente alarma.
				alertDialog.setPositiveButton("Ok", new OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						// Cuando se pulse el botón se marca la alarma como
						// finalizada, se actualizan las alarmas restantes y
						// en el caso de no ser la última alarma se vuelve a
						// ejecutar el método lanzarAlarma.
						alarmaFinalizada = true;
						numAlarmas--;
						tvAlarmRestantes.setText"Alarmas restantes: "
								+ numAlarmas);
						if (numAlarmaActual < tiempos.size())
							lanzarAlarma();
					}
				});
				// Mostramos el AlertDialog al usuario.
				alertDialog.show();
			}
		};
		// Una vez creado el temporizador lo iniciamos.
		timer.start();
	}

Y con esto ya podemos crear un sencillo programa de alarmas leyendo y escribiendo en archivos de texto.

Descargas

Puedes ver el resultado del ejemplo en tu móvil descargando nuestra aplicación desde Google Play.

Puedes acceder a nuestro repositorio para bajar el proyecto aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

8 thoughts on “Programación Android, leer y escribir archivos en memoria interna. Lanzar alarmas

  1. hola como se podria colocar una archivo txt en la memoria interna con informacion y validara con algun datos y sino fuera igual detubiera la aplicacion

  2. hola, gracias por tu blog, mira lo que pasa es que acabo de hacer una aplicacion que toma unos datos desde un txt, eso ya lo logré, pero ahora quiero que el apk instale no solo el software sino tambien los archivos de texto, ¿de casualidad sabes como hacerlo?

    gracias

  3. Hola, gracias por le tuto… miren que estoy programando una alarma para una hora espeficica, del dia… pero no esta funcionando.

    el siguiente metodo intenta agendar la alarma… pero esta no se esta lanzando a la hora indicada, sino q esta lanzandose cuando se abre el app. Creo es xq el scheduleNotification es llamado desde el onCreate del MainActivity…

    Que me pueden sugerir?

    public void scheduleNotification(Context context , Notification notification, int hour, int minute ,long delay) {

    //Creo el log
    Log.i(“MainAct”, “Agendo Notificacion “+hour+”:”+minute + “, cada “+ delay );
    Intent notificationIntent = new Intent(context, NotificationPublisher.class);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, hour);
    calendar.set(Calendar.MINUTE, minute);

    notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, 1);
    notificationIntent.putExtra(NotificationPublisher.NOTIFICATION, notification);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    long futureInMillis = SystemClock.elapsedRealtime() + delay;
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    // alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);

    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), delay, pendingIntent);

    }

    But is not working. For example is working only if you lunch the app., and after frequency-minuts, but not in the exact time.

    any suggestion?

Deja un comentario