תמונה בתוך תמונה

התכונה 'תמונה בתוך תמונה' (PIP) למכשירי Android ניידים מאפשרת למשתמשים לשנות את הגודל של אפליקציה עם פעילות מתמשכת לחלון קטן. התכונה 'תמונה בתוך תמונה' שימושית במיוחד באפליקציות וידאו, כי התוכן ממשיך לפעול בזמן שהמשתמש יכול לבצע פעולות אחרות. המשתמשים יכולים לשנות את המיקום של החלון הזה דרך SystemUI וליצור אינטראקציה עם האפליקציה שמוצגת כרגע במצב 'תמונה בתוך תמונה' באמצעות (עד שלוש) פעולות שסופקו על ידי האפליקציה.

כדי להשתמש ב-PIP, האפליקציות שתומכות בו צריכות לקבל אישור מפורש מהמשתמשים, והוא פועל על בסיס כל פעילות בנפרד. (לאפליקציה אחת יכולות להיות כמה פעילויות, אבל רק אחת מהן יכולה להיות במצב תמונה בתוך תמונה). הפעילויות מבקשות להיכנס למצב 'תמונה בתוך תמונה' על ידי קריאה ל-enterPictureInPictureMode(), ומקבלות קריאות חוזרות (callback) לפעילות בצורה של onPictureInPictureModeChanged().

השיטה setPictureInPictureParams() מאפשרת לפעילויות לשלוט ביחס הגובה-רוחב שלהן כשהן מוצגות בתצוגת PIP, וגם בפעולות מותאמות אישית שמאפשרות למשתמשים ליצור אינטראקציה עם הפעילות בלי להרחיב אותה. ב-PIP, הפעילות מושהית, אבל היא מוצגת, והיא לא מקבלת ישירות קלט מגע או מיקוד חלון. אפשר להציג רק משימה אחת במצב תמונה בתוך תמונה בכל פעם.

מידע נוסף זמין בתיעוד למפתחים של Android בנושא תמונה בתוך תמונה.

דרישות לגבי מכשירים

כדי לתמוך ב-PIP, צריך להפעיל את תכונת המערכת PackageManager#FEATURE_PICTURE_IN_PICTURE ב-/android/frameworks/base/core/java/android/content/pm/PackageManager.java. במכשירים שתומכים ב-PIP, המסך צריך להיות גדול מ-220dp ברוחב המינימלי שלו. בדומה למסך מפוצל עם ריבוי חלונות, PIP מאפשר להפעיל כמה פעילויות במסך בו-זמנית. לכן, במכשירים צריכים להיות מעבד (CPU) וזיכרון RAM מספיקים כדי לתמוך בתרחיש לדוגמה הזה.

הטמעה

רוב הפעולות שקשורות לניהול מחזור החיים של הפעילות מתבצעות במערכת בין ActivityManager לבין WindowManager. ההטמעה של ממשק המשתמש לדוגמה נמצאת בחבילה SystemUI.

שינויים במערכת לא אמורים להשפיע על ההתנהגות המובנית שלה, כפי שמוגדרת על ידי בדיקות Compatibility Test Suite‏ (CTS). הלוגיקה של המערכת לגבי PIP מתבססת בעיקר על ניהול של משימות ופעילויות בתוך הערימה 'מוצמדת'. הנה סקירה כללית מהירה של הכיתה:

  • ActivityRecord: עוקב אחרי מצב התמונה בתוך תמונה של כל פעילות. כדי למנוע ממשתמשים להיכנס למצב תמונה בתוך תמונה בנסיבות מסוימות, כמו ממסך הנעילה או במהלך שימוש במציאות מדומה, מוסיפים מקרים ל-checkEnterPictureInPictureState().
  • ActivityManagerService: הממשק הראשי שדרכו מבקשים להפעיל את התמונה בתוך התמונה (PIP) מהפעילות, והממשק שדרכו מתקשרים מ-WindowManager ומ-SystemUI כדי לשנות את מצב הפעילות של התמונה בתוך התמונה.
  • ActivityStackSupervisor: נקרא מתוך ActivityManagerService כדי להעביר משימות אל הערימה המוצמדת או ממנה, ולעדכן את WindowManager לפי הצורך.
  • PinnedStackWindowController: ממשק WindowManager מ-ActivityManager.
  • PinnedStackController: מדווח על שינויים במערכת ל-SystemUI, כמו הצגה או הסתרה של IME, שינוי יחס הגובה-רוחב או שינוי פעולות.
  • BoundsAnimationController: מפעיל אנימציה של חלונות הפעילות של PIP באופן שלא גורם לשינוי בהגדרות בזמן שינוי הגודל.
  • PipSnapAlgorithm: מחלקה משותפת שמשמשת גם במערכת וגם ב-SystemUI, ושולטת בהתנהגות ההצמדה של חלון ה-PIP ליד קצוות המסך.

ההפניה SystemUI מספקת הטמעה מלאה של PIP שתומכת בהצגת פעולות מותאמות אישית למשתמשים ובמניפולציה כללית, כמו הרחבה וסגירה. יצרני מכשירים יכולים להסתמך על השינויים האלה, כל עוד הם לא משפיעים על ההתנהגויות המובנות כפי שהן מוגדרות ב-CDD. הנה סקירה כללית מהירה של הכיתה:

  • PipManager: הרכיב SystemUI שמתחיל ב-SystemUI.
  • PipTouchHandler: רכיב הטיפול במגע, ששולט בתנועות שמשנות את התמונה בתוך תמונה. ההגדרה הזו משמשת רק בזמן שהקלט פעיל ב-PIP (ראו InputConsumerController). אפשר להוסיף כאן תנועות חדשות.
  • PipMotionHelper: מחלקה נוחה למעקב אחרי המיקום של חלון ה-PIP והאזור המותר במסך. השיחות מתבצעות דרך ActivityManagerService כדי לעדכן או להנפיש את המיקום והגודל של התמונה בתוך התמונה.
  • PipMenuActivityController: מתחיל פעילות שמציגה את הפעולות שזמינות כרגע בפעילות במצב תמונה בתוך תמונה. הפעילות הזו היא פעילות של שכבת-על של משימה, והיא מסירה את צרכן הקלט של שכבת-העל כדי לאפשר אינטראקציה.
  • PipMenuActivity: ההטמעה של פעילות התפריט.
  • PipMediaController: מאזין שמעדכן את SystemUI כשסשן המדיה משתנה באופן שעשוי להשפיע על פעולות ברירת המחדל ב-PIP.
  • PipNotificationController: בקר שמבטיח שההתראה תהיה פעילה בזמן שהמשתמש משתמש בתכונת ה-PIP.
  • PipDismissViewController: שכבת העל שמוצגת למשתמשים כשהם מתחילים ליצור אינטראקציה עם חלון ה-PIP, כדי לציין שאפשר לסגור אותו.

מיקום ברירת מחדל

יש משאבי מערכת שונים ששולטים במיקום ברירת המחדל של התמונה בתוך התמונה:

  • config_defaultPictureInPictureGravity: מספר שלם של gravity, שקובע את הפינה שבה ימוקם ה-PIP, כמו BOTTOM|RIGHT.
  • config_defaultPictureInPictureScreenEdgeInsets: ההיסטים מהצדדים של המסך למיקום התמונה בתוך תמונה.
  • config_pictureInPictureDefaultSizePercent ו-config_pictureInPictureDefaultAspectRatio: השילוב של אחוז רוחב המסך ויחס הגובה-רוחב קובע את הגודל של התמונה בתוך התמונה. גודל ברירת המחדל המחושב של חלון ה-PIP לא יכול להיות קטן מ-@dimen/default_minimal_size_pip_resizable_task, כפי שמוגדר ב-CTS וב-CDD.
  • config_pictureInPictureSnapMode: התנהגות ההצמדה כפי שמוגדרת ב-PipSnapAlgorithm.

ההטמעות במכשירים לא צריכות לשנות את יחסי הגובה-רוחב המינימליים והמקסימליים שמוגדרים ב-CDD וב-CTS.

הרשאות

ההגדרה 'פעולת האפליקציה' (OP_PICTURE_IN_PICTURE) לכל חבילה ב-AppOpsManager (main/core/java/android/app/AppOpsManager.java) מאפשרת למשתמשים לשלוט בהצגת תמונה בתוך תמונה ברמת האפליקציה דרך הגדרות המערכת. ההטמעות של המכשירים צריכות להתייחס לבדיקה הזו כשפעילות מבקשת להיכנס למצב 'תמונה בתוך תמונה'.

בדיקה

כדי לבדוק הטמעות של PIP, מריצים את כל הבדיקות שקשורות לתמונה בתוך תמונה שנמצאות בבדיקות CTS בצד המארח בקטע /cts/hostsidetests/services/activitymanager, במיוחד ב-ActivityManagerPinnedStackTests.java.