אפליקציית Android (Java) המיועדת למכשיר "מאזין" שנשאר בחדר הילדים: היא עוקבת אחרי עוצמת הקול דרך המיקרופון, ובזיהוי רעש/בכי חזק מחייגת אוטומטית למספר טלפון שהוגדר מראש. המכשיר המקבל (של ההורה/המטפל/ת) אינו זקוק לאפליקציה כלשהי — הוא רק מקבל שיחה טלפונית רגילה.
בנוסף, ניתן להפעיל מענה אוטומטי לשיחות נכנסות ממספרים מאושרים - מנגנון עצמאי לגמרי מהאזנת הבכי, שפועל גם כשההאזנה כבויה (ראו סעיף ייעודי למטה).
הפרויקט בנוי כך שירוץ גם על מכשירים ישנים וללא מסך מגע (minSdk 19 —
Android 4.4 KitKat, כולל תמיכה מלאה בניווט מקשים/D-pad), ופועל לגמרי
אופליין: אין שרת חיצוני, אין שירותי AI, כל הזיהוי מתבצע מקומית על המכשיר.
- סקירה כללית ועקרונות תכנון
- מבנה הפרויקט
- הרשאות (Permissions) — ומדוע כל אחת דרושה
- שני המסכים: בית + הגדרות
- מענה אוטומטי עצמאי לשיחות נכנסות
- ערכת נושא: מערכת / בהיר / כהה
- הרשאות בזמן ריצה ומדוע אין יותר נפילה למסך חיוג ידני
- תיקון רמקול ומענה אוטומטי במכשירים חדשים
- זיהוי בכי חכם יותר: כיול רעש רקע ומחזוריות
- אמינות ותפעול: מסך מצב מערכת, הודעת תקינות, חיוג חוזר
- ממשק משתמש: מד עוצמה, היסטוריית אירועים, בדיקת מיקרופון
- ווידג'ט למסך הבית ואריח הגדרות מהירות
- מפתחות SharedPreferences (טבלת עיון)
- נוסחת הרגישות לזיהוי רעש
- קבצי בנייה (Gradle) ועדכון גרסה אוטומטי ב-CI
- איך לבנות את זה ל-APK
- מגבלות ידועות
- יומן שינויים (Changelog)
| עיקרון | הסבר |
|---|---|
| אופליין לחלוטין | הזיהוי מתבצע רק עם MediaRecorder.getMaxAmplitude() — מדידת עוצמת קול מקומית. אין קריאות רשת, אין AI חיצוני, אין שמירת הקלטות בפועל. |
| תמיכה במכשירים ישנים | minSdk 19 (Android 4.4 KitKat) נשאר ללא שינוי, ללא תלות ב-AndroidX/AppCompat — משתמש ב-Activity וב-theme הגולמי של הפלטפורמה. targetSdk הועלה ל-34 (ראו הסבר בהמשך) - כל קריאת API חדשה יותר שנוספה מוגנת בבדיקת Build.VERSION.SDK_INT כדי לא לפגוע במכשיר הישן. |
| תמיכה במכשיר ללא מסך מגע | ניווט מבוסס D-pad/מקשים פיזיים, RadioGroup במקום סליידר, nextFocusDown מוגדר בין כל השדות, וטיפול מיוחד במקש Back (onKeyDown) במסך ההגדרות. |
| פרטיות | קובץ ההקלטה הזמני (monitor.3gp) משמש רק למדידת עוצמה, נמחק ונוצר מחדש כל 4 דקות, ואינו נשלח לשום מקום. |
| חוסן (Resilience) | התאוששות אוטומטית אם המיקרופון נתפס (למשל בזמן שיחה), חידוש האזנה מיידי בסיום כל שיחה, ו-START_REDELIVER_INTENT כדי שהשירות יתחדש עם אותם נתונים אם המערכת הרגה אותו. |
┌─────────────────┐ כפתור מרכזי ┌──────────────────────┐
│ MainActivity │ ───────────────▶│ CryDetectionService │
│ (כפתור התחל/עצור │ התחל/עצור │ (Foreground Service) │
│ + כפתור הגדרות) │◀───────────────│ │
└────────┬─────────┘ └──────────────────────┘
│ כפתור "הגדרות" │ כל 700ms
▼ ▼
┌─────────────────┐ ┌────────────────────────┐
│ SettingsActivity │ │ MediaRecorder │
│ (מספרים, מענה │ │ getMaxAmplitude() │
│ אוטומטי, שמע, │ └────────────────────────┘
│ רגישות, ערכת │ │
│ נושא) │ amplitude > threshold?
└─────────────────┘ │ כן (2 דגימות רצופות)
▼
ACTION_CALL למספר שהוגדר
(עצמאי לגמרי מהתרשים לעיל)
┌──────────────────────┐ שידור מערכת ┌──────────────────────┐
│ שיחה נכנסת מצלצלת │────────────────▶│ AutoAnswerReceiver │
│ │ PHONE_STATE │ (receiver ב-manifest,│
└──────────────────────┘ RINGING │ פועל גם אם ההאזנה │
│ לבכי כבויה לגמרי) │
└──────────────────────┘
│ מספר מאושר?
▼
TelecomManager.acceptRingingCall()
.
├── app/
│ ├── build.gradle # compileSdk/targetSdk 34, minSdk 19, versionCode/Name
│ └── src/main/
│ ├── AndroidManifest.xml # הרשאות, Activities, השירות, ה-receivers, הווידג'ט, האריח
│ ├── java/com/babysitter/app/
│ │ ├── MainActivity.java # מסך הבית: כפתור התחל/עצור, מענה אוטומטי מהיר, הגדרות, מד עוצמה
│ │ ├── SettingsActivity.java # מסך ההגדרות: מספרים, מענה אוטומטי, שמע, רגישות, ערכת נושא, הודעת תקינות, בדיקת מיקרופון
│ │ ├── StatusActivity.java # מסך "מצב מערכת": בריאות השירות, שיחות היום, סוללה, יומן אירועים
│ │ ├── CryDetectionService.java # שירות ההאזנה לבכי: כיול, מחזוריות, כמעט-בכי, שיחות יוצאות
│ │ ├── AutoAnswerReceiver.java # מענה אוטומטי עצמאי לשיחות נכנסות
│ │ ├── HeartbeatReceiver.java # שולח את הודעת התקינות היומית (SMS)
│ │ ├── HeartbeatScheduler.java # מתזמן/מבטל את אזעקת ה-AlarmManager היומית
│ │ ├── BootReceiver.java # מתזמן מחדש את הודעת התקינות אחרי reboot
│ │ ├── ListeningWidgetProvider.java # ווידג'ט למסך הבית - כפתור התחל/עצור
│ │ ├── ListeningTileService.java # אריח בהגדרות המהירות (Quick Settings, Android 7+)
│ │ ├── WidgetUpdateHelper.java # מסנכרן את הווידג'ט/האריח עם שאר האפליקציה
│ │ ├── EventLog.java # יומן אירועים (חיוגים/מענה אוטומטי/כמעט-בכי/הודעת תקינות)
│ │ ├── AppPrefs.java # מפתחות SharedPreferences + עזרי השוואת מספרים (משותף)
│ │ ├── ThemeUtil.java # פתרון ערכת הנושא (מערכת/בהיר/כהה)
│ │ ├── PermissionUtil.java # עזרי בקשת הרשאות בזמן ריצה
│ │ ├── AudioRoutingUtil.java # ניתוב רמקול (כולל תיקון לאנדרואיד 12+)
│ │ ├── BatteryOptimizationUtil.java # בדיקה/בקשת פטור מניהול סוללה אגרסיבי
│ │ └── MicTestUtil.java # בדיקת מיקרופון עצמאית (מנגן צליל ובודק שנתפס)
│ └── res/
│ ├── layout/
│ │ ├── activity_main.xml # מסך הבית (כפתור מרכזי, מענה אוטומטי, הגדרות, מד עוצמה)
│ │ ├── activity_settings.xml # מסך ההגדרות
│ │ ├── activity_status.xml # מסך "מצב מערכת"
│ │ └── widget_listening.xml # פריסת הווידג'ט למסך הבית
│ ├── xml/
│ │ └── widget_listening_info.xml # הגדרת הווידג'ט (גודל, resize וכו')
│ ├── values/
│ │ ├── strings.xml # כל מחרוזות הטקסט (עברית)
│ │ ├── colors.xml # פלטת הצבעים (כהה + בהיר)
│ │ ├── attrs.xml # "משתני צבע" לערכת הנושא (?attr/...)
│ │ └── styles.xml # AppTheme.Dark / AppTheme.Light
│ ├── drawable/ # כפתורים עם חיווי פוקוס ירוק, כפתור מרכזי עגול, אייקון אריח
│ └── mipmap-*/ # אייקון האפליקציה בכל צפיפויות המסך
├── .github/workflows/build.yml # GitHub Actions - כולל עדכון versionCode אוטומטי
└── .gitlab-ci.yml # GitLab CI - כולל עדכון versionCode אוטומטי
מוגדרות ב-AndroidManifest.xml:
| הרשאה | לשם מה משמשת בפועל |
|---|---|
RECORD_AUDIO |
קריאת עוצמת הקול מהמיקרופון (MediaRecorder) לצורך זיהוי רעש/בכי, כיול רעש רקע, ובדיקת מיקרופון. |
CALL_PHONE |
ביצוע שיחה יוצאת אוטומטית (Intent.ACTION_CALL) כשמזוהה רעש חזק. |
WAKE_LOCK |
שמירת ה-CPU ער כדי שההאזנה לא תיעצר כשהמסך כבוי (PowerManager.PARTIAL_WAKE_LOCK). |
FOREGROUND_SERVICE |
הרצת CryDetectionService כ-Foreground Service (עם התראה קבועה), כדי שהמערכת לא תהרוג אותו ברקע. |
FOREGROUND_SERVICE_MICROPHONE |
מאנדרואיד 14 ואילך, שירות חזית שמשתמש במיקרופון חייב גם הרשאה ספציפית זו, בנוסף ל-FOREGROUND_SERVICE הכללית (תואם ל-android:foregroundServiceType="microphone" שהוגדר על השירות). |
READ_PHONE_STATE |
מעקב אחרי מצב השיחה — מזהה מצלצל/מחובר/הסתיים, כדי לזהות "אין מענה", לחזור להאזין מיד בסיום שיחה, ולתמוך במענה האוטומטי העצמאי. |
READ_CALL_LOG |
נדרש מ-Android 10 ואילך כדי לקבל את מספר השיחה הנכנסת בפועל (יחד עם READ_PHONE_STATE), לצורך התאמה מול רשימת "מספרים למענה אוטומטי". |
ANSWER_PHONE_CALLS |
מאפשר מענה אוטומטי לשיחה נכנסת דרך TelecomManager.acceptRingingCall() (Android 8+). |
MODIFY_AUDIO_SETTINGS |
שליטה בעוצמת הרמקול/צלצול ובמצב הרמקול (Speaker) בזמן שיחה. |
POST_NOTIFICATIONS |
מאנדרואיד 13 ואילך, נדרש כדי להציג את התראת "שירות פעיל" ואת התראת האזהרה על חוסר הרשאה. |
SEND_SMS |
שליחת הודעת התקינות היומית (heartbeat) - מבוקשת בזמן ריצה רק אם מסמנים את הצ'קבוקס הרלוונטי בהגדרות. |
RECEIVE_BOOT_COMPLETED |
מתזמן מחדש את אזעקת הודעת התקינות אחרי הפעלה מחדש של המכשיר (אזעקות לא שורדות reboot) - רלוונטי רק אם הודעת התקינות מופעלת. |
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS |
מאפשרת לבקש מהמשתמש פטור מניהול סוללה אגרסיבי, דרך מסך "מצב מערכת". |
הצהרות <uses-feature> נוספות (ללא שינוי):
| Feature | required | הסבר |
|---|---|---|
android.hardware.touchscreen |
false |
חובה להצהיר על כך במפורש, אחרת אנדרואיד מניח כברירת מחדל שהאפליקציה דורשת מסך מגע ועלול לחסום התקנה במכשיר ללא מגע. |
android.hardware.microphone |
true |
האפליקציה לא פונקציונלית בלי מיקרופון. |
android.hardware.telephony |
true |
האפליקציה לא פונקציונלית בלי יכולת חיוג. |
בעבר היה מסך אחד עמוס (כל שדות הקלט + כפתור התחל/עצור). כעת:
מסך הבית (MainActivity / activity_main.xml)
- כפתור עגול במרכז המסך להפעלה/כיבוי ההאזנה - צבעו עצמו משתנה
(טורקיז כשכבוי, אדום כשפעיל) כחיווי ברור גם בלי לקרוא את הטקסט. גודלו
הוקטן במכוון (150dp) כדי שלא יחפוף את הכפתורים התחתונים במסכים קטנים -
ראו הערת המבנה ב-
activity_main.xml. - כפתור "מענה אוטומטי" מהיר, מעל כפתור ההגדרות - מפעיל/מכבה רק את
ה-flag (
KEY_AUTO_ANSWER_ENABLED) בלי להיכנס למסך ההגדרות. מספרי הטלפון עצמם למענה אוטומטי עדיין מוגדרים אך ורק במסך ההגדרות - כפתור זה רק מדליק/מכבה את התכונה. גם כאן, הפעלה דורשת הרשאות מראש (כמו למטה) - ואם אין עדיין אף מספר מוגדר, האפליקציה מפנה למסך ההגדרות במקום להדליק משהו שלא יעבוד. - כפתור "הגדרות" צמוד לתחתית המסך, פותח את
SettingsActivity. - לחיצה על הכפתור המרכזי כשהוא כבוי מבקשת הרשאות מראש (ראו סעיף
ייעודי למטה) ורק אם כולן אושרו, מפעילה את
CryDetectionService.
מסך ההגדרות (SettingsActivity / activity_settings.xml)
- כל שדות הקלט שהיו במסך הבית הישן: מספר טלפון ראשי/משני, בורר אנשי קשר, מענה אוטומטי (מספרים + רמקול), הגדרות שמע לשיחות, רגישות לזיהוי בכי.
- חדש: בחירת ערכת נושא (מערכת/בהיר/כהה).
- כפתור "שמור הגדרות" בתחתית - וגם שמירה אוטומטית ב-
onPause(חזרה/מעבר אפליקציה), כדי שהגדרות לא "יאבדו" בטעות. - מקש Back בזמן עמידה בשדה מספר ממשיך למחוק תו אחרון (כמו קודם).
בעבר, מענה אוטומטי לשיחה נכנסת היה חלק מ-CryDetectionService - כלומר
אפשר היה להשתמש בו רק כשההאזנה לבכי הייתה פעילה. עכשיו זה שני מנגנונים
נפרדים לגמרי:
- האזנה לבכי (
CryDetectionService) - מופעלת/מכובה ידנית מכפתור ההתחלה/עצירה במסך הבית. - מענה אוטומטי לשיחות נכנסות (
AutoAnswerReceiver) -BroadcastReceiverרשום ב-AndroidManifest.xml(לא נוצר דינמית בתוך שירות), שמאזין לשידור המערכתandroid.intent.action.PHONE_STATE. שידור מערכת "מוגן" (protected broadcast) כזה ממשיך להגיע ל-receiver סטטי גם אם האפליקציה לא רצה כרגע ברקע - ולכן המענה האוטומטי פועל גם אם ההאזנה לבכי כבויה לגמרי, ומוגדר/מופעל בנפרד לגמרי דרך צ'קבוקס במסך ההגדרות.
ה-receiver מגיב אך ורק למצב RINGING - מצב שקיים אך ורק בשיחות נכנסות,
לעולם לא בשיחות יוצאות. כך מובטח שהמענה האוטומטי לעולם לא "נוגע" בשיחה
יוצאת שהאפליקציה עצמה יזמה (זו נשארת באחריות CryDetectionService בלבד).
כשמסמנים את הצ'קבוקס "אפשר מענה אוטומטי" במסך ההגדרות, האפליקציה מבקשת
מיד את ההרשאות הדרושות (READ_PHONE_STATE, READ_CALL_LOG,
ANSWER_PHONE_CALLS) - בלי קשר לכך שההאזנה בכלל הופעלה פעם.
במסך ההגדרות ניתן לבחור בין שלוש אפשרויות (מערכת היא ברירת המחדל):
- לפי המערכת - עוקב אחרי מצב Dark Mode של המכשיר (מאנדרואיד 10 ומעלה; במכשירים ישנים יותר, כולל מכשיר המקשים היעד, אין מושג כזה ברמת המערכת, ולכן נשארת ערכת הנושא הכהה המקורית כברירת מחדל בפועל).
- בהיר
- כהה (זו הייתה ערכת הנושא היחידה שהייתה קודם)
מימוש: ללא AndroidX/AppCompat (AppCompatDelegate לא זמין) - במקום זאת,
כל צבע ברקעים/טקסטים בפריסות מוגדר כ-?attr/colorXxx (ראו attrs.xml),
עם שני style-ים קונקרטיים - AppTheme.Dark ו-AppTheme.Light
(ב-styles.xml). ThemeUtil.applyTheme(Activity) נקרא בתחילת onCreate
של כל Activity, לפני setContentView, ומבצע setTheme(...) בהתאם
להעדפה השמורה - שיטה גולמית שנתמכת מ-API 1, ולכן בטוחה גם למכשיר היעד
הישן (מינימום API 19).
בעבר targetSdk היה זהה ל-minSdk (19) - זה בכוונה נבחר כי אז כל ההרשאות
המוצהרות ב-manifest מאושרות אוטומטית בהתקנה, בלי דיאלוג הרשאות כלל, בכל
גרסת אנדרואיד. אבל זה גם הסיבה לדיאלוג "האפליקציה נבנתה לגרסה ישנה של
אנדרואיד ועלולה שלא לעבוד כראוי" שמופיע במכשירים חדשים - אנדרואיד מציג
אותו אוטומטית (מגרסה 6 ואילך) לכל אפליקציה שה-targetSdk שלה נמוך מ-23,
בלי קשר לגרסת אנדרואיד בפועל של המכשיר.
הפתרון: targetSdk הועלה ל-34 (compileSdk בהתאם). זה מבטל את הדיאלוג,
אבל אומר שהמערכת עוברת למודל ההרשאות ה"רגיל" (runtime) על מכשירי Android
6+ - צריך לבקש הרשאות מפורשות בזמן ריצה, לא לסמוך על אישור אוטומטי
בהתקנה. minSdk נשאר 19 ללא שינוי - מכשירים מתחת ל-API 23 ממשיכים
לקבל את כל ההרשאות בהתקנה כרגיל, ואף קריאת API שנוספה בקוד מוגנת בבדיקת
Build.VERSION.SDK_INT כך שלא תקרוס עליהם.
בעבר, אם הרשאת CALL_PHONE לא הייתה זמינה, האפליקציה נפלה חזרה למסך חיוג
רגיל (ACTION_DIAL) שדורש לחיצה ידנית - זה חסר תועלת כשמדובר בילדים
קטנים שלא יכולים לבצע את הלחיצה הזו בעצמם.
הפתרון: כל ההרשאות הדרושות נבדקות ונדרשות מראש, ברגע שמבוגר לוחץ
על "התחל האזנה" במסך הבית (או על צ'יקבוקס "מענה אוטומטי" במסך ההגדרות) -
בדיוק כשעוד יש מבוגר שיכול לאשר דיאלוג. אם משהו נדחה - ההאזנה/המענה
האוטומטי לא מופעלים בכלל, כדי שלעולם לא ניגיע למצב שבו הילד תלוי במסך
חיוג ידני. אם בכל זאת ההרשאה נשללת מאוחר יותר (למשל ידנית דרך הגדרות
המכשיר, אחרי שההאזנה כבר רצה) - האפליקציה עוצרת את עצמה ומציגה התראה
בולטת וקבועה למבוגר, במקום להיכשל בשקט. ראו PermissionUtil.java,
MainActivity.requestPermissionsThenStart(), ו-הערה מפורטת מעל
CryDetectionService.placeCallTo().
שני דיווחים נפרדים ממשתמשים באנדרואיד גבוה (עבד היטב באנדרואיד 4.4):
רמקול לא עובר אוטומטית - AudioManager.setSpeakerphoneOn() (השיטה
הקלאסית) הופכת פחות אמינה מאנדרואיד 9 ואילך, ומאנדרואיד 12 (API 31) גוגל
הציגו API ייעודי במקומה - AudioManager.setCommunicationDevice(), שמנתב
במפורש להתקן אודיו ספציפי (במקרה הזה: הרמקול המובנה). באנדרואיד 12+
משתמשים ב-setCommunicationDevice (עם בקשת audio focus מפורשת לפניו),
ובמכשירים ישנים יותר ממשיכים עם setSpeakerphoneOn הקלאסי.
עדכון - אנדרואיד 11 עדיין לא עבד: גרסה 11 (API 30) נמצאת "באזור
אפור" - חדשה מספיק כדי שהמערכת/אפליקציית הטלפון "דורסות" את ניתוב הרמקול
בחזרה כעבור רגע אחרי שהשיחה מתחברת, אבל ישנה מספיק כדי שאין לה עדיין את
setCommunicationDevice (רק מ-API 31). הפתרון: AudioRoutingUtil .applySpeakerReinforced() לא מסתפק בקריאה בודדת - מיישם את הרמקול מיד,
ואז שוב 4 פעמים נוספות בהפרשי זמן גדלים (עד כ-3.5 שניות מתחילת השיחה),
כל עוד השיחה עדיין פעילה בפועל - טכניקה מוכרת שאפליקציות דומות (מקליטי
שיחות, מגבירי רמקול) משתמשות בה בדיוק בגלל אותה מגבלת פלטפורמה.
מענה אוטומטי לא עובד - מטופל כעת ע"י AutoAnswerReceiver העצמאי
(ראו סעיף למעלה), עם בקשת הרשאות מפורשת ומבוקרת מראש (ולא הסתמכות על
מודל אישור אוטומטי בהתקנה שהתנהגותו פחות עקבית בין יצרנים/גרסאות של
מכשירים חדשים) - ראו הסבר בסעיף "הרשאות בזמן ריצה" למעלה.
"ההאזנה מפסיקה כשהמסך כבה" - השירות כבר מחזיק PARTIAL_WAKE_LOCK
שאמור לשמור את המעבד ער גם עם מסך כבוי, אבל הרבה מכשירים (בעיקר
Xiaomi/MIUI, Samsung, Huawei) מפעילים שכבת "ניהול סוללה" אגרסיבית משלהם
שעדיין עלולה להשהות שירותי רקע גם עם WakeLock תקין, אלא אם האפליקציה
ברשימת "ללא הגבלת סוללה". התיקון: ברגע שלוחצים "התחל האזנה", אם
האפליקציה עדיין לא מוגנת - MainActivity מציגה מיד את דיאלוג הפטור
הרשמי של המערכת (BatteryOptimizationUtil), במקום להשאיר את זה נסתר רק
במסך "מצב מערכת". דחיית הדיאלוג לא חוסמת את ההאזנה - זו רק הפחתת סיכון.
"האפליקציה 'חושבת' שהתקשרה במצב טיסה" - Intent.ACTION_CALL "מצליח"
מבחינת הקוד שלנו (startActivity לא זורק שגיאה) גם כשהמכשיר במצב טיסה -
האינטנט פשוט מגיע לאפליקציית הטלפון, שהיא זו שבסוף לא תצליח לחייג. לפני
התיקון, זה נרשם ביומן האירועים כ"חיוג בוצע" בהצלחה. עכשיו, CryDetectionService .placeCallTo() בודק מצב טיסה (CellularServiceUtil.isAirplaneModeOn)
לפני הניסיון, ואם הוא דלוק - לא מנסים לחייג בכלל, רושמים את זה נכון
כ"נכשל", ומציגים התראה ייעודית ("מצב טיסה פעיל"). ההאזנה עצמה ממשיכה
(מצב טיסה יכול להיות זמני) - רק הניסיון הספציפי הזה מדולג.
בדיקת קליטה בהתחלת האזנה - warnIfNoCellularService() ב-MainActivity
בודקת (מיד כשלוחצים "התחל האזנה"): מצב טיסה, האם יש כרטיס SIM תקין,
ובמכשירי אנדרואיד 8+ גם את מצב השירות הסלולרי בפועל
(TelephonyManager.getServiceState(), זמין כ-API סינכרוני רק מ-API 26
ואילך). זו אזהרה חד-פעמית ולא חוסמת - המצב יכול להשתנות תוך כדי הלילה -
ולא מבטלת את הבדיקה השנייה, הקריטית יותר, שנעשית שוב ממש לפני כל חיוג
בפועל (ראו סעיף הקודם).
שני שיפורים לזיהוי הבכי עצמו, עדיין לגמרי אופליין ובלי AI - חישוב
סטטיסטי פשוט על MediaRecorder.getMaxAmplitude() בלבד:
כיול אוטומטי לרעש רקע - בכל "התחל האזנה" (לא בכל הפעלה-מחדש פנימית
של ההקלטה - רק פעם אחת למחזור), השירות מודד כ-3 שניות של הרעש שקיים
בפועל בחדר לפני שהוא מתחיל להתייחס לכלום כמועמד לבכי, וקובע לפי זה
"קו בסיס" (baseline) ספציפי לאותו חדר. הסף בפועל לזיהוי בכי, וסף
הביניים החזותי במד העוצמה (ראו בהמשך), מחושבים מעל קו הבסיס הזה
(baseline + מרווח לפי רגישות) - כך שחדר עם מזגן/מאוורר רועש לא "יוצף"
בחיוגי שווא, וחדר שקט לא יפספס בכי חלש יחסית. קו הבסיס נשמר
(KEY_CALIBRATED_BASELINE) ומשמש כברירת מחדל גם לפני שהכיול של המחזור
הנוכחי מסתיים.
זיהוי מחזוריות (בכי אמיתי "פועם", לא רעש קבוע) - בכי אמיתי נוטה
להתחלף בין רגע חזק לרגע נשימה/שקט יחסי, בעוד רעש רקע קבוע (מזגן, מאוורר,
טלוויזיה ברקע) נשאר בערך באותה עוצמה לאורך זמן. השירות מחשב "מקדם
השונות" (סטיית תקן חלקי ממוצע) של כמה הדגימות האחרונות, ודורש גם רעש
מתמשך (כמה דגימות רצופות מעל הסף) וגם מקדם שונות מספיק גבוה לפני
שמחייגים בפועל - ראו looksLikePulsatingCry() ב-CryDetectionService.java.
מסך "מצב מערכת" (StatusActivity, נגיש מכפתור בכותרת מסך ההגדרות) -
מציג האם ההאזנה פעילה בפועל (לא רק לפי דגל שמור, אלא לפי זמן הדגימה
התקינה האחרונה בפועל), מתי הייתה הדגימה האחרונה, כמה שיחות בוצעו היום,
האם האפליקציה מוגנת מניהול סוללה אגרסיבי (עם כפתור לתיקון), ורשימת
האירועים האחרונים - שימושי מאוד לאבחון תקלות מרחוק ("תגיד לי מה כתוב
במסך").
הגנה מפני Battery Optimization / Doze - BatteryOptimizationUtil
בודק אם האפליקציה ברשימת "ללא הגבלת סוללה" (PowerManager .isIgnoringBatteryOptimizations) ומאפשר לבקש פטור ישירות דרך דיאלוג
המערכת הרשמי. במכשירי שיאומי (MIUI) ויצרנים דומים יש לעיתים גם שכבת
ניהול חשמל/"אתחול אוטומטי" נפרדת בהגדרות היצרן, שאין לה API ציבורי
לבדיקה/בקשה תכנותית - מוצגת הנחיה טקסטואלית לבדוק זאת ידנית.
חיוג חוזר גם אחרי כשל טכני בביצוע השיחה - בעבר, אם Intent .ACTION_CALL עצמו נכשל מסיבה שאינה בעיית הרשאה (למשל שגיאה חד-פעמית של
המערכת) - האפליקציה פשוט ויתרה. עכשיו attemptStartCallActivity()
מנסה שוב פעם אחת אחרי השהיה קצרה (3 שניות) לפני שהיא מוותרת ומודיעה.
הודעת אימות יומית ("heartbeat") - צ'קבוקס בהגדרות ("שלח הודעת אימות
יומית להורה"), מתוזמן ע"י HeartbeatScheduler (AlarmManager) לשעה
הניתנת לבחירה (לא קבועה על 08:00 יותר - יש כפתור בחירת שעה בהגדרות).
כבוי כברירת מחדל. BootReceiver מתזמן את האזעקה מחדש אחרי הפעלה מחדש של
המכשיר (אזעקות AlarmManager לא שורדות reboot), רק אם התכונה מופעלת.
שלוש שיטות אימות אפשריות (KEY_HEARTBEAT_METHOD), נבחרות בהגדרות:
- SMS בלבד - הכי פשוט; לא מתאים למשפחות בלי חבילת SMS פעילה.
- שיחה קולית בלבד - עובדת על כל קו סלולרי. מגבלת פלטפורמה כנה שחשוב לדעת עליה: Android לא מאפשר לאפליקציה רגילה (שאינה החייגן ברירת המחדל) לתייג שיחה יוצאת בטקסט מותאם אישית, ואין דרך לסיים אותה אוטומטית אחרי כמה שניות - כלומר טכנית אי אפשר "לסמן" שיחה בודדת כ"זו רק בדיקה". הפתרון המעשי: שיחת האימות מגיעה תמיד באותה שעה קבועה שההורה עצמו בחר, בעוד ששיחה שמקורה בבכי אמיתי יכולה להגיע בכל שעה - ההבדל הוא דפוס עקבי, לא תיוג טכני. מומלץ גם לשמור את מספר המכשיר באנשי הקשר של ההורה עם שם ברור כמו "בייביסיטר - בדיקה 08:00".
- שיחה + SMS מבשר (מומלץ) - הפתרון המשולב: שולחים קודם הודעת SMS קצרה ("שיחת אימות תיכנס תוך רגע"), וכ-5 שניות אחר כך את שיחת האימות עצמה. ה-SMS מופיע כהתראה על המסך ממש כשהטלפון מתחיל לצלצל (או רגע לפניו) - נותן הקשר מיידי לפני שעונים, בלי לדרוש לזכור שעה קבועה. דורש את שתי ההרשאות (SMS + שיחה) יחד.
מד עוצמה בזמן אמת - כשההאזנה פעילה, מוצג במסך הבית פס עוצמה חי
(volumeMeterBar) שמתעדכן משידור מקומי (AppPrefs.ACTION_AMPLITUDE _UPDATE, מוגבל לחבילת האפליקציה עצמה) שהשירות שולח בכל דגימה. הצבע
משתנה: ירוק (רגוע), כתום ("כמעט בכי" - מתחת לסף החיוג), אדום (ברמת חיוג)
- עוזר למבוגר "לכייל" את הרגישות בעצמו ולראות בזמן אמת כמה רועש בחדר לעומת הסף. זו תצוגה חזותית פסיבית בלבד - במפורש אין שום התראה/רטט/notification נפרדים על מצב "כמעט בכי" (הוסר לפי בקשה מפורשת - רק המד במסך עצמו, שום בועת מידע קופצת).
היסטוריית אירועים (EventLog.java) - רשימה פשוטה (בלי תוכן/הקלטה,
רק סוג אירוע וזמן) של חיוגים, מענה אוטומטי, והודעות תקינות - מוצגת במסך
"מצב מערכת", כדי לדעת למשל אם הילד באמת בכה כמה פעמים בלילה.
בדיקת מיקרופון (MicTestUtil.java, כפתור בהגדרות) - מנגן צליל קצר
ובודק שה-MediaRecorder באמת תופס עלייה משמעותית בעוצמה בזמנו, כדי
לוודא שהמיקרופון וההרשאה תקינים לפני שסומכים על זה בלילה - בלי לבצע
שיחת בדיקה אמיתית (שהייתה עולה כסף ומטרידה את ההורה סתם). זו בדיקה
לשרשרת "מיקרופון -> מדידת עוצמה" בלבד, לא לשיחה היוצאת עצמה.
שני "משטחי כניסה מהירים" נוספים להפעלה/כיבוי ההאזנה, בלי לפתוח את האפליקציה בכלל - שימושיים בעיקר אם משתמשים באפליקציה גם על מכשיר "רגיל" ומודרני יותר, לא רק על מכשיר המקשים הייעודי:
ווידג'ט למסך הבית (ListeningWidgetProvider.java) - כפתור בודד
שאפשר לגרור למסך הבית של אנדרואיד; מציג את המצב הנוכחי (התחל/עצור,
עם אותו צבע טורקיז/אדום כמו הכפתור הראשי באפליקציה) ומגיב ללחיצה ישירה.
אריח בהגדרות המהירות (ListeningTileService.java, Quick Settings
Tile) - מופיע במגירת ההגדרות המהירות (מושכים מלמעלה פעמיים), לצורך אותה
הפעלה/כיבוי מהירים. זמין רק מ-Android 7 (API 24) ואילך - זו מגבלת
פלטפורמה (Quick Settings כמושג לא קיים לפני זה), לא בחירה שלנו; במכשיר
היעד המקורי (מקשים, Android 4.4) הרכיב הזה לעולם לא נטען.
מגבלה חשובה ומשותפת לשניהם: לא ניתן להציג דיאלוג בקשת הרשאה (או כל
דיאלוג אחר) מתוך widget/receiver או מתוך Quick Settings Tile - ל-Android
אין API לזה מחוץ ל-Activity פעיל. לכן: אם כל ההרשאות הדרושות כבר אושרו
וגם מוגדר מספר טלפון - לחיצה על הווידג'ט/האריח מפעילה/מכבה את ההאזנה
מיד, בלי לפתוח שום מסך. אם משהו חסר (הרשאה או מספר) - הלחיצה פותחת את
MainActivity (עם קיפול אוטומטי של מגירת ההגדרות המהירות באריח) עם דגל
(EXTRA_AUTO_START) שגורם לה לנסות "התחל האזנה" מיד עם הפתיחה - בדיוק
כאילו המבוגר עצמו לחץ על הכפתור בתוך האפליקציה, כדי שיראה ויאשר את מה
שחסר שם.
שני המשטחים, וגם המסך הראשי של האפליקציה עצמו, חולקים את אותו מצב
(AppPrefs.KEY_RUNNING) ומתעדכנים זה מזה באופן מיידי דרך
WidgetUpdateHelper.refreshAll() - כל שינוי במקום אחד (כולל אם השירות
עוצר את עצמו לבד עקב אובדן הרשאה) מתעדכן בכל המשטחים האחרים.
הערה טכנית: ה-layout של הווידג'ט (widget_listening.xml) מכוון
בכוונה לא להשתמש בערכות הנושא המותאמות אישית שלנו (?attr/colorXxx)
- RemoteViews של widget מצוירים בהקשר (context) של הלancher של המשתמש,
לא של האפליקציה שלנו, ולכן ה-attrs שהוגדרו רק אצלנו לא היו נפתרים שם
נכון. הוא משתמש בצבעים/drawables קונקרטיים בלבד (כמו
bg_toggle_idle), שממילא כבר בנויים כך מטעמי תאימות למכשירים ישנים.
כולם מרוכזים כעת ב-AppPrefs.java (בעבר היו קבועים כפולים ב-MainActivity
בלבד - עכשיו גם AutoAnswerReceiver צריך גישה אליהם באופן עצמאי):
PREFS_NAME = "babysitter_prefs"
KEY_PHONE // מספר טלפון ראשי
KEY_PHONE_SECONDARY // מספר טלפון משני (ל"אין מענה")
KEY_SENSITIVITY // 0 (רגיש מאוד) עד 100 (פחות רגיש)
KEY_RUNNING // האם ההאזנה לבכי פעילה כרגע
KEY_AUTO_ANSWER_ENABLED // האם מענה אוטומטי לשיחות נכנסות מופעל
KEY_AUTO_ANSWER_NUMBERS // רשימת מספרים מאושרים למענה אוטומטי (מופרדת בפסיקים)
KEY_AUTO_ANSWER_SPEAKER // האם לענות ברמקול
KEY_ALL_CALLS_SPEAKER // כל שיחות ה-CryDetectionService ברמקול
KEY_MUTE_INCOMING_VOICE // השתקת קול השיחה הנכנסת (יוצאות שלנו)
KEY_MUTE_RINGER_WHILE_ACTIVE // השתקת צלצול בזמן שההאזנה פעילה
KEY_THEME_MODE // "system" / "light" / "dark" (ברירת מחדל: system)
KEY_CALIBRATED_BASELINE // קו הבסיס האחרון שנמדד לרעש הרקע בחדר (כיול אוטומטי)
KEY_LAST_SAMPLE_TIME_MS // חותמת הזמן של הדגימה התקינה האחרונה (למסך "מצב מערכת")
KEY_EVENT_LOG // יומן האירועים האחרונים (ראו EventLog.java)
KEY_HEARTBEAT_ENABLED // האם הודעת התקינות היומית מופעלת
הסף כעת מחושב מעל קו הבסיס שנמדד בכיול האוטומטי (ראו הסבר מפורט למעלה), ולא כערך מוחלט קבוע כמו בעבר:
margin = 1500 + (sensitivityPercent * 130) // ~1500 עד ~14500 מעל קו הבסיס
threshold = baseline + margin // סף חיוג מלא
nearCryThreshold = baseline + margin * 0.45 // סף "כמעט בכי" (חזותי בלבד - צביעת מד העוצמה)
כאשר sensitivityPercent הוא 0 (רגיש מאוד), 40 (רגיל, ברירת מחדל) או 80
(פחות רגיש), ו-baseline הוא קו הבסיס שנמדד בפועל בחדר (ברירת מחדל אם
עוד לא נמדד: 800). נדרשות 3 דגימות רצופות מעל הסף (כ-2 שניות של רעש
מתמשך) וגם מקדם שונות (מחזוריות) מספיק גבוה בין הדגימות האחרונות
לפני שמתבצע חיוג בפועל - ראו הסבר מלא בסעיף "זיהוי בכי חכם יותר" למעלה.
| הגדרה | ערך | הערה |
|---|---|---|
compileSdk |
34 |
הועלה מ-33 כדי לתמוך ב-targetSdk 34. |
targetSdk |
34 |
הועלה מ-19 - ראו הסבר מלא בסעיף "הרשאות בזמן ריצה" למעלה. |
minSdk |
19 |
ללא שינוי - Android 4.4 KitKat, תואם למכשיר היעד. |
versionCode / versionName |
1 / "1.0" |
ערכי ברירת מחדל לבנייה מקומית בלבד - ב-CI מוחלפים אוטומטית (ראו למטה). |
בעבר versionCode היה קבוע (1) בקוד ולא התעדכן בין בניות. עכשיו, בשני
ה-pipelines (.github/workflows/build.yml וגם .gitlab-ci.yml), שלב
חדש לפני הבנייה מחליף את versionCode/versionName ב-app/build.gradle
במספר הרצה עולה (github.run_number / CI_PIPELINE_IID בהתאמה) - כך שכל
APK שיוצא מקבל אוטומטית מספר גרסה חדש וגבוה יותר מהקודם, בלי צורך לזכור
לעדכן ידנית בקוד. ב-GitHub Actions, כל בנייה שמקורה ב-push גם יוצרת
GitHub Release קבוע עם ה-APK, בתגית build-<run_number>.
AGP הועלה בהתאם ל-8.5.2 (נדרש כדי לתמוך ב-compileSdk 34), ותמונת ה-Docker
בשני ה-pipelines הועלתה ל-gradle:8.7-jdk17.
- פתחו את התיקייה כפרויקט קיים.
- חכו לסנכרון Gradle (עשוי לדרוש הורדת Android SDK 34 אם לא מותקן).
Build > Build Bundle(s) / APK(s) > Build APK(s).
- צרו מאגר ריק ב-GitLab, ודחפו אליו את כל תוכן התיקייה.
- עברו ל-
Build > Pipelines— הריצה מתחילה אוטומטית. - בסיום, ה-APK זמין כ-Job Artifact של
assembleDebug.
- צרו מאגר ריק ב-GitHub, ודחפו אליו את כל תוכן התיקייה (ל-
mainאוmaster). - עברו ל-טאב
Actions— הריצה מתחילה אוטומטית. - בסיום, ה-APK זמין גם כ-Artifact של ה-workflow וגם בעמוד
Releases.
בשתי אופציות הענן, הריצה הראשונה איטית יותר (הורדת SDK), וריצות הבאות מהירות בזכות ה-cache.
- זיהוי לפי עוצמת קול בלבד, לא זיהוי בכי אמיתי — אין רכיב AI; כל רעש חזק (לא רק בכי) יכול להפעיל חיוג. זהו טרייד-אוף מכוון לטובת פעולה אופליין, פרטית וללא עלות.
- לאחר כל שיחה יש "קירור" של דקה לפני חיוג חוזר, כדי למנוע חיוגים ברצף.
- מומלץ לוודא במכשיר בפועל שהאפליקציה אינה נסגרת ע"י חוסכי סוללה אגרסיביים (למשל ב-MIUI יש להוסיף אותה לרשימת "ללא הגבלת סוללה").
- זיהוי "אין מענה" לשיחה יוצאת הוא הערכה (heuristic) מבוססת-זמן, לא זיהוי
ודאי — ראו הסבר ב-
ANSWER_TIMEOUT_MS. - מענה אוטומטי לשיחות נכנסות במכשירים מתחת ל-Android 8 מתבסס על סימולציית
לחיצת "הדסט" (
KEYCODE_HEADSETHOOK) — best-effort בלבד, תלוי יצרן/גרסה. - תיקון הרמקול (
setCommunicationDeviceל-API31+,applySpeakerReinforcedלגרסאות מתחת לזה כולל אנדרואיד 11) עדיין best-effort: אפליקציה שאינה החייגן ברירת המחדל לא יכולה להבטיח 100% שליטה בניתוב שמע של שיחה שמנוהלת ע"י אפליקציית הטלפון המובנית - ראו הסבר מפורט ב-AudioRoutingUtil. - אין עדיין חתימת release (keystore) - ה-CI מפיק APK דיבאג בלבד, מוכן להתקנה ידנית (side-load).
- כיול רעש הרקע מתבצע פעם אחת בתחילת כל "התחל האזנה" (כ-3 שניות) - אם הרעש בחדר משתנה מהותית תוך כדי ריצה ארוכה (למשל מזגן שנדלק/נכבה באמצע הלילה), הסף לא יתעדכן שוב עד ההפעלה הבאה.
- בדיקת המחזוריות (זיהוי בכי "פועם" מול רעש קבוע) היא היוריסטיקה סטטיסטית פשוטה, לא זיהוי בכי אמיתי - רעש לא-קבוע אחר (למשל שיחה ברקע) עדיין יכול לעבור אותה.
- הודעת האימות היומית בשיטת "שיחה קולית בלבד" לא יכולה טכנית "לתייג" את עצמה כשיחת בדיקה (Android לא חושף API לזה לאפליקציה רגילה) - ההבדל מבוסס על שעה קבועה וצפויה בלבד. שיטת "שיחה + SMS" נותנת הקשר ברור יותר, במחיר דרישת שתי ההרשאות. עלות SMS/שיחה - לפי תוכנית הסלולר של ה-SIM שבמכשיר.
- בדיקת המיקרופון בודקת רק שהמיקרופון תופס שינוי עוצמה - היא לא מבצעת שיחת בדיקה אמיתית (נמנע בכוונה, כדי לא לחייב/להטריד את ההורה סתם בכל בדיקה).
- בדיקת ניהול הסוללה מכסה רק את מנגנון Battery Optimization הסטנדרטי של אנדרואיד - שכבות ניהול חשמל נוספות של יצרנים ספציפיים (כמו "אתחול אוטומטי" ב-MIUI) אין להן API ציבורי לבדיקה/תיקון אוטומטי, ומוצגת רק הנחיה טקסטואלית. גם עם אישור "ללא הגבלת סוללה", ייתכן שמכשירים מסוימים עדיין ישהו שירותי רקע בנסיבות קיצוניות.
- בדיקת הקליטה (
CellularServiceUtil.definitelyOutOfService) מבוססת עלTelephonyManager.getServiceState(), שזמין כ-API סינכרוני רק מ-Android 8 (API 26) - במכשירים ישנים יותר (כולל מכשיר המקשים היעד) לא מוצגת אזהרת "אין קליטה" מיידית בהתחלת האזנה (בדיקת מצב טיסה/SIM כן פעילה שם), אבל הבדיקה הקריטית יותר לפני כל חיוג בפועל (מצב טיסה) פעילה בכל הגרסאות.
עדכון: רמקול באנדרואיד 11, האזנה שנעצרת עם מסך כבוי, מצב טיסה, בדיקת קליטה, מסך הגדרות, הודעת אימות משודרגת
- תיקון רמקול לאנדרואיד 11:
AudioRoutingUtil.applySpeakerReinforced()מיישם את הרמקול מיד ועוד 4 פעמים נוספות בשניות הראשונות של השיחה, כדי "לנצח" מקרים שבהם המערכת/אפליקציית הטלפון דורסת את הניתוב בחזרה - תוקן גם ב-CryDetectionServiceוגם ב-AutoAnswerReceiver. תוקן גם באג נלווה ב-AutoAnswerReceiver: ה-goAsync()נסגר מוקדם מדי וחתך את הניסיונות החוזרים באמצע - עכשיו נשאר פתוח עד אחריהם. - "ההאזנה מפסיקה כשהמסך כבה":
MainActivityמציגה מיד בהתחלת האזנה את דיאלוג פטור ניהול הסוללה הרשמי אם עוד לא אושר, במקום להשאיר את זה נסתר רק במסך "מצב מערכת". - "האפליקציה חושבת שהתקשרה במצב טיסה":
CellularServiceUtilחדש - נבדק מצב טיסה לפני כל ניסיון חיוג; אם דלוק, לא מנסים בכלל ונרשם נכון כ"נכשל" (לא כ"בוצע"), עם התראה ייעודית. - בדיקת קליטה בהתחלת האזנה: אזהרה חד-פעמית (לא חוסמת) על מצב טיסה/ SIM חסר/אין שירות סלולרי, בנוסף לבדיקה הקריטית שנעשית שוב לפני כל חיוג.
- מסך הגדרות: כפתור "מצב מערכת" הופרד ויזואלית מהכותרת (רקע/מרווח נפרדים, לא צמוד לרצועה הטורקיז), והוא מקבל את הפוקוס הראשוני בכניסה למסך (במקום שדה הזנת המספר).
- הודעת אימות יומית שודרגה: שעה ניתנת לבחירה (לא קבועה על 08:00), ושלוש שיטות אימות - SMS בלבד, שיחה קולית בלבד, או שיחה + SMS מבשר (מומלץ) - כדי שאפשר יהיה להבדיל שיחת אימות משיחת חירום אמיתית גם במשפחות בלי חבילת SMS פעילה. ראו הסבר מלא בסעיף הודעת האימות למעלה.
- שם קובץ ה-APK שיוצא מה-CI (גם GitHub וגם GitLab) שונה מ-"app-debug.apk"
הגנרי לשם האפליקציה בעברית -
בייביסיטר_טלפוני-debug.apk(מוגדר ישירות ב-app/build.gradleעםoutputFileName, כך שזה חל גם על בנייה מקומית מ-Android Studio, לא רק ב-CI). - הוסרה לחלוטין התראת/רטט "כמעט בכי" (notification קופצת שהופיעה
כשהרעש היה מוגבר אך מתחת לסף החיוג) - לפי בקשה מפורשת. הרשאת
VIBRATEהוסרה מהמניפסט בהתאם, כשלא נשאר לה עוד שימוש. מד העוצמה הוויזואלי במסך הבית (שממשיך לצבוע כתום במצב "כמעט בכי") נשאר - זו תצוגה פסיבית בלבד במסך, לא בועת מידע/התראה קופצת.
- ווידג'ט למסך הבית (
ListeningWidgetProvider) - כפתור התחל/עצור האזנה ישירות מהמסך הראשי של אנדרואיד, בלי לפתוח את האפליקציה. - אריח בהגדרות המהירות (
ListeningTileService, Quick Settings, Android 7+) - אותה הפעלה/כיבוי מהירים ממגירת ההגדרות המהירות. - שניהם משתפים מצב עם המסך הראשי דרך
WidgetUpdateHelper, ופותחים את האפליקציה (עם ניסיון אוטומטי להתחיל) רק אם משהו חסר (הרשאה/מספר טלפון) שדורש אישור מבוגר בחלון אמיתי.
- כיול אוטומטי לרעש רקע: כל "התחל האזנה" פותח בכ-3 שניות של מדידת הרעש הבסיסי בחדר, וקובע לפיו "קו בסיס" ספציפי - הסף בפועל נבנה מעליו, כדי שחדר רועש (מזגן/מאוורר) לא יגרום לחיוגי שווא וחדר שקט לא יפספס בכי חלש.
- זיהוי מחזוריות: נדרש גם רעש מתמשך וגם "מקדם שונות" מספיק (בכי אמיתי פועם, לא קבוע כמו רעש רקע) לפני שמחייגים בפועל.
- מסך "מצב מערכת" חדש (
StatusActivity): בריאות השירות בזמן אמת, זמן הדגימה האחרונה, כמות שיחות היום, מצב הגנת סוללה (עם כפתור לתיקון), ורשימת אירועים אחרונים. - הודעת תקינות יומית (heartbeat, כבויה כברירת מחדל): SMS קצר להורה
כל בוקר ב-08:00, מתוזמן ע"י
AlarmManagerומשוחזר אחרי הפעלה מחדש של המכשיר. - חיוג חוזר אחרי כשל טכני (לא רק "אין מענה"): אם
ACTION_CALLעצמו נכשל מסיבה טכנית, מנסים שוב פעם אחת לפני שמוותרים. - מד עוצמה בזמן אמת במסך הבית, בזמן שההאזנה פעילה (ירוק/כתום/אדום לפי מצב הרעש הנוכחי).
- בדיקת מיקרופון בהגדרות - מנגן צליל קצר ובודק שה-recorder תופס אותו, בלי לבצע שיחת בדיקה אמיתית.
- הוקטן הכפתור המרכזי (מ-220dp ל-150dp) ומבנה המסך שונה כך שהאזור המרכזי ממורכז רק בין הכותרת למארז הכפתורים התחתון - למניעת חפיפה במסכים קטנים.
- כפתור "מענה אוטומטי" מהיר נוסף למסך הבית, מעל כפתור ההגדרות - מפעיל/מכבה רק את המתג עצמו, בלי להיכנס להגדרות; מספרי הטלפון עצמם נשארים אך ורק בתוך ההגדרות.
- מסך בית חדש: כפתור עגול גדול במרכז המסך להפעלה/כיבוי (צבע משתנה
טורקיז/אדום), וכפתור "הגדרות" צמוד לתחתית. כל שדות הקלט עברו למסך
הגדרות נפרד (
SettingsActivity). - מענה אוטומטי הופרד לגמרי מהאזנת הבכי:
AutoAnswerReceiverחדש,BroadcastReceiverעצמאי הרשום ב-manifest, פועל גם כשההאזנה כבויה לגמרי - ותמיד רק על שיחות נכנסות, לעולם לא יוצאות. targetSdkהועלה מ-19 ל-34 - מבטל את דיאלוג "האפליקציה נבנתה לגרסה ישנה" במכשירים חדשים.minSdkנשאר 19 ללא שינוי.- בוטלה נפילה למסך חיוג ידני (ACTION_DIAL) אם הרשאת שיחה חסרה - הוחלפה בבקשת הרשאות מראש (לפני הפעלת ההאזנה/המענה האוטומטי) + התראת אזהרה בולטת אם ההרשאה בכל זאת אבדה אחר כך.
- ערכת נושא: מערכת/בהיר/כהה בהגדרות (ברירת מחדל: מערכת), ממומש בלי
AndroidX דרך
?attr/...+ שניstyle-ים (ThemeUtil.java). - תיקון רמקול באנדרואיד 12+:
AudioManager.setCommunicationDevice()במקוםsetSpeakerphoneOn()הישן שהפך פחות אמין (AudioRoutingUtil.java). - עדכון גרסה אוטומטי ב-CI:
versionCode/versionNameמתעדכנים אוטומטית בכל ריצת CI לפי מספר ההרצה, במקום להישאר קבועים על 1.compileSdk/AGP/Gradle הועלו בהתאם לתמיכה ב-API 34.
- תוקן באג מרכזי: אחרי חיוג ראשון, ההאזנה לפעמים "נתקעה בשקט" ולא זיהתה רעש נוסף עד כיבוי/הפעלה מחדש ידניים (ולפעמים התאוששה מעצמה רק אחרי כמה דקות). נוסף מנגנון "שומר סף" (watchdog) שבודק כל 10 שניות שההאזנה עדיין מייצרת דגימות עוצמה תקינות, וכופה הפעלה מחדש תוך כ-15-25 שניות אם לא.
- שיפור זיהוי רעש: כעת נדרשות 2 דגימות רצופות מעל הסף לפני שמתבצע חיוג, כדי לצמצם חיוגי שווא מרעש חד-פעמי.
- "אין מענה" / זיהוי דחייה לתא קולי: נוסף איתות
FAST_CONNECT_SUSPICIOUS_MSשמזהה מעבר מהיר מדי למצב OFFHOOK כחשוד לדחייה מיידית. - חיווי פוקוס ירוק לכפתורי "אנשי קשר", אייקון האפליקציה ללא סרט "TRIAL".
- הוסרו כל תלויות AndroidX/AppCompat;
MainActivityעבר ל-Activityרגיל ול-theme הגולמי של הפלטפורמה (החלטה זו נשארת ללא שינוי גם אחרי הוספת מסך ההגדרות וערכות הנושא). - עיצוב מחדש למוסכמת "רקע כהה + הדגשת טורקיז" שנמצאה במכשירי היעד.
- הוסר שימוש ב-
RECEIVE_BOOT_COMPLETED(הרשאה שלא הייתה בשימוש בפועל).
- תוקן באג שבו ניתוק שיחה יכול "לתקוע" את ההאזנה בשקט עד הפעלה מחדש ידנית — כעת כל סיום שיחה מפעיל מחדש את ההאזנה באופן יזום.
- נוספה הפעלה מחדש תקופתית של ההקלטה (כל 4 דקות) כדי שהקובץ הזמני לא יתפח.
- נוספה התאוששות אוטומטית כשהמיקרופון תפוס (למשל בזמן שיחה פעילה).
- הוחלף סליידר (SeekBar) לבחירת רגישות בשלושה כפתורי בחירה — ניווט אמין יותר עם מקשים פיזיים.
- עדכון תמונת ה-Docker בשני ה-pipelines ל-
gradle:8.7-jdk17(בעקבות העלאת AGP ל-8.5.2 לתמיכה ב-compileSdk 34). - תוקנה שגיאת קומפילציה קודמת ב-
CryDetectionService.java: הקריאהtelephonyManager.acceptRingingCall()הוחלפה בקריאה נכונה עלTelecomManager.acceptRingingCall().