From 0d022cec30398021bf130129b70ed197f0bb201a Mon Sep 17 00:00:00 2001 From: vialatol Date: Mon, 11 May 2026 19:20:53 +0200 Subject: [PATCH 01/14] Version utilisable et sympa --- .classpath | 9 - .project | 33 - .settings/org.eclipse.jdt.ui.prefs | 3 - AndroidManifest.xml | 48 -- app/build.gradle | 40 + app/src/main/AndroidManifest.xml | 62 ++ .../main/java}/org/balau/fakedawn/Alarm.java | 111 ++- .../org/balau/fakedawn/AlarmReceiver.java | 68 +- .../org/balau/fakedawn/ColorPickerDialog.java | 0 .../main/java/org/balau/fakedawn/Dawn.java | 269 ++++++ .../java}/org/balau/fakedawn/DawnSound.java | 0 .../balau/fakedawn/InstallationReceiver.java | 0 .../org/balau/fakedawn/IntervalSlider.java | 0 .../java}/org/balau/fakedawn/License.java | 0 .../java/org/balau/fakedawn/Preferences.java | 490 +++++++++++ .../java}/org/balau/fakedawn/StartAtBoot.java | 0 .../java}/org/balau/fakedawn/TimeSlider.java | 0 .../main/res}/drawable-hdpi/ic_launcher.png | Bin .../main/res}/drawable-ldpi/ic_launcher.png | Bin .../main/res}/drawable-mdpi/ic_launcher.png | Bin .../main/res}/drawable-xhdpi/ic_launcher.png | Bin {res => app/src/main/res}/drawable/sound.png | Bin .../src/main/res}/drawable/sunrise.png | Bin {res => app/src/main/res}/layout/dawn.xml | 0 {res => app/src/main/res}/layout/license.xml | 0 app/src/main/res/layout/predawn.xml | 19 + app/src/main/res/layout/preferences.xml | 328 ++++++++ {res => app/src/main/res}/raw/license | 0 {res => app/src/main/res}/values/strings.xml | 0 app/src/main/res/values/themes.xml | 22 + build.gradle | 13 + gradle.properties | 3 + res/layout/preferences.xml | 241 ------ settings.gradle | 16 + src/org/balau/fakedawn/Dawn.java | 268 ------ src/org/balau/fakedawn/Preferences.java | 764 ------------------ .../balau/fakedawn/test/PreferencesTest.java | 2 - 37 files changed, 1401 insertions(+), 1408 deletions(-) delete mode 100644 .classpath delete mode 100644 .project delete mode 100644 .settings/org.eclipse.jdt.ui.prefs delete mode 100644 AndroidManifest.xml create mode 100644 app/build.gradle create mode 100644 app/src/main/AndroidManifest.xml rename {src => app/src/main/java}/org/balau/fakedawn/Alarm.java (69%) rename {src => app/src/main/java}/org/balau/fakedawn/AlarmReceiver.java (59%) rename {src => app/src/main/java}/org/balau/fakedawn/ColorPickerDialog.java (100%) create mode 100644 app/src/main/java/org/balau/fakedawn/Dawn.java rename {src => app/src/main/java}/org/balau/fakedawn/DawnSound.java (100%) rename {src => app/src/main/java}/org/balau/fakedawn/InstallationReceiver.java (100%) rename {src => app/src/main/java}/org/balau/fakedawn/IntervalSlider.java (100%) rename {src => app/src/main/java}/org/balau/fakedawn/License.java (100%) create mode 100644 app/src/main/java/org/balau/fakedawn/Preferences.java rename {src => app/src/main/java}/org/balau/fakedawn/StartAtBoot.java (100%) rename {src => app/src/main/java}/org/balau/fakedawn/TimeSlider.java (100%) rename {res => app/src/main/res}/drawable-hdpi/ic_launcher.png (100%) rename {res => app/src/main/res}/drawable-ldpi/ic_launcher.png (100%) rename {res => app/src/main/res}/drawable-mdpi/ic_launcher.png (100%) rename {res => app/src/main/res}/drawable-xhdpi/ic_launcher.png (100%) rename {res => app/src/main/res}/drawable/sound.png (100%) rename {res => app/src/main/res}/drawable/sunrise.png (100%) rename {res => app/src/main/res}/layout/dawn.xml (100%) rename {res => app/src/main/res}/layout/license.xml (100%) create mode 100644 app/src/main/res/layout/predawn.xml create mode 100644 app/src/main/res/layout/preferences.xml rename {res => app/src/main/res}/raw/license (100%) rename {res => app/src/main/res}/values/strings.xml (100%) create mode 100644 app/src/main/res/values/themes.xml create mode 100644 build.gradle create mode 100644 gradle.properties delete mode 100644 res/layout/preferences.xml create mode 100644 settings.gradle delete mode 100644 src/org/balau/fakedawn/Dawn.java delete mode 100644 src/org/balau/fakedawn/Preferences.java diff --git a/.classpath b/.classpath deleted file mode 100644 index dfadf67..0000000 --- a/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/.project b/.project deleted file mode 100644 index 84d7c45..0000000 --- a/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - FakeDawn - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index dea482e..0000000 --- a/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.ui.javadoc=false -org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/AndroidManifest.xml b/AndroidManifest.xml deleted file mode 100644 index 0898327..0000000 --- a/AndroidManifest.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..39ec75e --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,40 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'org.balau.fakedawn' + compileSdk 34 + + defaultConfig { + applicationId "org.balau.fakedawn" + minSdk 21 // On monte à 21 (Android 5.0) pour la compatibilité moderne + targetSdk 34 + versionCode 5 + versionName "2.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } +} + +dependencies { + // Bibliothèques de compatibilité modernes (AndroidX) + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.11.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + + // Pour les préférences (très utilisé dans les vieux projets) + implementation 'androidx.preference:preference:1.2.1' +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..60748ed --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/org/balau/fakedawn/Alarm.java b/app/src/main/java/org/balau/fakedawn/Alarm.java similarity index 69% rename from src/org/balau/fakedawn/Alarm.java rename to app/src/main/java/org/balau/fakedawn/Alarm.java index b8886b7..32978ce 100644 --- a/src/org/balau/fakedawn/Alarm.java +++ b/app/src/main/java/org/balau/fakedawn/Alarm.java @@ -24,17 +24,24 @@ import java.util.Calendar; import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.content.SharedPreferences; +import android.os.Build; import android.os.IBinder; import android.util.Log; import android.widget.Toast; +import androidx.core.app.NotificationCompat; +import android.content.pm.ServiceInfo; public class Alarm extends Service { public static final String EXTRA_SHOW_TOAST = "org.balau.fakedawn.Alarm.EXTRA_SHOW_TOAST"; + private static final int NOTIFICATION_ID = 1; private static final long TOLERANCE_MILLIS = 1000*10; @Override @@ -48,6 +55,30 @@ public IBinder onBind(Intent arg0) { */ @Override public int onStartCommand(Intent intent, int flags, int startId) { + // 1. Créer le canal de notification pour Android 8+ + String CHANNEL_ID = "fakedawn_service"; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel(CHANNEL_ID, + "Alarme FakeDawn", NotificationManager.IMPORTANCE_LOW); + getSystemService(NotificationManager.class).createNotificationChannel(channel); + } + + // 2. Lancer en premier plan (Foreground) pour éviter le crash immédiat + Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("FakeDawn") + .setContentText("Mise à jour de l'alarme...") + .setSmallIcon(android.R.drawable.ic_lock_idle_alarm) + .build(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startForeground( + NOTIFICATION_ID, + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE // Matches the manifest + ); + } else { + startForeground(NOTIFICATION_ID, notification); + } boolean showToast; if(intent != null) @@ -84,18 +115,25 @@ public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); } // If we get killed, after returning from here, restart + stopForeground(true); return START_STICKY; } - + private PendingIntent getOpenDawnPendingIntent() { - Intent openDawn = new Intent(AlarmReceiver.ACTION_START_ALARM); - PendingIntent openDawnPendingIntent = PendingIntent.getBroadcast( - getApplicationContext(), - 0, + Intent openDawn = new Intent(this, AlarmReceiver.class); + openDawn.setAction(AlarmReceiver.ACTION_START_ALARM); + + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; // Obligatoire sur Android 12+ + } + + return PendingIntent.getBroadcast( + getApplicationContext(), + 0, openDawn, - 0); - return openDawnPendingIntent; + flags); } private AlarmManager getAlarmManager() @@ -178,37 +216,42 @@ private Calendar getNextAlarmTime() return nextAlarmTime; } + private void set(Calendar nextAlarmTime) { + AlarmManager alarmManager = getAlarmManager(); + PendingIntent openDawnIntent = getOpenDawnPendingIntent(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, + nextAlarmTime.getTimeInMillis(), openDawnIntent); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + alarmManager.setExact(AlarmManager.RTC_WAKEUP, + nextAlarmTime.getTimeInMillis(), openDawnIntent); + } else { + alarmManager.set(AlarmManager.RTC_WAKEUP, + nextAlarmTime.getTimeInMillis(), openDawnIntent); + } + } + private void set(AlarmManager alarmManager, int type, long triggerAtMillis, PendingIntent operation) { - // API 19 changed set() behaviour and added setExact + // API 19 changed set() behaviour and added setExact // https://developer.android.com/reference/android/app/AlarmManager.html#set(int, long, android.app.PendingIntent) // Using setExact if it exists, otherwise fall back to set. - try { - Method setExact = AlarmManager.class.getDeclaredMethod( - "setExact", int.class, long.class, PendingIntent.class); - setExact.invoke(alarmManager, type, - triggerAtMillis, operation); - } catch (NoSuchMethodException e) { - alarmManager.set(type, - triggerAtMillis, operation); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - private void set(Calendar nextAlarmTime) - { - AlarmManager alarmManager = getAlarmManager(); - PendingIntent openDawnIntent = getOpenDawnPendingIntent(); - set( - alarmManager, - AlarmManager.RTC_WAKEUP, - nextAlarmTime.getTimeInMillis(), - openDawnIntent); + try { + Method setExact = AlarmManager.class.getDeclaredMethod( + "setExact", int.class, long.class, PendingIntent.class); + setExact.invoke(alarmManager, type, + triggerAtMillis, operation); + } catch (NoSuchMethodException e) { + alarmManager.set(type, + triggerAtMillis, operation); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } } private String getPlural(long n, String name) diff --git a/src/org/balau/fakedawn/AlarmReceiver.java b/app/src/main/java/org/balau/fakedawn/AlarmReceiver.java similarity index 59% rename from src/org/balau/fakedawn/AlarmReceiver.java rename to app/src/main/java/org/balau/fakedawn/AlarmReceiver.java index a5a5d7e..6a8ab0a 100644 --- a/src/org/balau/fakedawn/AlarmReceiver.java +++ b/app/src/main/java/org/balau/fakedawn/AlarmReceiver.java @@ -20,14 +20,21 @@ import java.util.Calendar; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; +// import android.content.SharedPreferences; +import android.os.Build; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; +import androidx.core.app.NotificationCompat; + /** * @author francesco * @@ -66,7 +73,7 @@ private void releaseWakeLock(boolean expectedHeld) { } } - public static Calendar getAlarmStart(SharedPreferences pref) + /* public static Calendar getAlarmStart(SharedPreferences pref) { Calendar rightNow = Calendar.getInstance(); @@ -90,7 +97,7 @@ else if(alarmStartMillis - rightNowMillis < -halfDayMillis) } return alarmStart; - } + } */ /* (non-Javadoc) * @see android.content.BroadcastReceiver#onReceive(android.content.Context, android.content.Intent) @@ -111,13 +118,62 @@ public void onReceive(Context context, Intent intent) { PowerManager.PARTIAL_WAKE_LOCK, "FakeDawn.AlarmReceiver"); AlarmReceiver.m_alarmWakeLock.acquire(WAKE_LOCK_TIMEOUT_MILLIS); //TODO: use WakefulBroadcastReceiver instead? - Intent openDawn = new Intent(context, Dawn.class); + + // Il faudra peut-être modifier ci-après + /* Intent openDawn = new Intent(context, Dawn.class); openDawn.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS| Intent.FLAG_FROM_BACKGROUND); - Log.d("FakeDawn", "Starting Dawn Activity."); - context.startActivity(openDawn); + Log.d("FakeDawn", "Starting Dawn Activity."); */ + // 1. Créer l'intention pour l'écran de réveil + Intent dawnIntent = new Intent(context, Dawn.class); + dawnIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION); + + // 2. Créer le PendingIntent (obligatoire pour la notification) + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(context, 0, dawnIntent, flags); + + // 3. Créer la notification de réveil + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "fakedawn_alarm") + .setSmallIcon(android.R.drawable.ic_lock_idle_alarm) + .setContentTitle("Réveil FakeDawn") + .setContentText("L'alarme est en cours...") + .setPriority(NotificationCompat.PRIORITY_MAX) + .setCategory(NotificationCompat.CATEGORY_ALARM) + .setFullScreenIntent(fullScreenPendingIntent, true); // <--- C'est ici que la magie opère + + // 4. Envoyer la notification + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + // Identifiant du canal (doit correspondre à celui utilisé dans le Builder) + String CHANNEL_ID = "fakedawn_alarm"; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + // On vérifie si le canal existe déjà + if (nm.getNotificationChannel(CHANNEL_ID) == null) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + "Réveil FakeDawn", + NotificationManager.IMPORTANCE_HIGH // Crucial pour le plein écran + ); + channel.setDescription("Utilisé pour afficher l'écran de réveil"); + channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + + // Optionnel : Désactiver le vibreur/son par défaut de la notification + // car l'activité Dawn s'en occupe déjà + channel.enableVibration(false); + channel.setSound(null, null); + + nm.createNotificationChannel(channel); + } + } + notificationManager.notify(2, builder.build()); + // context.startActivity(openDawn); //TODO: start sound service? } else if(intent.getAction().equals(ACTION_STOP_ALARM)) diff --git a/src/org/balau/fakedawn/ColorPickerDialog.java b/app/src/main/java/org/balau/fakedawn/ColorPickerDialog.java similarity index 100% rename from src/org/balau/fakedawn/ColorPickerDialog.java rename to app/src/main/java/org/balau/fakedawn/ColorPickerDialog.java diff --git a/app/src/main/java/org/balau/fakedawn/Dawn.java b/app/src/main/java/org/balau/fakedawn/Dawn.java new file mode 100644 index 0000000..d29de94 --- /dev/null +++ b/app/src/main/java/org/balau/fakedawn/Dawn.java @@ -0,0 +1,269 @@ +/** + * Copyright 2012 Francesco Balducci + * + * This file is part of FakeDawn. + * + * FakeDawn is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FakeDawn is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FakeDawn. If not, see . + */ +package org.balau.fakedawn; + +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.app.NotificationManager; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +// import android.widget.TextView; +import android.media.AudioAttributes; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +// import android.util.Log; +import java.io.IOException; +import android.os.Vibrator; + +public class Dawn extends Activity { + private boolean useDismissButton; + private ValueAnimator colorAnimation; + private MediaPlayer mediaPlayer; + private final Handler soundHandler = new Handler(Looper.getMainLooper()); + private Runnable stopSoundRunnable; + private Vibrator vibrator; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + // 1. Luminosité au minimum immédiatement + WindowManager.LayoutParams lp = getWindow().getAttributes(); + lp.screenBrightness = 0.01f; + getWindow().setAttributes(lp); + super.onCreate(savedInstanceState); + + // 1. Gestion du réveil de l'écran (Indispensable pour Android 14) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + setShowWhenLocked(true); + setTurnScreenOn(true); + } + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | + WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); + + // 2. Passage en plein écran (cache les barres système) + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + + setContentView(R.layout.predawn); + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + if (nm != null) nm.cancel(2); // AlarmReceiver.NOTIFICATION_ALARM_RUNNING_ID); + SharedPreferences pref = getSharedPreferences("main", MODE_PRIVATE); + useDismissButton = pref.getBoolean("dismiss", false); + View root = findViewById(R.id.predawn_root); + root.setOnClickListener(v -> { + if (!useDismissButton) { + // Méthode "Touch Screen" : on arrête tout + stopAlarm(); + } else { + // Méthode "Dismiss Button" : on bascule vers la vue normale + showDawnWithButton(); + } + }); + soundHandler.postDelayed(() -> { + boolean soundEnabled = pref.getBoolean("sound_master_enabled", true); + + if (soundEnabled) { + startAlarmSound(); // Lance la musique + boolean vibrateEnabled = pref.getBoolean("vibrate", false); // Clé de votre ToggleButton + if (vibrateEnabled) { + vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + if (vibrator != null && vibrator.hasVibrator()) { + // Vibration : 0ms pause, 500ms vibre, 500ms pause... (en boucle) + long[] pattern = {0, 500, 10000}; + vibrator.vibrate(pattern, 0); // 0 = répéter indéfiniment + } + } + // 2. Programmation de l'ARRÊT automatique du son + stopSoundRunnable = () -> { + if (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) mediaPlayer.stop(); + mediaPlayer.release(); + mediaPlayer = null; + // Log.d("FakeDawn", "Sound stopped automatically after duration limit."); + if (vibrator != null) { + vibrator.cancel(); + } + } + }; + soundHandler.postDelayed(stopSoundRunnable, pref.getInt("sound_duration", 15) * 60 * 1000L); + } + }, pref.getInt("light_pre_delay", 15) * 60 * 1000L); + startColorAnimation(root); + } + + private void startAlarmSound() { + SharedPreferences pref = getSharedPreferences("main", MODE_PRIVATE); + String soundStr = pref.getString("sound", ""); + + if (soundStr.isEmpty()) { + // Log.d("FakeDawn", "No sound selected (Silent)"); + return; + } + + try { + mediaPlayer = new MediaPlayer(); + mediaPlayer.setDataSource(this, Uri.parse(soundStr)); + + // Configuration pour que le son sorte comme une alarme (même si le téléphone est en vibreur) + mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ALARM) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build()); + + mediaPlayer.setLooping(true); // La musique boucle jusqu'à l'arrêt + mediaPlayer.prepare(); + mediaPlayer.start(); + + // Log.d("FakeDawn", "Alarm sound started"); + } catch (IOException e) { + // Log.e("FakeDawn", "Error playing sound: " + e.getMessage()); + } + } + + private void showDawnWithButton() { + if (colorAnimation != null) colorAnimation.cancel(); + // Affiche le layout avec le bouton + setContentView(R.layout.dawn); + + // Rétablir la luminosité normale (système) + WindowManager.LayoutParams lp = getWindow().getAttributes(); + lp.screenBrightness = -1f; // -1 = Valeur par défaut de l'utilisateur + getWindow().setAttributes(lp); + final View background = findViewById(R.id.dawn_background); + + // Configurer le bouton Dismiss de la nouvelle vue + Button dismissBtn = findViewById(R.id.dismiss_button); + if (dismissBtn != null) { + dismissBtn.setOnClickListener(v -> stopAlarm()); + } + + // Lancer l'animation de couleur si vous en avez une + startColorAnimation(background); + } + + private void startColorAnimation(View background) { + // 5. Calcul de la durée de transition + SharedPreferences pref = getSharedPreferences("main", MODE_PRIVATE); + // On récupère la durée : light_max (fin) - light_start (début) + int durationMinutes = pref.getInt("light_pre_delay", 15) + pref.getInt("light_post_delay", 15); + if (durationMinutes <= 0) durationMinutes = 1; // Sécurité + long durationMillis = durationMinutes * 60 * 1000; + + // 6. Animation du fond (Noir vers Blanc) + colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), Color.BLACK, Color.WHITE); + colorAnimation.setDuration(durationMillis); + + colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + int color = (int) animator.getAnimatedValue(); + background.setBackgroundColor(color); + + // Optionnel : On peut aussi ajuster la luminosité physique de l'écran + WindowManager.LayoutParams lp = getWindow().getAttributes(); + if (lp.screenBrightness < 1.0f && lp.screenBrightness != -1f) { + lp.screenBrightness = animator.getAnimatedFraction(); + getWindow().setAttributes(lp); + } + } + }); + + colorAnimation.start(); + } + private void stopAlarm() { + soundHandler.removeCallbacksAndMessages(null); + if (colorAnimation != null) colorAnimation.cancel(); + if (vibrator != null) { + vibrator.cancel(); + } + if (mediaPlayer != null) { + try { + if (mediaPlayer.isPlaying()) mediaPlayer.stop(); + } catch (Exception e) { + mediaPlayer.release(); + mediaPlayer = null; + } + } + // 2. EFFACER LA NOTIFICATION "En cours" + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + if (nm != null) nm.cancel(2); // ID 2 correspond à celui d'AlarmReceiver + + // 3. Fermer l'activité + finish(); + } +} + + + /* + // 3. Récupération des vues + final View background = findViewById(R.id.dawn_background); + Button dismissButton = findViewById(R.id.dismiss_button); + // Note: DigitalClock est déprécié mais fonctionne encore si vous l'utilisez dans le XML + + // 4. Action du bouton Dismiss + dismissButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Arrêter l'activité (et donc l'alarme visuelle) + finish(); + } + }); + + // 5. Calcul de la durée de transition + SharedPreferences pref = getSharedPreferences("main", MODE_PRIVATE); + // On récupère la durée : light_max (fin) - light_start (début) + int durationMinutes = pref.getInt("light_max", 30) - pref.getInt("light_start", 0); + if (durationMinutes <= 0) durationMinutes = 1; // Sécurité + long durationMillis = durationMinutes * 60 * 1000; + + // 6. Animation du fond (Noir vers Blanc) + ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), Color.BLACK, Color.WHITE); + colorAnimation.setDuration(durationMillis); + + colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + int color = (int) animator.getAnimatedValue(); + background.setBackgroundColor(color); + + // Optionnel : On peut aussi ajuster la luminosité physique de l'écran + WindowManager.LayoutParams lp = getWindow().getAttributes(); + lp.screenBrightness = animator.getAnimatedFraction(); // de 0.0 à 1.0 + getWindow().setAttributes(lp); + } + }); + + colorAnimation.start(); + } +}*/ \ No newline at end of file diff --git a/src/org/balau/fakedawn/DawnSound.java b/app/src/main/java/org/balau/fakedawn/DawnSound.java similarity index 100% rename from src/org/balau/fakedawn/DawnSound.java rename to app/src/main/java/org/balau/fakedawn/DawnSound.java diff --git a/src/org/balau/fakedawn/InstallationReceiver.java b/app/src/main/java/org/balau/fakedawn/InstallationReceiver.java similarity index 100% rename from src/org/balau/fakedawn/InstallationReceiver.java rename to app/src/main/java/org/balau/fakedawn/InstallationReceiver.java diff --git a/src/org/balau/fakedawn/IntervalSlider.java b/app/src/main/java/org/balau/fakedawn/IntervalSlider.java similarity index 100% rename from src/org/balau/fakedawn/IntervalSlider.java rename to app/src/main/java/org/balau/fakedawn/IntervalSlider.java diff --git a/src/org/balau/fakedawn/License.java b/app/src/main/java/org/balau/fakedawn/License.java similarity index 100% rename from src/org/balau/fakedawn/License.java rename to app/src/main/java/org/balau/fakedawn/License.java diff --git a/app/src/main/java/org/balau/fakedawn/Preferences.java b/app/src/main/java/org/balau/fakedawn/Preferences.java new file mode 100644 index 0000000..6598197 --- /dev/null +++ b/app/src/main/java/org/balau/fakedawn/Preferences.java @@ -0,0 +1,490 @@ +/** + * Copyright 2012 Francesco Balducci + * + * This file is part of FakeDawn. + * + * FakeDawn is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FakeDawn is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FakeDawn. If not, see . + */ + +package org.balau.fakedawn; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.balau.fakedawn.ColorPickerDialog.OnColorChangedListener; +import org.balau.fakedawn.TimeSlider.OnTimesChangedListener; // Import décommenté + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.TimePickerDialog; +import android.app.TimePickerDialog.OnTimeSetListener; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager.NameNotFoundException; +import android.media.AudioManager; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.NumberPicker; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; +import android.widget.TimePicker; +import android.widget.Toast; +import android.widget.ToggleButton; + +public class Preferences extends Activity implements OnClickListener, OnSeekBarChangeListener, OnColorChangedListener, OnTimeSetListener, OnTimesChangedListener { + + // Variables pour ta nouvelle logique + private int lightStartH = 7, lightStartM = 0; + private int lightPreDelayMinutes = 15; + private int lightPostDelayMinutes = 15; + private int lightDuration = lightPreDelayMinutes + lightPostDelayMinutes; + private int soundStartH = 7, soundStartM = 15; + private int soundDuration = 15; + private CheckBox m_checkBoxSoundEnabled; + private LinearLayout m_containerSoundOptions; + private static final int REQUEST_PICK_SOUND = 0; + private Uri m_soundUri; + private HelpListener m_helpListener = new HelpListener(); + + private TextView txtLightStart, txtLightPreDelay, txtLightPostDelay, txtSoundStart, txtSoundDuration; + + private void updateLabels() { + if (txtLightStart != null) txtLightStart.setText(String.format("%02d:%02d", lightStartH, lightStartM)); + if (txtLightPreDelay != null) txtLightPreDelay.setText(lightPreDelayMinutes + " min"); + if (txtLightPostDelay != null) txtLightPostDelay.setText(lightPostDelayMinutes + " min"); + if (txtSoundStart != null) txtSoundStart.setText(String.format("%02d:%02d", soundStartH, soundStartM)); + if (txtSoundDuration != null) txtSoundDuration.setText(soundDuration + " min"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.preferences); + + txtLightStart = findViewById(R.id.txtLightStart); + txtLightPreDelay = findViewById(R.id.txtLightPreDelay); + txtLightPostDelay = findViewById(R.id.txtLightPostDelay); + txtSoundStart = findViewById(R.id.txtSoundStart); + txtSoundDuration = findViewById(R.id.txtSoundDuration); + m_checkBoxSoundEnabled = findViewById(R.id.checkBoxSoundEnabled); + m_containerSoundOptions = findViewById(R.id.containerSoundOptions); + final TextView txtSoundDuration = findViewById(R.id.txtSoundDuration); + + m_checkBoxSoundEnabled.setOnCheckedChangeListener((buttonView, isChecked) -> { + // Active/Désactive le container et le texte de durée + updateSoundUI(isChecked); + }); + // Liaison des clics + /*if(txtLightStart != null) txtLightStart.setOnClickListener(this); + if(txtLightPreDelay != null) txtLightPreDelay.setOnClickListener(this); + if(txtLightPostDelay != null) txtLightPostDelay.setOnClickListener(this); + if(txtSoundStart != null) txtSoundStart.setOnClickListener(this); + if(txtSoundDuration != null) txtSoundDuration.setOnClickListener(this); */ + if(txtLightStart != null) txtLightStart.setOnClickListener(v -> { + showTimePicker(lightStartH, lightStartM, (view, hour, minute) -> { + lightStartH = hour; + lightStartM = minute; + // Formule : Sound = Light + PreDelay + soundStartH = (lightStartH * 60 + lightStartM + lightPreDelayMinutes) % 1440 / 60; + soundStartM = (lightStartH * 60 + lightStartM + lightPreDelayMinutes) % 1440 % 60; + updateLabels(); + }); + }); + if(txtLightPreDelay != null) txtLightPreDelay.setOnClickListener(v -> { + showDurationPicker(lightPreDelayMinutes, (newDelay) -> { + lightPreDelayMinutes = newDelay; + // Formule : Sound = Light + PreDelay + int totalLightMinutes = lightStartH * 60 + lightStartM; + int totalSoundMinutes = (totalLightMinutes + lightPreDelayMinutes) % 1440; + + soundStartH = totalSoundMinutes / 60; + soundStartM = totalSoundMinutes % 60; + lightDuration = lightPreDelayMinutes + lightPostDelayMinutes; + updateLabels(); + }); + }); + if(txtLightPostDelay != null) txtLightPostDelay.setOnClickListener(v -> { + showDurationPicker(lightPostDelayMinutes, (newDelay) -> { + lightPostDelayMinutes = newDelay; + lightDuration = lightPreDelayMinutes + lightPostDelayMinutes; + updateLabels(); + }); + }); + if(txtSoundStart != null) txtSoundStart.setOnClickListener(v -> { + showTimePicker(soundStartH, soundStartM, (view, hour, minute) -> { + soundStartH = hour; + soundStartM = minute; + // Logic: Light = Sound - PreDelay + int totalMinutes = (soundStartH * 60 + soundStartM) - lightPreDelayMinutes; + if (totalMinutes < 0) totalMinutes += 1440; + lightStartH = totalMinutes / 60; + lightStartM = totalMinutes % 60; + updateLabels(); + }); + }); + if(txtSoundDuration != null) txtSoundDuration.setOnClickListener(this); + + // Liaison des boutons standards + findViewById(R.id.buttonSave).setOnClickListener(this); + findViewById(R.id.buttonDiscard).setOnClickListener(this); + findViewById(R.id.buttonSound).setOnClickListener(this); + + SeekBar seekBarVolume = (SeekBar) findViewById(R.id.seekBarVolume); + seekBarVolume.setOnSeekBarChangeListener(this); + + loadPreferences(); + } + + private void updateSoundUI(boolean enabled) { + // Grise ou active les composants + m_containerSoundOptions.setEnabled(enabled); + findViewById(R.id.txtSoundDuration).setEnabled(enabled); + findViewById(R.id.buttonSound).setEnabled(enabled); + findViewById(R.id.seekBarVolume).setEnabled(enabled); + findViewById(R.id.toggleButtonVibrate).setEnabled(enabled); + + // Change l'opacité pour l'effet visuel "grisé" + float alpha = enabled ? 1.0f : 0.4f; + m_containerSoundOptions.setAlpha(alpha); + findViewById(R.id.txtSoundDuration).setAlpha(alpha); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + // On vérifie que c'est bien le retour du sélecteur de son (REQUEST_PICK_SOUND est 0 chez vous) + if (resultCode == RESULT_OK && requestCode == REQUEST_PICK_SOUND) { + Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); + + // On met à jour la variable de classe + m_soundUri = uri; + + // On met à jour le texte du bouton immédiatement + updateSoundButtonText(); + } + } + + private void updateSoundButtonText() { + Button buttonSound = findViewById(R.id.buttonSound); + if (buttonSound == null) return; + + if (m_soundUri != null) { + // On récupère le titre de la sonnerie via le RingtoneManager + android.media.Ringtone ringtone = RingtoneManager.getRingtone(this, m_soundUri); + if (ringtone != null) { + buttonSound.setText(ringtone.getTitle(this)); + } else { + buttonSound.setText("Sound Selected"); + } + } else { + buttonSound.setText("Silent"); + } + } + + private void loadPreferences() { + SharedPreferences pref = getSharedPreferences("main", MODE_PRIVATE); + int startH = pref.getInt("dawn_start_hour", 7); + int startM = pref.getInt("dawn_start_minute", 0); + int globalStart = startH * 60 + startM; + + // Calcul des heures à partir des offsets enregistrés + int lStart = globalStart; // + pref.getInt("light_start", 0); + lightStartH = lStart / 60; + lightStartM = lStart % 60; + lightPreDelayMinutes = pref.getInt("light_pre_delay", 15); + lightPostDelayMinutes = pref.getInt("light_post_delay", 15); + lightDuration = lightPreDelayMinutes + lightPostDelayMinutes; // pref.getInt("light_max", 30) - pref.getInt("light_start", 0); + + int sStart = globalStart + lightPreDelayMinutes; // globalStart + pref.getInt("sound_start", 15); + soundStartH = sStart / 60; + soundStartM = sStart % 60; + soundDuration = pref.getInt("sound_duration", 15); + + // --- 2. État Global et Volume --- + ToggleButton alarmEnabled = findViewById(R.id.toggleButtonAlarmEnabled); + if (alarmEnabled != null) alarmEnabled.setChecked(pref.getBoolean("enabled", true)); + + boolean soundMaster = pref.getBoolean("sound_master_enabled", true); + m_checkBoxSoundEnabled.setChecked(soundMaster); + updateSoundUI(soundMaster); + + SeekBar sb = (SeekBar) findViewById(R.id.seekBarVolume); + if (sb != null) sb.setProgress(pref.getInt("volume", 50)); + + // --- 3. Vibreur et Méthode d'arrêt --- + ToggleButton vibrateButton = (ToggleButton) findViewById(R.id.toggleButtonVibrate); + if (vibrateButton != null) vibrateButton.setChecked(pref.getBoolean("vibrate", false)); + + ToggleButton dismissButton = (ToggleButton) findViewById(R.id.toggleButtonUseDismiss); + if (dismissButton != null) dismissButton.setChecked(pref.getBoolean("dismiss", false)); + + // --- 4. Jours de la semaine --- + int[] checkBoxes = {R.id.checkBoxMondays, R.id.checkBoxTuesdays, R.id.checkBoxWednesdays, + R.id.checkBoxThursdays, R.id.checkBoxFridays, R.id.checkBoxSaturdays, R.id.checkBoxSundays}; + String[] keys = {"mondays", "tuesdays", "wednesdays", "thursdays", "fridays", "saturdays", "sundays"}; + + for (int i = 0; i < checkBoxes.length; i++) { + CheckBox cb = (CheckBox) findViewById(checkBoxes[i]); + if (cb != null) cb.setChecked(pref.getBoolean(keys[i], true)); + } + + // --- 5. Son --- + String soundStr = pref.getString("sound", null); + if (soundStr != null && !soundStr.isEmpty()) { + m_soundUri = Uri.parse(soundStr); + } else { + m_soundUri = Settings.System.DEFAULT_ALARM_ALERT_URI; + } + // Validation du son (s'assure que le fichier existe toujours) + m_soundUri = checkSound(this, m_soundUri); + updateSoundButtonText(); + // Mise à jour des textes (Heures/Durées) + updateLabels(); + } + + @Override + public void onClick(View v) { + int id = v.getId(); + + if (id == R.id.buttonSave) { + savePreferences(); + } else if (id == R.id.buttonDiscard) { + finish(); + } else if (id == R.id.buttonSound) { + pickSound(); + } else if (id == R.id.txtLightStart) { + showTimePicker(lightStartH, lightStartM, (view, h, m) -> { + lightStartH = h; lightStartM = m; + ((TextView)v).setText(String.format("%02d:%02d", h, m)); + updateLabels(); + }); + } /* else if (id == R.id.txtLightPreDelay) { + showDurationPicker(lightPreDelayMinutes, (val) -> { + lightPreDelayMinutes = val; + ((TextView)v).setText(val + " min"); + updateLabels(); + }); + } else if (id == R.id.txtLightPostDelay) { + showDurationPicker(lightPostDelayMinutes, (val) -> { + lightPostDelayMinutes = val; + ((TextView)v).setText(val + " min"); + updateLabels(); + }); + } */ + else if (id == R.id.txtSoundStart) { + showTimePicker(soundStartH, soundStartM, (view, h, m) -> { + soundStartH = h; soundStartM = m; + updateLabels(); + }); + } else if (id == R.id.txtSoundDuration) { + showDurationPicker(soundDuration, (val) -> { + soundDuration = val; + updateLabels(); + }); + } + } + + private void showTimePicker(int h, int m, OnTimeSetListener listener) { + new TimePickerDialog(this, listener, h, m, true).show(); + } + + private void showDurationPicker(int currentVal, OnDurationSetListener listener) { + final NumberPicker np = new NumberPicker(this); + np.setMinValue(1); + np.setMaxValue(120); + np.setValue(currentVal); + new AlertDialog.Builder(this) + .setTitle("Durée (minutes)") + .setView(np) + .setPositiveButton("OK", (d, w) -> listener.onSet(np.getValue())) + .show(); + } + + private void savePreferences() { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + android.app.AlarmManager alarmManager = (android.app.AlarmManager) getSystemService(Context.ALARM_SERVICE); + if (alarmManager != null && !alarmManager.canScheduleExactAlarms()) { + // Si on n'a pas le droit, on envoie l'utilisateur dans les réglages + Intent intent = new Intent(android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM); + startActivity(intent); + return; // On ne sauvegarde pas tant qu'on n'a pas la permission + } + } + SharedPreferences pref = getSharedPreferences("main", MODE_PRIVATE); + SharedPreferences.Editor editor = pref.edit(); + // 1. Calcul de l'heure de début absolue (le plus tôt entre lumière et son) + int lightStartTotal = lightStartH * 60 + lightStartM; + int soundStartTotal = soundStartH * 60 + soundStartM; + int globalStartTotal = Math.min(lightStartTotal, soundStartTotal); + + // 2. Sauvegarde de l'heure de réveil système + editor.putInt("dawn_start_hour", globalStartTotal / 60); + editor.putInt("dawn_start_minute", globalStartTotal % 60); + + // 3. Sauvegarde des décalages (en minutes par rapport au début global) + editor.putInt("light_pre_delay", lightPreDelayMinutes); + editor.putInt("light_post_delay", lightPostDelayMinutes); + + editor.putInt("sound_duration", soundDuration); + editor.putBoolean("sound_master_enabled", m_checkBoxSoundEnabled.isChecked()); + + // 4. Jours de la semaine + int[] checkBoxes = {R.id.checkBoxMondays, R.id.checkBoxTuesdays, R.id.checkBoxWednesdays, + R.id.checkBoxThursdays, R.id.checkBoxFridays, R.id.checkBoxSaturdays, R.id.checkBoxSundays}; + String[] keys = {"mondays", "tuesdays", "wednesdays", "thursdays", "fridays", "saturdays", "sundays"}; + + for (int i = 0; i < checkBoxes.length; i++) { + CheckBox cb = (CheckBox) findViewById(checkBoxes[i]); + if (cb != null) editor.putBoolean(keys[i], cb.isChecked()); + } + + // Volume + SeekBar sb = (SeekBar) findViewById(R.id.seekBarVolume); + if (sb != null) editor.putInt("volume", sb.getProgress()); + + // Vibreur et Dismiss + ToggleButton vibrateButton = (ToggleButton) findViewById(R.id.toggleButtonVibrate); + if (vibrateButton != null) editor.putBoolean("vibrate", vibrateButton.isChecked()); + + ToggleButton dismissButton = (ToggleButton) findViewById(R.id.toggleButtonUseDismiss); + if (dismissButton != null) editor.putBoolean("dismiss", dismissButton.isChecked()); + + // 4. Autres réglages (Volume, État alarme, etc.) + ToggleButton alarmEnabled = findViewById(R.id.toggleButtonAlarmEnabled); + if (alarmEnabled != null) { + editor.putBoolean("enabled", alarmEnabled.isChecked()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!Settings.canDrawOverlays(this)) { + Toast.makeText(this, "Autorisez 'Afficher sur d'autres applis' pour que le réveil puisse s'ouvrir", Toast.LENGTH_LONG).show(); + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + getPackageName())); + startActivity(intent); + } + } + } + + Log.d("FakeDawn", "Alarme activée : " + alarmEnabled.isChecked()); + if (m_soundUri != null) { + editor.putString("sound", m_soundUri.toString()); + } else { + editor.putString("sound", ""); + } + editor.commit(); + // editor.apply(); + + // 5. Relancer le service pour mettre à jour l'alarme système + Intent updateAlarm = new Intent(this, Alarm.class); + // updateAlarm.putExtra("force_update", true); // On peut ajouter un flag pour forcer + updateAlarm.putExtra(Alarm.EXTRA_SHOW_TOAST, true); + + // 2. Sur Android 12+, il est parfois plus fiable de stopper + // le service avant de le relancer pour forcer un rafraîchissement complet + stopService(updateAlarm); + try { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + startForegroundService(updateAlarm); + } else { + startService(updateAlarm); + } + } catch (Exception e) { + Log.e("FakeDawn", "Erreur lors du lancement du service Alarm" + e.getMessage()); + } + Log.d("FakeDawn", "Preferences sauvegardées" + globalStartTotal / 60 + "h" + globalStartTotal % 60); + setResult(RESULT_OK); + finish(); + } + + private void pickSound() { + Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); + // 1. Permet d'afficher l'option "Silencieux" ou "Aucun" en haut de la liste + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); + + // 2. Titre de la fenêtre + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Choisir une sonnerie"); + + // 3. Pré-sélectionner la sonnerie actuelle (si elle existe) + if (m_soundUri != null) { + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, m_soundUri); + } + startActivityForResult(intent, REQUEST_PICK_SOUND); + } + + private void changeSound(Uri sound) { + m_soundUri = sound; + } + + public static Uri checkSound(Context context, Uri sound) { + //TODO: move in other class? + Uri[] sounds = { + sound, + Settings.System.DEFAULT_ALARM_ALERT_URI, + Settings.System.DEFAULT_RINGTONE_URI, + Settings.System.DEFAULT_NOTIFICATION_URI, + }; + for (Uri s: sounds) + { + try { + InputStream tmp = context.getContentResolver().openInputStream(s); + tmp.close(); + //TODO: toast if not first. + return s; + } catch (FileNotFoundException e) { + continue; + } catch (IOException e) { + continue; + } + } + //TODO: error toast + return null; + } + // --- MÉTHODES D'INTERFACE (Indispensables pour la compilation) --- + + @Override public void onProgressChanged(SeekBar sb, int p, boolean b) {} + @Override public void onStartTrackingTouch(SeekBar sb) {} + @Override public void onStopTrackingTouch(SeekBar sb) {} + @Override public void colorChanged(int c) {} + @Override public void onTimeSet(TimePicker v, int h, int m) {} + @Override public void onTimesChanged(TimeSlider s) {} //}, int l, int r) {} + + // --- CLASSES INTERNES --- + + private class HelpListener implements DialogInterface.OnClickListener { + @Override + public void onClick(DialogInterface dialog, int which) { + // Logique du bouton Aide + } + } + + interface OnDurationSetListener { + void onSet(int value); + } + + private class VolumePreview { + // Logique de preview sonore + } +} \ No newline at end of file diff --git a/src/org/balau/fakedawn/StartAtBoot.java b/app/src/main/java/org/balau/fakedawn/StartAtBoot.java similarity index 100% rename from src/org/balau/fakedawn/StartAtBoot.java rename to app/src/main/java/org/balau/fakedawn/StartAtBoot.java diff --git a/src/org/balau/fakedawn/TimeSlider.java b/app/src/main/java/org/balau/fakedawn/TimeSlider.java similarity index 100% rename from src/org/balau/fakedawn/TimeSlider.java rename to app/src/main/java/org/balau/fakedawn/TimeSlider.java diff --git a/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png similarity index 100% rename from res/drawable-hdpi/ic_launcher.png rename to app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/res/drawable-ldpi/ic_launcher.png b/app/src/main/res/drawable-ldpi/ic_launcher.png similarity index 100% rename from res/drawable-ldpi/ic_launcher.png rename to app/src/main/res/drawable-ldpi/ic_launcher.png diff --git a/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png similarity index 100% rename from res/drawable-mdpi/ic_launcher.png rename to app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from res/drawable-xhdpi/ic_launcher.png rename to app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/res/drawable/sound.png b/app/src/main/res/drawable/sound.png similarity index 100% rename from res/drawable/sound.png rename to app/src/main/res/drawable/sound.png diff --git a/res/drawable/sunrise.png b/app/src/main/res/drawable/sunrise.png similarity index 100% rename from res/drawable/sunrise.png rename to app/src/main/res/drawable/sunrise.png diff --git a/res/layout/dawn.xml b/app/src/main/res/layout/dawn.xml similarity index 100% rename from res/layout/dawn.xml rename to app/src/main/res/layout/dawn.xml diff --git a/res/layout/license.xml b/app/src/main/res/layout/license.xml similarity index 100% rename from res/layout/license.xml rename to app/src/main/res/layout/license.xml diff --git a/app/src/main/res/layout/predawn.xml b/app/src/main/res/layout/predawn.xml new file mode 100644 index 0000000..eeb4571 --- /dev/null +++ b/app/src/main/res/layout/predawn.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/preferences.xml b/app/src/main/res/layout/preferences.xml new file mode 100644 index 0000000..c6d2e82 --- /dev/null +++ b/app/src/main/res/layout/preferences.xml @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +