• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.pip;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
21 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
22 
23 import android.annotation.Nullable;
24 import android.app.ActivityTaskManager;
25 import android.app.ActivityTaskManager.RootTaskInfo;
26 import android.app.RemoteAction;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.os.RemoteException;
30 import android.util.DisplayMetrics;
31 import android.util.Log;
32 import android.util.Pair;
33 import android.util.TypedValue;
34 import android.window.TaskSnapshot;
35 
36 import com.android.internal.protolog.common.ProtoLog;
37 import com.android.wm.shell.protolog.ShellProtoLogGroup;
38 
39 import java.util.List;
40 import java.util.Objects;
41 
42 /** A class that includes convenience methods. */
43 public class PipUtils {
44     private static final String TAG = "PipUtils";
45 
46     // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
47     private static final double EPSILON = 1e-7;
48 
49     /**
50      * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
51      * The component name may be null if no such activity exists.
52      */
getTopPipActivity(Context context)53     public static Pair<ComponentName, Integer> getTopPipActivity(Context context) {
54         try {
55             final String sysUiPackageName = context.getPackageName();
56             final RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
57                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
58             if (pinnedTaskInfo != null && pinnedTaskInfo.childTaskIds != null
59                     && pinnedTaskInfo.childTaskIds.length > 0) {
60                 for (int i = pinnedTaskInfo.childTaskNames.length - 1; i >= 0; i--) {
61                     ComponentName cn = ComponentName.unflattenFromString(
62                             pinnedTaskInfo.childTaskNames[i]);
63                     if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
64                         return new Pair<>(cn, pinnedTaskInfo.childTaskUserIds[i]);
65                     }
66                 }
67             }
68         } catch (RemoteException e) {
69             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
70                     "%s: Unable to get pinned stack.", TAG);
71         }
72         return new Pair<>(null, 0);
73     }
74 
75     /**
76      * @return the pixels for a given dp value.
77      */
dpToPx(float dpValue, DisplayMetrics dm)78     public static int dpToPx(float dpValue, DisplayMetrics dm) {
79         return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
80     }
81 
82     /**
83      * @return true if the aspect ratios differ
84      */
aspectRatioChanged(float aspectRatio1, float aspectRatio2)85     public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
86         return Math.abs(aspectRatio1 - aspectRatio2) > EPSILON;
87     }
88 
89     /**
90      * Checks whether title, description and intent match.
91      * Comparing icons would be good, but using equals causes false negatives
92      */
remoteActionsMatch(RemoteAction action1, RemoteAction action2)93     public static boolean remoteActionsMatch(RemoteAction action1, RemoteAction action2) {
94         if (action1 == action2) return true;
95         if (action1 == null || action2 == null) return false;
96         return action1.isEnabled() == action2.isEnabled()
97                 && action1.shouldShowIcon() == action2.shouldShowIcon()
98                 && Objects.equals(action1.getTitle(), action2.getTitle())
99                 && Objects.equals(action1.getContentDescription(), action2.getContentDescription())
100                 && Objects.equals(action1.getActionIntent(), action2.getActionIntent());
101     }
102 
103     /**
104      * Returns true if the actions in the lists match each other according to {@link
105      * PipUtils#remoteActionsMatch(RemoteAction, RemoteAction)}, including their position.
106      */
remoteActionsChanged(List<RemoteAction> list1, List<RemoteAction> list2)107     public static boolean remoteActionsChanged(List<RemoteAction> list1, List<RemoteAction> list2) {
108         if (list1 == null && list2 == null) {
109             return false;
110         }
111         if (list1 == null || list2 == null) {
112             return true;
113         }
114         if (list1.size() != list2.size()) {
115             return true;
116         }
117         for (int i = 0; i < list1.size(); i++) {
118             if (!remoteActionsMatch(list1.get(i), list2.get(i))) {
119                 return true;
120             }
121         }
122         return false;
123     }
124 
125     /** @return {@link TaskSnapshot} for a given task id. */
126     @Nullable
getTaskSnapshot(int taskId, boolean isLowResolution)127     public static TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
128         if (taskId <= 0) return null;
129         try {
130             return ActivityTaskManager.getService().getTaskSnapshot(
131                     taskId, isLowResolution, false /* takeSnapshotIfNeeded */);
132         } catch (RemoteException e) {
133             Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
134             return null;
135         }
136     }
137 }
138