Merge "DO NOT MERGE - Merge ab/7272582" into stage-aosp-master

This commit is contained in:
TreeHugger Robot
2021-06-18 00:30:39 +00:00
committed by Android (Google) Code Review
174 changed files with 3765 additions and 1731 deletions

View File

@@ -5183,12 +5183,6 @@ public class Activity extends ContextThemeWrapper
* #checkSelfPermission(String)}.
* </p>
* <p>
* Calling this API for permissions already granted to your app would show UI
* to the user to decide whether the app can still hold these permissions. This
* can be useful if the way your app uses data guarded by the permissions
* changes significantly.
* </p>
* <p>
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* <code>true</code> because in this case the activity would not receive

View File

@@ -377,6 +377,21 @@ public abstract class ActivityManagerInternal {
*/
public abstract boolean hasRunningForegroundService(int uid, int foregroundServiceType);
/**
* Returns {@code true} if the given notification channel currently has a
* notification associated with a foreground service. This is an AMS check
* because that is the source of truth for the FGS state.
*/
public abstract boolean hasForegroundServiceNotification(String pkg, @UserIdInt int userId,
String channelId);
/**
* If the given app has any FGSs whose notifications are in the given channel,
* stop them.
*/
public abstract void stopForegroundServicesForChannel(String pkg, @UserIdInt int userId,
String channelId);
/**
* Registers the specified {@code processObserver} to be notified of future changes to
* process state.
@@ -440,4 +455,11 @@ public abstract class ActivityManagerInternal {
* @return true if exists, false otherwise.
*/
public abstract boolean isPendingTopUid(int uid);
public abstract void tempAllowWhileInUsePermissionInFgs(int uid, long duration);
public abstract boolean isTempAllowlistedForFgsWhileInUse(int uid);
public abstract boolean canAllowWhileInUsePermissionInFgs(int pid, int uid,
@NonNull String packageName);
}

View File

@@ -4602,6 +4602,10 @@ public final class ActivityThread extends ClientTransactionHandler {
}
if (r.isTopResumedActivity == onTop) {
if (!Build.IS_DEBUGGABLE) {
Slog.w(TAG, "Activity top position already set to onTop=" + onTop);
return;
}
throw new IllegalStateException("Activity top position already set to onTop=" + onTop);
}

View File

@@ -16,6 +16,7 @@
package android.app.admin;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
@@ -241,6 +242,7 @@ public abstract class DevicePolicyManagerInternal {
/**
* Returns the profile owner component for the given user, or {@code null} if there is not one.
*/
@Nullable
public abstract ComponentName getProfileOwnerAsUser(int userHandle);
/**
@@ -254,4 +256,9 @@ public abstract class DevicePolicyManagerInternal {
* {@link #supportsResetOp(int)} is true.
*/
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
/**
* Returns whether the given package is a device owner or a profile owner in the calling user.
*/
public abstract boolean isDeviceOrProfileOwnerInCallingUser(String packageName);
}

View File

@@ -62,6 +62,9 @@ public final class DisplayManager {
* {@link #EXTRA_WIFI_DISPLAY_STATUS} extra.
* </p><p>
* This broadcast is only sent to registered receivers and can only be sent by the system.
* </p><p>
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission is required to
* receive this broadcast.
* </p>
* @hide
*/
@@ -870,12 +873,52 @@ public final class DisplayManager {
public interface DeviceConfig {
/**
* Key for refresh rate in the zone defined by thresholds.
* Key for refresh rate in the low zone defined by thresholds.
*
* Note that the name and value don't match because they were added before we had a high
* zone to consider.
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_defaultZoneBehavior
*/
String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone";
/**
* Key for accessing the low display brightness thresholds for the configured refresh
* rate zone.
* The value will be a pair of comma separated integers representing the minimum and maximum
* thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
*
* Note that the name and value don't match because they were added before we had a high
* zone to consider.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
* @hide
*/
String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_brightness_thresholds";
/**
* Key for accessing the low ambient brightness thresholds for the configured refresh
* rate zone. The value will be a pair of comma separated integers representing the minimum
* and maximum thresholds of the zone, respectively, in lux.
*
* Note that the name and value don't match because they were added before we had a high
* zone to consider.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
* @hide
*/
String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_ambient_thresholds";
/**
* Key for refresh rate in the high zone defined by thresholds.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_fixedRefreshRateInHighZone
*/
String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone";
/**
* Key for accessing the display brightness thresholds for the configured refresh rate zone.
@@ -883,11 +926,11 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
* @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate
* @hide
*/
String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_brightness_thresholds";
String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS =
"fixed_refresh_rate_high_display_brightness_thresholds";
/**
* Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
@@ -895,12 +938,11 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in lux.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
* @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate
* @hide
*/
String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_ambient_thresholds";
String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
"fixed_refresh_rate_high_ambient_brightness_thresholds";
/**
* Key for default peak refresh rate
*

View File

@@ -101,7 +101,7 @@ public class AccessoryFilter {
public boolean matches(UsbAccessory acc) {
if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
if (mModel != null && !acc.getModel().equals(mModel)) return false;
return !(mVersion != null && !acc.getVersion().equals(mVersion));
return !(mVersion != null && !mVersion.equals(acc.getVersion()));
}
/**

View File

@@ -34,8 +34,12 @@ import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.TimeoutException;
/**
@@ -207,6 +211,12 @@ public class Process {
*/
public static final int SE_UID = 1068;
/**
* Defines the UID/GID for the iorapd.
* @hide
*/
public static final int IORAPD_UID = 1071;
/**
* Defines the UID/GID for the NetworkStack app.
* @hide
@@ -1397,4 +1407,43 @@ public class Process {
}
private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
/**
* Checks if a process corresponding to a specific pid owns any file locks.
* @param pid The process ID for which we want to know the existence of file locks.
* @return true If the process holds any file locks, false otherwise.
* @throws IOException if /proc/locks can't be accessed.
*
* @hide
*/
public static boolean hasFileLocks(int pid) throws Exception {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("/proc/locks"));
String line;
while ((line = br.readLine()) != null) {
StringTokenizer st = new StringTokenizer(line);
for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
String str = st.nextToken();
try {
if (i == 4 && Integer.parseInt(str) == pid) {
return true;
}
} catch (NumberFormatException nfe) {
throw new Exception("Exception parsing /proc/locks at \" "
+ line + " \", token #" + i);
}
}
}
return false;
} finally {
if (br != null) {
br.close();
}
}
}
}

View File

@@ -108,6 +108,9 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
types, mCallbacks, durationMs, interpolator, animationType);
InsetsAnimationThread.getHandler().post(() -> {
if (mControl.isCancelled()) {
return;
}
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
listener.onReady(mControl, types);

View File

@@ -113,13 +113,20 @@ public class InsetsSourceConsumer {
InsetsState.typeToString(control.getType()),
mController.getHost().getRootViewTitle()));
}
// We are loosing control
if (mSourceControl == null) {
// We are loosing control
mController.notifyControlRevoked(this);
// Restore server visibility.
mState.getSource(getType()).setVisible(
mController.getLastDispatchedState().getSource(getType()).isVisible());
// Check if we need to restore server visibility.
final InsetsSource source = mState.getSource(mType);
final boolean serverVisibility =
mController.getLastDispatchedState().getSourceOrDefaultVisibility(mType);
if (source.isVisible() != serverVisibility) {
source.setVisible(serverVisibility);
mController.notifyVisibilityChanged();
}
// For updateCompatSysUiVisibility
applyLocalVisibilityOverride();
} else {
// We are gaining control, and need to run an animation since previous state

View File

@@ -156,7 +156,10 @@ public class WindowlessWindowManager implements IWindowSession {
mStateForWindow.put(window.asBinder(), state);
}
return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
// Include whether the window is in touch mode.
return isInTouchMode() ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res;
}
/**
@@ -207,6 +210,15 @@ public class WindowlessWindowManager implements IWindowSession {
return !PixelFormat.formatHasAlpha(attrs.format);
}
private boolean isInTouchMode() {
try {
return WindowManagerGlobal.getWindowSession().getInTouchMode();
} catch (RemoteException e) {
Log.e(TAG, "Unable to check if the window is in touch mode", e);
}
return false;
}
/** @hide */
protected SurfaceControl getSurfaceControl(View rootView) {
final ViewRootImpl root = rootView.getViewRootImpl();
@@ -268,7 +280,8 @@ public class WindowlessWindowManager implements IWindowSession {
}
}
return 0;
// Include whether the window is in touch mode.
return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
}
@Override

View File

@@ -593,6 +593,14 @@ public class RemoteViews implements Parcelable, Filter {
public String getPackageName() {
return mContextForResources.getPackageName();
}
@Override
public boolean isRestricted() {
// Override isRestricted and direct to resource's implementation. The isRestricted is
// used for determining the risky resources loading, e.g. fonts, thus direct to context
// for resource.
return mContextForResources.isRestricted();
}
}
private class SetEmptyView extends Action {

View File

@@ -17,18 +17,14 @@
package com.android.internal.app;
import android.app.AlertDialog;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManagerInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.location.GpsNetInitiatedHandler;
@@ -43,7 +39,6 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
private static final String TAG = "NetInitiatedActivity";
private static final boolean DEBUG = true;
private static final boolean VERBOSE = false;
private static final int POSITIVE_BUTTON = AlertDialog.BUTTON_POSITIVE;
private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON_NEGATIVE;
@@ -55,17 +50,6 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
private int default_response = -1;
private int default_response_timeout = 6;
/** Used to detect when NI request is received */
private BroadcastReceiver mNetInitiatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "NetInitiatedReceiver onReceive: " + intent.getAction());
if (intent.getAction() == GpsNetInitiatedHandler.ACTION_NI_VERIFY) {
handleNIVerify(intent);
}
}
};
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -109,14 +93,12 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
protected void onResume() {
super.onResume();
if (DEBUG) Log.d(TAG, "onResume");
registerReceiver(mNetInitiatedReceiver, new IntentFilter(GpsNetInitiatedHandler.ACTION_NI_VERIFY));
}
@Override
protected void onPause() {
super.onPause();
if (DEBUG) Log.d(TAG, "onPause");
unregisterReceiver(mNetInitiatedReceiver);
}
/**
@@ -141,17 +123,4 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
LocationManagerInternal lm = LocalServices.getService(LocationManagerInternal.class);
lm.sendNiResponse(notificationId, response);
}
@UnsupportedAppUsage
private void handleNIVerify(Intent intent) {
int notifId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
notificationId = notifId;
if (DEBUG) Log.d(TAG, "handleNIVerify action: " + intent.getAction());
}
private void showNIError() {
Toast.makeText(this, "NI error" /* com.android.internal.R.string.usb_storage_error_message */,
Toast.LENGTH_LONG).show();
}
}

View File

@@ -22,7 +22,7 @@ import android.util.SparseArray;
public class ProcessMap<E> {
final ArrayMap<String, SparseArray<E>> mMap
= new ArrayMap<String, SparseArray<E>>();
public E get(String name, int uid) {
SparseArray<E> uids = mMap.get(name);
if (uids == null) return null;
@@ -58,4 +58,6 @@ public class ProcessMap<E> {
public int size() {
return mMap.size();
}
public void putAll(ProcessMap<E> other) { mMap.putAll(other.mMap); }
}

View File

@@ -115,6 +115,12 @@
<protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
<protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
<protected-broadcast android:name="android.app.action.USER_ADDED" />
<protected-broadcast android:name="android.app.action.USER_REMOVED" />
<protected-broadcast android:name="android.app.action.USER_STARTED" />
<protected-broadcast android:name="android.app.action.USER_STOPPED" />
<protected-broadcast android:name="android.app.action.USER_SWITCHED" />
<protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" />
<protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" />
<protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" />
@@ -494,6 +500,8 @@
<protected-broadcast android:name="android.app.action.ACTION_PASSWORD_FAILED" />
<protected-broadcast android:name="android.app.action.ACTION_PASSWORD_SUCCEEDED" />
<protected-broadcast android:name="com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION" />
<protected-broadcast android:name="com.android.server.ACTION_PROFILE_OFF_DEADLINE" />
<protected-broadcast android:name="com.android.server.ACTION_TURN_PROFILE_ON_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_ADDED" />
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNLOCKED" />

View File

@@ -16,5 +16,6 @@ limitations under the License.
<!-- Default text colors for car buttons when enabled/disabled. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@*android:color/car_grey_700" android:state_enabled="false"/>
<item android:color="@*android:color/car_grey_700" android:state_ux_restricted="true"/>
<item android:color="?android:attr/colorButtonNormal"/>
</selector>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- copy of switch_track_material, but with a ux restricted state -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="?attr/colorForeground"
android:alpha="?attr/disabledAlpha" />
<item android:state_ux_restricted="true"
android:color="?attr/colorForeground"
android:alpha="?attr/disabledAlpha" />
<item android:state_checked="true"
android:color="?attr/colorControlActivated" />
<item android:color="?attr/colorForeground" />
</selector>

View File

@@ -25,6 +25,22 @@ limitations under the License.
android:color="#0059B3"/>
</shape>
</item>
<item android:state_focused="true" android:state_pressed="true" android:state_ux_restricted="true">
<shape android:shape="rectangle">
<corners android:radius="@*android:dimen/car_button_radius"/>
<solid android:color="@*android:color/car_grey_300"/>
<stroke android:width="4dp"
android:color="#0059B3"/>
</shape>
</item>
<item android:state_focused="true" android:state_ux_restricted="true">
<shape android:shape="rectangle">
<corners android:radius="@*android:dimen/car_button_radius"/>
<solid android:color="@*android:color/car_grey_300"/>
<stroke android:width="8dp"
android:color="#0059B3"/>
</shape>
</item>
<item android:state_focused="true" android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="@*android:dimen/car_button_radius"/>
@@ -47,6 +63,12 @@ limitations under the License.
<solid android:color="@*android:color/car_grey_300"/>
</shape>
</item>
<item android:state_ux_restricted="true">
<shape android:shape="rectangle">
<corners android:radius="@*android:dimen/car_button_radius"/>
<solid android:color="@*android:color/car_grey_300"/>
</shape>
</item>
<item>
<ripple android:color="?android:attr/colorControlHighlight">
<item>

View File

@@ -41,7 +41,7 @@
android:right="@dimen/car_switch_track_margin_size">
<shape
android:shape="rectangle"
android:tint="@color/switch_track_material">
android:tint="@color/car_switch_track">
<corners android:radius="7dp" />
<solid android:color="@color/white_disabled_material" />
<size android:height="14dp" />

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Formatting note: terminate all comments with a period, to avoid breaking
the documentation output. To suppress comment lines from the documentation
output, insert an eat-comment element after the comment lines.
-->
<resources>
<attr name="state_ux_restricted" format="boolean"/>
</resources>

View File

@@ -4154,6 +4154,35 @@
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
<integer name="config_defaultRefreshRateInZone">0</integer>
<!-- The display uses different gamma curves for different refresh rates. It's hard for panel
vendor to tune the curves to have exact same brightness for different refresh rate. So
flicker could be observed at switch time. The issue can be observed on the screen with
even full white content at the high brightness. To prevent flickering, we support fixed
refresh rates if the display and ambient brightness are equal to or above the provided
thresholds. You can define multiple threshold levels as higher brightness environments
may have lower display brightness requirements for the flickering is visible. And the
high brightness environment could have higher threshold.
For example, fixed refresh rate if
display brightness >= disp0 && ambient brightness >= amb0
|| display brightness >= disp1 && ambient brightness >= amb1 -->
<integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate">
<!--
<item>disp0</item>
<item>disp1</item>
-->
</integer-array>
<integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate">
<!--
<item>amb0</item>
<item>amb1</item>
-->
</integer-array>
<!-- Default refresh rate in the high zone defined by brightness and ambient thresholds.
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
<integer name="config_fixedRefreshRateInHighZone">0</integer>
<!-- The type of the light sensor to be used by the display framework for things like
auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
<string name="config_displayLightSensorType" translatable="false" />

View File

@@ -3801,6 +3801,11 @@
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
<!-- For fixed refresh rate displays in high brightness-->
<java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />
<java-symbol type="array" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate" />
<java-symbol type="array" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate" />
<!-- For Auto-Brightness -->
<java-symbol type="string" name="config_displayLightSensorType" />

View File

@@ -137,13 +137,6 @@ prebuilt_etc {
filename_from_src: true,
}
prebuilt_etc {
name: "privapp_whitelist_com.android.car.companiondevicesupport",
sub_dir: "permissions",
src: "com.android.car.companiondevicesupport.xml",
filename_from_src: true,
}
prebuilt_etc {
name: "privapp_whitelist_com.google.android.car.kitchensink",
sub_dir: "permissions",
@@ -159,13 +152,6 @@ prebuilt_etc {
system_ext_specific: true,
}
prebuilt_etc {
name: "privapp_whitelist_com.android.car.floatingcardslauncher",
sub_dir: "permissions",
src: "com.android.car.floatingcardslauncher.xml",
filename_from_src: true,
}
prebuilt_etc {
name: "privapp_allowlist_com.google.android.car.networking.preferenceupdater",
sub_dir: "permissions",
@@ -186,3 +172,17 @@ prebuilt_etc {
src: "com.android.car.shell.xml",
filename_from_src: true,
}
prebuilt_etc {
name: "allowed_privapp_com.android.car.activityresolver",
sub_dir: "permissions",
src: "com.android.car.activityresolver.xml",
filename_from_src: true,
}
prebuilt_etc {
name: "allowed_privapp_com.android.car.rotary",
sub_dir: "permissions",
src: "com.android.car.rotary.xml",
filename_from_src: true,
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2019 The Android Open Source Project
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -12,14 +12,10 @@
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
~ limitations under the License.
-->
<permissions>
<privapp-permissions package="com.android.car.floatingcardslauncher">
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<privapp-permissions package="com.android.car.activityresolver">
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
</privapp-permissions>
</privapp-permissions>
</permissions>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2019 The Android Open Source Project
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -12,13 +12,11 @@
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
~ limitations under the License.
-->
<permissions>
<privapp-permissions package="com.android.car.companiondevicesupport">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.PROVIDE_TRUST_AGENT"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<privapp-permissions package="com.android.car.rotary">
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>

View File

@@ -15,7 +15,9 @@
~ limitations under the License
-->
<permissions>
<privapp-permissions package="com.android.car.shell">
<!-- CarShell now overrides the shell package and adding permission here
is ok. -->
<privapp-permissions package="com.android.shell">
<permission name="android.permission.INSTALL_PACKAGES" />
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
</privapp-permissions>

View File

@@ -51,9 +51,6 @@ public class GpsNetInitiatedHandler {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// NI verify activity for bringing up UI (not used yet)
public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
// string constants for defining data fields in NI Intent
public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id";
public static final String NI_INTENT_KEY_TITLE = "title";

View File

@@ -381,7 +381,12 @@ public class MediaRouter {
}
public Display[] getAllPresentationDisplays() {
return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
try {
return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
} catch (RuntimeException ex) {
Log.e(TAG, "Unable to get displays.", ex);
return null;
}
}
private void updatePresentationDisplays(int changedDisplayId) {
@@ -2085,6 +2090,9 @@ public class MediaRouter {
private Display choosePresentationDisplay() {
if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
Display[] displays = sStatic.getAllPresentationDisplays();
if (displays == null || displays.length == 0) {
return null;
}
// Ensure that the specified display is valid for presentations.
// This check will normally disallow the default display unless it was

View File

@@ -19,8 +19,7 @@ package android.mtp;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -32,7 +31,6 @@ import android.media.ExifInterface;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
@@ -103,8 +101,6 @@ public class MtpDatabase implements AutoCloseable {
private MtpStorageManager mManager;
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID};
private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA};
private static final String NO_MEDIA = ".nomedia";
static {
@@ -431,7 +427,7 @@ public class MtpDatabase implements AutoCloseable {
}
// Add the new file to MediaProvider
if (succeeded) {
MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
updateMediaStore(mContext, obj.getPath().toFile());
}
}
@@ -580,32 +576,8 @@ public class MtpDatabase implements AutoCloseable {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
// finally update MediaProvider
ContentValues values = new ContentValues();
values.put(Files.FileColumns.DATA, newPath.toString());
String[] whereArgs = new String[]{oldPath.toString()};
try {
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
// check if nomedia status changed
if (obj.isDir()) {
// for directories, check if renamed from something hidden to something non-hidden
if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) {
MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile());
}
} else {
// for files, check if renamed from .nomedia to something else
if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)
&& !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) {
MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile());
}
}
updateMediaStore(mContext, oldPath.toFile());
updateMediaStore(mContext, newPath.toFile());
return MtpConstants.RESPONSE_OK;
}
@@ -635,48 +607,15 @@ public class MtpDatabase implements AutoCloseable {
Log.e(TAG, "Failed to end move object");
return;
}
obj = mManager.getObject(objId);
if (!success || obj == null)
return;
// Get parent info from MediaProvider, since the id is different from MTP's
ContentValues values = new ContentValues();
Path path = newParentObj.getPath().resolve(name);
Path oldPath = oldParentObj.getPath().resolve(name);
values.put(Files.FileColumns.DATA, path.toString());
if (obj.getParent().isRoot()) {
values.put(Files.FileColumns.PARENT, 0);
} else {
int parentId = findInMedia(newParentObj, path.getParent());
if (parentId != -1) {
values.put(Files.FileColumns.PARENT, parentId);
} else {
// The new parent isn't in MediaProvider, so delete the object instead
deleteFromMedia(obj, oldPath, obj.isDir());
return;
}
}
// update MediaProvider
Cursor c = null;
String[] whereArgs = new String[]{oldPath.toString()};
try {
int parentId = -1;
if (!oldParentObj.isRoot()) {
parentId = findInMedia(oldParentObj, oldPath.getParent());
}
if (oldParentObj.isRoot() || parentId != -1) {
// Old parent exists in MediaProvider - perform a move
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
} else {
// Old parent doesn't exist - add the object
MediaStore.scanFile(mContext.getContentResolver(), path.toFile());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
updateMediaStore(mContext, oldPath.toFile());
updateMediaStore(mContext, path.toFile());
}
@VisibleForNative
@@ -699,7 +638,19 @@ public class MtpDatabase implements AutoCloseable {
if (!success) {
return;
}
MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
updateMediaStore(mContext, obj.getPath().toFile());
}
private static void updateMediaStore(@NonNull Context context, @NonNull File file) {
final ContentResolver resolver = context.getContentResolver();
// For file, check whether the file name is .nomedia or not.
// If yes, scan the parent directory to update all files in the directory.
if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(NO_MEDIA)) {
MediaStore.scanFile(resolver, file.getParentFile());
} else {
MediaStore.scanFile(resolver, file);
}
}
@VisibleForNative
@@ -940,26 +891,6 @@ public class MtpDatabase implements AutoCloseable {
deleteFromMedia(obj, obj.getPath(), obj.isDir());
}
private int findInMedia(MtpStorageManager.MtpObject obj, Path path) {
final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
int ret = -1;
Cursor c = null;
try {
c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE,
new String[]{path.toString()}, null, null);
if (c != null && c.moveToNext()) {
ret = c.getInt(0);
}
} catch (RemoteException e) {
Log.e(TAG, "Error finding " + path + " in MediaProvider");
} finally {
if (c != null)
c.close();
}
return ret;
}
private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) {
final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
try {
@@ -975,13 +906,10 @@ public class MtpDatabase implements AutoCloseable {
}
String[] whereArgs = new String[]{path.toString()};
if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) {
if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile());
}
} else {
Log.i(TAG, "Mediaprovider didn't delete " + path);
if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) == 0) {
Log.i(TAG, "MediaProvider didn't delete " + path);
}
updateMediaStore(mContext, path.toFile());
} catch (Exception e) {
Log.d(TAG, "Failed to delete " + path + " from MediaProvider");
}

View File

@@ -60,6 +60,7 @@
android:layout_width="@dimen/keyguard_security_width"
android:layout_height="@dimen/pin_entry_height"
android:gravity="center"
android:focusedByDefault="true"
app:scaledTextSize="@integer/password_text_view_scale"
android:contentDescription="@string/keyguard_accessibility_pin_area" />

View File

@@ -14,10 +14,7 @@
~ limitations under the License.
-->
<!-- Car customizations
Car has solid black background instead of a transparent one
-->
<LinearLayout
<com.android.car.ui.FocusArea
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_container"
android:layout_width="match_parent"

View File

@@ -47,6 +47,7 @@
android:layout_width="@dimen/keyguard_security_width"
android:layout_height="@dimen/pin_entry_height"
android:gravity="center"
android:focusedByDefault="true"
app:scaledTextSize="@integer/password_text_view_scale"
android:contentDescription="@string/keyguard_accessibility_pin_area" />

View File

@@ -66,7 +66,6 @@
android:src="@drawable/ic_backspace"
android:clickable="true"
android:tint="@android:color/white"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/keyboardview_keycode_delete" />
<com.android.keyguard.NumPadKey
android:id="@+id/key0"
@@ -77,7 +76,6 @@
style="@style/NumPadKeyButton.LastRow"
android:src="@drawable/ic_done"
android:tint="@android:color/white"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/keyboardview_keycode_enter" />
</merge>

View File

@@ -17,10 +17,8 @@
<resources>
<dimen name="num_pad_margin_left">112dp</dimen>
<dimen name="num_pad_margin_right">144dp</dimen>
<dimen name="num_pad_key_width">80dp</dimen>
<dimen name="num_pad_key_width">120dp</dimen>
<dimen name="num_pad_key_height">80dp</dimen>
<dimen name="num_pad_key_margin_horizontal">@*android:dimen/car_padding_5</dimen>
<dimen name="num_pad_key_margin_bottom">@*android:dimen/car_padding_5</dimen>
<dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
<dimen name="divider_height">1dp</dimen>
<dimen name="key_enter_margin_top">128dp</dimen>

View File

@@ -23,12 +23,11 @@
<item name="android:layout_width">@dimen/num_pad_key_width</item>
<item name="android:layout_height">@dimen/num_pad_key_height</item>
<item name="android:layout_marginBottom">@dimen/num_pad_key_margin_bottom</item>
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="textView">@id/pinEntry</item>
</style>
<style name="NumPadKeyButton.MiddleColumn">
<item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
<item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
</style>
<style name="NumPadKeyButton.LastRow">
@@ -36,12 +35,10 @@
</style>
<style name="NumPadKeyButton.LastRow.MiddleColumn">
<item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
<item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
</style>
<style name="KeyguardButton" parent="@android:style/Widget.DeviceDefault.Button">
<item name="android:background">@drawable/keyguard_button_background</item>
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:textColor">@color/button_text</item>
<item name="android:textAllCaps">false</item>
</style>

View File

@@ -14,18 +14,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<FrameLayout
<com.android.car.ui.FocusArea
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fullscreen_user_switcher"
android:id="@+id/user_switcher_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/car_user_switcher_background_color">
<LinearLayout
android:gravity="center">
<com.android.systemui.car.userswitcher.UserSwitcherContainer
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:background="@color/car_user_switcher_background_color"
android:orientation="vertical">
<include
@@ -45,5 +45,5 @@
android:layout_marginTop="@dimen/car_user_switcher_margin_top"/>
</FrameLayout>
</LinearLayout>
</FrameLayout>
</com.android.systemui.car.userswitcher.UserSwitcherContainer>
</com.android.car.ui.FocusArea>

View File

@@ -77,8 +77,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end"
/>
android:focusedByDefault="true"
systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end"/>
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"

View File

@@ -74,7 +74,8 @@
android:id="@+id/qs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"/>
android:background="@null"
android:focusedByDefault="true"/>
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"

View File

@@ -35,7 +35,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent"
app:shouldRestoreFocus="false"/>
<View
android:id="@+id/scrim"

View File

@@ -22,10 +22,6 @@
android:layout_height="match_parent"
android:background="@color/notification_shade_background_color">
<com.android.car.ui.FocusParkingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<View
android:id="@+id/glass_pane"
android:layout_width="match_parent"
@@ -37,20 +33,15 @@
app:layout_constraintTop_toTopOf="parent"
/>
<com.android.car.ui.FocusArea
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/notifications"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/notification_shade_list_padding_bottom"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/notifications"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/notification_shade_list_padding_bottom"/>
</com.android.car.ui.FocusArea>
app:layout_constraintTop_toTopOf="parent"/>
<include layout="@layout/notification_handle_bar"/>

View File

@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<FrameLayout
<com.android.car.ui.FocusArea
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_container"
android:layout_width="match_parent"

View File

@@ -22,25 +22,29 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.car.ui.FocusParkingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/notification_panel_container"
android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/notification_panel_container"
android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/keyguard_container" />
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/keyguard_container" />
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/car_fullscreen_user_switcher"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/car_fullscreen_user_switcher"/>
<ViewStub android:id="@+id/user_switching_dialog_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/car_user_switching_dialog"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/car_user_switching_dialog"/>
</FrameLayout>

View File

@@ -147,6 +147,11 @@ public class CarKeyguardViewController extends OverlayViewController implements
registerUserSwitchedListener();
}
@Override
protected int getFocusAreaViewId() {
return R.id.keyguard_container;
}
@Override
protected boolean shouldShowNavigationBarInsets() {
return true;
@@ -233,9 +238,6 @@ public class CarKeyguardViewController extends OverlayViewController implements
public void setOccluded(boolean occluded, boolean animate) {
mIsOccluded = occluded;
getOverlayViewGlobalStateController().setOccluded(occluded);
if (!occluded) {
reset(/* hideBouncerWhenShowing= */ false);
}
}
@Override

View File

@@ -28,6 +28,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -165,6 +166,10 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mEnableHeadsUpNotificationWhenNotificationShadeOpen = mResources.getBoolean(
com.android.car.notification.R.bool
.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
// Inflate view on instantiation to properly initialize listeners even if panel has
// not been opened.
getOverlayViewGlobalStateController().inflateView(this);
}
// CommandQueue.Callbacks
@@ -217,6 +222,11 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mNotificationVisibilityLogger.stop();
}
@Override
protected int getFocusAreaViewId() {
return R.id.notification_container;
}
@Override
protected boolean shouldShowNavigationBarInsets() {
return true;
@@ -239,12 +249,26 @@ public class NotificationPanelViewController extends OverlayPanelViewController
/** Reinflates the view. */
public void reinflate() {
// Do not reinflate the view if it has not been inflated at all.
if (!isInflated()) return;
ViewGroup container = (ViewGroup) getLayout();
container.removeView(mNotificationView);
mNotificationView = (CarNotificationView) LayoutInflater.from(mContext).inflate(
R.layout.notification_center_activity, container,
/* attachToRoot= */ false);
mNotificationView.setKeyEventHandler(
event -> {
if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
return false;
}
if (event.getAction() == KeyEvent.ACTION_UP && isPanelExpanded()) {
toggle();
}
return true;
});
container.addView(mNotificationView);
onNotificationViewInflated();

View File

@@ -22,6 +22,7 @@ import android.car.Car;
import android.car.user.CarUserManager;
import android.content.Context;
import android.content.res.Resources;
import android.view.KeyEvent;
import android.view.View;
import androidx.recyclerview.widget.GridLayoutManager;
@@ -67,6 +68,19 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController
@Override
protected void onFinishInflate() {
// Intercept back button.
UserSwitcherContainer container = getLayout().findViewById(R.id.container);
container.setKeyEventHandler(event -> {
if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
return false;
}
if (event.getAction() == KeyEvent.ACTION_UP && getLayout().isVisibleToUser()) {
stop();
}
return true;
});
// Initialize user grid.
mUserGridView = getLayout().findViewById(R.id.user_grid);
GridLayoutManager layoutManager = new GridLayoutManager(mContext,
@@ -77,9 +91,14 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController
registerCarUserManagerIfPossible();
}
@Override
protected int getFocusAreaViewId() {
return R.id.user_switcher_container;
}
@Override
protected boolean shouldFocusWindow() {
return false;
return true;
}
@Override

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.car.userswitcher;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/** Container for the user switcher which intercepts the key events. */
public class UserSwitcherContainer extends LinearLayout {
private KeyEventHandler mKeyEventHandler;
public UserSwitcherContainer(@NonNull Context context) {
super(context);
}
public UserSwitcherContainer(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public UserSwitcherContainer(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public UserSwitcherContainer(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (super.dispatchKeyEvent(event)) {
return true;
}
if (mKeyEventHandler != null) {
return mKeyEventHandler.dispatchKeyEvent(event);
}
return false;
}
/** Sets a {@link KeyEventHandler} to help interact with the notification panel. */
public void setKeyEventHandler(KeyEventHandler keyEventHandler) {
mKeyEventHandler = keyEventHandler;
}
/** An interface to help interact with the notification panel. */
public interface KeyEventHandler {
/** Allows handling of a {@link KeyEvent} if it wasn't already handled by the superclass. */
boolean dispatchKeyEvent(KeyEvent event);
}
}

View File

@@ -17,12 +17,17 @@
package com.android.systemui.car.window;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.WindowInsets;
import androidx.annotation.IdRes;
import com.android.car.ui.FocusArea;
/**
* Owns a {@link View} that is present in SystemUIOverlayWindow.
*/
@@ -128,6 +133,66 @@ public class OverlayViewController {
return mOverlayViewGlobalStateController;
}
/** Returns whether the view controlled by this controller is visible. */
public final boolean isVisible() {
return mLayout.getVisibility() == View.VISIBLE;
}
/**
* Returns the ID of the focus area that should receive focus when this view is the
* topmost view or {@link View#NO_ID} if there is no focus area.
*/
@IdRes
protected int getFocusAreaViewId() {
return View.NO_ID;
}
/** Returns whether the view controlled by this controller has rotary focus. */
protected final boolean hasRotaryFocus() {
return !mLayout.isInTouchMode() && mLayout.hasFocus();
}
/**
* Sets whether this view allows rotary focus. This should be set to {@code true} for the
* topmost layer in the overlay window and {@code false} for the others.
*/
public void setAllowRotaryFocus(boolean allowRotaryFocus) {
if (!isInflated()) {
return;
}
if (!(mLayout instanceof ViewGroup)) {
return;
}
ViewGroup viewGroup = (ViewGroup) mLayout;
viewGroup.setDescendantFocusability(allowRotaryFocus
? ViewGroup.FOCUS_BEFORE_DESCENDANTS
: ViewGroup.FOCUS_BLOCK_DESCENDANTS);
}
/**
* Refreshes the rotary focus in this view if we are in rotary mode. If the view already has
* rotary focus, it leaves the focus alone. Returns {@code true} if a new view was focused.
*/
public boolean refreshRotaryFocusIfNeeded() {
if (mLayout.isInTouchMode()) {
return false;
}
if (hasRotaryFocus()) {
return false;
}
View view = mLayout.findViewById(getFocusAreaViewId());
if (view == null || !(view instanceof FocusArea)) {
return mLayout.requestFocus();
}
FocusArea focusArea = (FocusArea) view;
return focusArea.performAccessibilityAction(ACTION_FOCUS, /* arguments= */ null);
}
/**
* Returns {@code true} if heads up notifications should be displayed over this view.
*/

View File

@@ -29,6 +29,7 @@ import androidx.annotation.VisibleForTesting;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -120,6 +121,7 @@ public class OverlayViewGlobalStateController {
refreshWindowFocus();
refreshNavigationBarVisibility();
refreshStatusBarVisibility();
refreshRotaryFocusIfNeeded();
Log.d(TAG, "Content shown: " + viewController.getClass().getName());
debugLog();
@@ -193,6 +195,7 @@ public class OverlayViewGlobalStateController {
refreshWindowFocus();
refreshNavigationBarVisibility();
refreshStatusBarVisibility();
refreshRotaryFocusIfNeeded();
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(false);
@@ -254,6 +257,17 @@ public class OverlayViewGlobalStateController {
}
}
private void refreshRotaryFocusIfNeeded() {
for (OverlayViewController controller : mZOrderVisibleSortedMap.values()) {
boolean isTop = Objects.equals(controller, mHighestZOrder);
controller.setAllowRotaryFocus(isTop);
}
if (!mZOrderVisibleSortedMap.isEmpty()) {
mHighestZOrder.refreshRotaryFocusIfNeeded();
}
}
/** Returns {@code true} is the window is visible. */
public boolean isWindowVisible() {
return mSystemUIOverlayWindowController.isWindowVisible();

View File

@@ -16,14 +16,15 @@
package com.android.systemui.wm;
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindowManager;
import android.view.InsetsController;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.WindowInsets;
@@ -48,30 +49,32 @@ public class DisplaySystemBarsController extends DisplayImeController {
private static final String TAG = "DisplaySystemBarsController";
private final Context mContext;
private final Handler mHandler;
private SparseArray<PerDisplay> mPerDisplaySparseArray;
@Inject
public DisplaySystemBarsController(
SystemWindows syswin,
Context context,
IWindowManager wmService,
DisplayController displayController,
@Main Handler mainHandler,
TransactionPool transactionPool) {
super(syswin, displayController, mainHandler, transactionPool);
super(wmService, displayController, mainHandler::post, transactionPool);
mContext = context;
mHandler = mainHandler;
}
@Override
public void onDisplayAdded(int displayId) {
PerDisplay pd = new PerDisplay(displayId);
try {
mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
pd.register();
// Lazy loading policy control filters instead of during boot.
if (mPerDisplaySparseArray == null) {
mPerDisplaySparseArray = new SparseArray<>();
BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
BarControlPolicy.reloadFromSetting(mContext);
BarControlPolicy.registerContentObserver(mContext, mHandler, () -> {
int size = mPerDisplaySparseArray.size();
for (int i = 0; i < size; i++) {
mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
@@ -84,7 +87,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
@Override
public void onDisplayRemoved(int displayId) {
try {
mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -100,11 +103,10 @@ public class DisplaySystemBarsController extends DisplayImeController {
String mPackageName;
PerDisplay(int displayId) {
super(displayId,
mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
super(displayId, mDisplayController.getDisplayLayout(displayId).rotation());
mDisplayId = displayId;
mInsetsController = new InsetsController(
new DisplaySystemBarsInsetsControllerHost(mHandler, this));
new DisplaySystemBarsInsetsControllerHost(mHandler, mInsetsControllerImpl));
}
@Override
@@ -120,13 +122,6 @@ public class DisplaySystemBarsController extends DisplayImeController {
}
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
super.insetsControlChanged(insetsState, activeControls);
mInsetsController.onControlsChanged(activeControls);
}
@Override
public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
@@ -166,7 +161,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
showInsets(barVisibilities[0], /* fromIme= */ false);
hideInsets(barVisibilities[1], /* fromIme= */ false);
try {
mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to update window manager service.");
}

View File

@@ -214,6 +214,16 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
verify(mSystemUIOverlayWindowController).setWindowVisible(true);
}
@Test
public void showView_nothingAlreadyShown_newHighestZOrder_isVisible() {
setupOverlayViewController1();
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsKey(
OVERLAY_VIEW_CONTROLLER_1_Z_ORDER)).isTrue();
}
@Test
public void showView_nothingAlreadyShown_newHighestZOrder() {
setupOverlayViewController1();
@@ -225,13 +235,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
}
@Test
public void showView_nothingAlreadyShown_newHighestZOrder_isVisible() {
public void showView_nothingAlreadyShown_descendantsFocusable() {
setupOverlayViewController1();
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsKey(
OVERLAY_VIEW_CONTROLLER_1_Z_ORDER)).isTrue();
verify(mOverlayViewController1).setAllowRotaryFocus(true);
}
@Test
@@ -331,6 +340,30 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
OVERLAY_VIEW_CONTROLLER_2_Z_ORDER).toArray());
}
@Test
public void showView_newHighestZOrder_topDescendantsFocusable() {
setupOverlayViewController1();
setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
verify(mOverlayViewController1).setAllowRotaryFocus(false);
verify(mOverlayViewController2).setAllowRotaryFocus(true);
}
@Test
public void showView_newHighestZOrder_refreshTopFocus() {
setupOverlayViewController1();
setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
verify(mOverlayViewController1, never()).refreshRotaryFocusIfNeeded();
verify(mOverlayViewController2).refreshRotaryFocusIfNeeded();
}
@Test
public void showView_oldHighestZOrder() {
setupOverlayViewController2();
@@ -345,9 +378,9 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true);
when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
@@ -360,11 +393,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false);
when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -374,9 +408,9 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true);
when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
@@ -389,11 +423,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false);
when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -425,6 +460,30 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
OVERLAY_VIEW_CONTROLLER_2_Z_ORDER).toArray());
}
@Test
public void showView_oldHighestZOrder_topDescendantsFocusable() {
setupOverlayViewController1();
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
verify(mOverlayViewController1).setAllowRotaryFocus(false);
verify(mOverlayViewController2).setAllowRotaryFocus(true);
}
@Test
public void showView_oldHighestZOrder_refreshTopFocus() {
setupOverlayViewController1();
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
verify(mOverlayViewController1, never()).refreshRotaryFocusIfNeeded();
verify(mOverlayViewController2).refreshRotaryFocusIfNeeded();
}
@Test
public void showView_somethingAlreadyShown_windowVisibleNotCalled() {
setupOverlayViewController1();
@@ -577,10 +636,10 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_newHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
@@ -593,10 +652,10 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_newHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true);
reset(mWindowInsetsController);
@@ -609,10 +668,10 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
@@ -625,10 +684,10 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true);
reset(mWindowInsetsController);
@@ -668,10 +727,10 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
@@ -684,11 +743,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
@@ -699,10 +759,10 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
@@ -715,11 +775,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
setupOverlayViewController1();
setupOverlayViewController2();
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
@@ -917,7 +978,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
View layout = overlayViewController.getLayout();
reset(mSystemUIOverlayWindowController);
reset(overlayViewController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
when(overlayViewController.getLayout()).thenReturn(layout);
when(overlayViewController.isInflated()).thenReturn(true);
}
}

View File

@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.car.settings.CarSettings;
import android.os.Handler;
@@ -29,6 +30,7 @@ import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.IWindowManager;
import android.view.Surface;
import androidx.test.filters.SmallTest;
@@ -60,15 +62,20 @@ public class DisplaySystemBarsControllerTest extends SysuiTestCase {
private Handler mHandler;
@Mock
private TransactionPool mTransactionPool;
@Mock
private DisplayLayout mDisplayLayout;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mSystemWindows.mContext = mContext;
mSystemWindows.mWmService = mIWindowManager;
when(mDisplayLayout.rotation()).thenReturn(Surface.ROTATION_0);
when(mDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mDisplayLayout);
mController = new DisplaySystemBarsController(
mSystemWindows,
mContext,
mIWindowManager,
mDisplayController,
mHandler,
mTransactionPool
@@ -81,7 +88,8 @@ public class DisplaySystemBarsControllerTest extends SysuiTestCase {
mController.onDisplayAdded(DISPLAY_ID);
verify(mIWindowManager).setDisplayWindowInsetsController(
eq(DISPLAY_ID), any(DisplaySystemBarsController.PerDisplay.class));
eq(DISPLAY_ID),
any(DisplayImeController.PerDisplay.DisplayWindowInsetsControllerImpl.class));
}
@Test

View File

@@ -41,6 +41,15 @@ import java.util.List;
public class InstallSuccess extends AlertActivity {
private static final String LOG_TAG = InstallSuccess.class.getSimpleName();
@Nullable
private PackageUtil.AppSnippet mAppSnippet;
@Nullable
private String mAppPackageName;
@Nullable
private Intent mLaunchIntent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -55,59 +64,73 @@ public class InstallSuccess extends AlertActivity {
Intent intent = getIntent();
ApplicationInfo appInfo =
intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mAppPackageName = appInfo.packageName;
Uri packageURI = intent.getData();
// Set header icon and title
PackageUtil.AppSnippet as;
PackageManager pm = getPackageManager();
if ("package".equals(packageURI.getScheme())) {
as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
mAppSnippet = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
pm.getApplicationIcon(appInfo));
} else {
File sourceFile = new File(packageURI.getPath());
as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
mAppSnippet = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
}
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null,
null);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
(ignored, ignored2) -> {
if (appInfo.packageName != null) {
Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
}
finish();
}, null);
setupAlert();
requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
// Enable or disable "launch" button
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(
appInfo.packageName);
boolean enabled = false;
if (launchIntent != null) {
List<ResolveInfo> list = getPackageManager().queryIntentActivities(launchIntent,
0);
if (list != null && list.size() > 0) {
enabled = true;
}
}
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppPackageName);
Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
try {
startActivity(launchIntent);
} catch (ActivityNotFoundException | SecurityException e) {
Log.e(LOG_TAG, "Could not start activity", e);
bindUi();
}
}
@Override
protected void onResume() {
super.onResume();
bindUi();
}
private void bindUi() {
if (mAppSnippet == null) {
return;
}
mAlert.setIcon(mAppSnippet.icon);
mAlert.setTitle(mAppSnippet.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null,
null);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
(ignored, ignored2) -> {
if (mAppPackageName != null) {
Log.i(LOG_TAG, "Finished installing " + mAppPackageName);
}
finish();
});
} else {
launchButton.setEnabled(false);
}, null);
setupAlert();
requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
// Enable or disable "launch" button
boolean enabled = false;
if (mLaunchIntent != null) {
List<ResolveInfo> list = getPackageManager().queryIntentActivities(mLaunchIntent,
0);
if (list != null && list.size() > 0) {
enabled = true;
}
}
Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
try {
startActivity(mLaunchIntent);
} catch (ActivityNotFoundException | SecurityException e) {
Log.e(LOG_TAG, "Could not start activity", e);
}
finish();
});
} else {
launchButton.setEnabled(false);
}
}
}

View File

@@ -343,17 +343,19 @@ public class PackageInstallerActivity extends AlertActivity {
if (!wasSetUp) {
return;
}
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
bindUi();
checkIfAllowedAndInitiateInstall();
}
@Override
protected void onResume() {
super.onResume();
if (mAppSnippet != null) {
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
bindUi();
checkIfAllowedAndInitiateInstall();
}
if (mOk != null) {
mOk.setEnabled(mEnableOk);
}

View File

@@ -25,6 +25,7 @@ import static android.os.BatteryManager.EXTRA_LEVEL;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
import static android.os.BatteryManager.EXTRA_PLUGGED;
import static android.os.BatteryManager.EXTRA_PRESENT;
import static android.os.BatteryManager.EXTRA_STATUS;
import android.content.Context;
@@ -50,14 +51,16 @@ public class BatteryStatus {
public final int plugged;
public final int health;
public final int maxChargingWattage;
public final boolean present;
public BatteryStatus(int status, int level, int plugged, int health,
int maxChargingWattage) {
int maxChargingWattage, boolean present) {
this.status = status;
this.level = level;
this.plugged = plugged;
this.health = health;
this.maxChargingWattage = maxChargingWattage;
this.present = present;
}
public BatteryStatus(Intent batteryChangedIntent) {
@@ -65,6 +68,7 @@ public class BatteryStatus {
plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0);
level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0);
health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true);
final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT,
-1);

View File

@@ -205,7 +205,6 @@ public class LocalMediaManager implements BluetoothCallback {
void dispatchDeviceListUpdate() {
final List<MediaDevice> mediaDevices = new ArrayList<>(mMediaDevices);
Collections.sort(mediaDevices, COMPARATOR);
for (DeviceCallback callback : getCallbacks()) {
callback.onDeviceListUpdate(mediaDevices);
}
@@ -465,6 +464,7 @@ public class LocalMediaManager implements BluetoothCallback {
synchronized (mMediaDevicesLock) {
mMediaDevices.clear();
mMediaDevices.addAll(devices);
Collections.sort(devices, COMPARATOR);
// Add disconnected bluetooth devices only when phone output device is available.
for (MediaDevice device : devices) {
final int type = device.getDeviceType();

View File

@@ -7,13 +7,17 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
// used both for the android_app and android_library
shell_srcs = ["src/**/*.java",":dumpstate_aidl"]
shell_static_libs = ["androidx.legacy_legacy-support-v4"]
android_app {
name: "Shell",
srcs: ["src/**/*.java",":dumpstate_aidl"],
srcs: shell_srcs,
aidl: {
include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
},
static_libs: ["androidx.legacy_legacy-support-v4"],
static_libs: shell_static_libs,
platform_apis: true,
certificate: "platform",
privileged: true,
@@ -21,3 +25,17 @@ android_app {
include_filter: ["com.android.shell.*"],
},
}
// A library for product type like auto to create a new shell package
// with product specific permissions.
android_library {
name: "Shell-package-library",
srcs: shell_srcs,
aidl: {
include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
},
resource_dirs: ["res"],
static_libs: shell_static_libs,
platform_apis: true,
manifest: "AndroidManifest.xml",
}

View File

@@ -803,7 +803,7 @@ public class BugreportProgressService extends Service {
intent.setClass(context, BugreportProgressService.class);
intent.putExtra(EXTRA_ID, info.id);
return PendingIntent.getService(context, info.id, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
/**
@@ -1263,7 +1263,7 @@ public class BugreportProgressService extends Service {
.setTicker(title)
.setContentText(content)
.setContentIntent(PendingIntent.getService(mContext, info.id, shareIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.setOnlyAlertOnce(false)
.setDeleteIntent(newCancelIntent(mContext, info));

View File

@@ -30,7 +30,7 @@ import java.io.PrintWriter;
*/
@ProvidesInterface(version = FalsingManager.VERSION)
public interface FalsingManager {
int VERSION = 4;
int VERSION = 5;
void onSuccessfulUnlock();
@@ -42,7 +42,8 @@ public interface FalsingManager {
boolean isUnlockingDisabled();
boolean isFalseTouch();
/** Returns true if the gesture should be rejected. */
boolean isFalseTouch(int interactionType);
void onNotificatonStopDraggingDown();

View File

@@ -14,16 +14,16 @@
package com.android.systemui.plugins.statusbar;
import com.android.systemui.plugins.annotations.DependsOn;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.systemui.plugins.annotations.DependsOn;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@ProvidesInterface(version = NotificationSwipeActionHelper.VERSION)
@DependsOn(target = SnoozeOption.class)
public interface NotificationSwipeActionHelper {
@@ -52,7 +52,8 @@ public interface NotificationSwipeActionHelper {
public boolean isDismissGesture(MotionEvent ev);
public boolean isFalseGesture(MotionEvent ev);
/** Returns true if the gesture should be rejected. */
boolean isFalseGesture();
public boolean swipedFarEnough(float translation, float viewSize);

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="24dp"
android:viewportWidth="12.0"
android:viewportHeight="24.0">
<path
android:pathData="M10.404,2.4L8.4,2.4L8.4,0L3.6,0L3.6,2.4L1.596,2.4C0.72,2.4 0,3.12 0,3.996L0,22.392C0,23.28 0.72,24 1.596,24L10.392,24C11.28,24 12,23.28 12,22.404L12,3.996C12,3.12 11.28,2.4 10.404,2.4ZM7.14,19.14L4.86,19.14L4.86,16.86L7.14,16.86L7.14,19.14ZM8.76,12.828C8.76,12.828 8.304,13.332 7.956,13.68C7.38,14.256 6.96,15.06 6.96,15.6L5.04,15.6C5.04,14.604 5.592,13.776 6.156,13.2L7.272,12.072C7.596,11.748 7.8,11.292 7.8,10.8C7.8,9.804 6.996,9 6,9C5.004,9 4.2,9.804 4.2,10.8L2.4,10.8C2.4,8.808 4.008,7.2 6,7.2C7.992,7.2 9.6,8.808 9.6,10.8C9.6,11.592 9.276,12.312 8.76,12.828L8.76,12.828Z"
android:fillColor="#ffffff" />
</vector>

View File

@@ -581,4 +581,9 @@
<integer name="controls_max_columns_adjust_below_width_dp">320</integer>
<!-- If the config font scale is >= this value, potentially adjust the number of columns-->
<item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item>
<!-- Whether or not to show a notification for an unknown battery state -->
<bool name="config_showNotificationForUnknownBatteryState">false</bool>
<!-- content URL in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false -->
<string translatable="false" name="config_batteryStateUnknownUrl"></string>
</resources>

View File

@@ -437,6 +437,8 @@
<string name="accessibility_battery_three_bars">Battery three bars.</string>
<!-- Content description of the battery when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_full">Battery full.</string>
<!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_unknown">Battery percentage unknown.</string>
<!-- Content description of the phone signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_no_phone">No phone.</string>
@@ -2870,4 +2872,11 @@
<string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string>
<!-- Title for pairing item [CHAR LIMIT=60] -->
<string name="media_output_dialog_pairing_new">Pair new device</string>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
<string name="battery_state_unknown_notification_title">Problem reading your battery meter</string>
<!-- Text to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
<string name="battery_state_unknown_notification_text">Tap for more information</string>
</resources>

View File

@@ -1696,7 +1696,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
// Take a guess at initial SIM state, battery status and PLMN until we get an update
mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0);
mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0, true);
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
@@ -2563,6 +2563,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean wasPluggedIn = old.isPluggedIn();
final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
&& (old.status != current.status);
final boolean nowPresent = current.present;
final boolean wasPresent = old.present;
// change in plug state is always interesting
if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) {
@@ -2584,6 +2586,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return true;
}
// Battery either showed up or disappeared
if (wasPresent != nowPresent) {
return true;
}
return false;
}

View File

@@ -33,6 +33,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
@@ -95,12 +96,15 @@ public class BatteryMeterView extends LinearLayout implements
private int mTextColor;
private int mLevel;
private int mShowPercentMode = MODE_DEFAULT;
private boolean mForceShowPercent;
private boolean mShowPercentAvailable;
// Some places may need to show the battery conditionally, and not obey the tuner
private boolean mIgnoreTunerUpdates;
private boolean mIsSubscribedForTunerUpdates;
private boolean mCharging;
// Error state where we know nothing about the current battery state
private boolean mBatteryStateUnknown;
// Lazily-loaded since this is expected to be a rare-if-ever state
private Drawable mUnknownStateDrawable;
private DualToneHandler mDualToneHandler;
private int mUser;
@@ -350,6 +354,11 @@ public class BatteryMeterView extends LinearLayout implements
}
private void updatePercentText() {
if (mBatteryStateUnknown) {
setContentDescription(getContext().getString(R.string.accessibility_battery_unknown));
return;
}
if (mBatteryController == null) {
return;
}
@@ -390,9 +399,13 @@ public class BatteryMeterView extends LinearLayout implements
final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System
.getIntForUser(getContext().getContentResolver(),
SHOW_BATTERY_PERCENT, 0, mUser));
boolean shouldShow =
(mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
|| mShowPercentMode == MODE_ON
|| mShowPercentMode == MODE_ESTIMATE;
shouldShow = shouldShow && !mBatteryStateUnknown;
if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
|| mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE) {
if (shouldShow) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mPercentageStyleId != 0) { // Only set if specified as attribute
@@ -418,6 +431,32 @@ public class BatteryMeterView extends LinearLayout implements
scaleBatteryMeterViews();
}
private Drawable getUnknownStateDrawable() {
if (mUnknownStateDrawable == null) {
mUnknownStateDrawable = mContext.getDrawable(R.drawable.ic_battery_unknown);
mUnknownStateDrawable.setTint(mTextColor);
}
return mUnknownStateDrawable;
}
@Override
public void onBatteryUnknownStateChanged(boolean isUnknown) {
if (mBatteryStateUnknown == isUnknown) {
return;
}
mBatteryStateUnknown = isUnknown;
if (mBatteryStateUnknown) {
mBatteryIconView.setImageDrawable(getUnknownStateDrawable());
} else {
mBatteryIconView.setImageDrawable(mDrawable);
}
updateShowPercent();
}
/**
* Looks up the scale factor for status bar icons and scales the battery view by that amount.
*/
@@ -458,6 +497,10 @@ public class BatteryMeterView extends LinearLayout implements
if (mBatteryPercentView != null) {
mBatteryPercentView.setTextColor(singleToneColor);
}
if (mUnknownStateDrawable != null) {
mUnknownStateDrawable.setTint(singleToneColor);
}
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -467,8 +510,8 @@ public class BatteryMeterView extends LinearLayout implements
pw.println(" mDrawable.getPowerSave: " + powerSave);
pw.println(" mBatteryPercentView.getText(): " + percent);
pw.println(" mTextColor: #" + Integer.toHexString(mTextColor));
pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown);
pw.println(" mLevel: " + mLevel);
pw.println(" mForceShowPercent: " + mForceShowPercent);
}
private final class SettingObserver extends ContentObserver {

View File

@@ -16,6 +16,8 @@
package com.android.systemui;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -697,14 +699,15 @@ public class SwipeHelper implements Gefingerpoken {
float translation = getTranslation(mCurrView);
return ev.getActionMasked() == MotionEvent.ACTION_UP
&& !mFalsingManager.isUnlockingDisabled()
&& !isFalseGesture(ev) && (swipedFastEnough() || swipedFarEnough())
&& !isFalseGesture() && (swipedFastEnough() || swipedFarEnough())
&& mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0);
}
public boolean isFalseGesture(MotionEvent ev) {
/** Returns true if the gesture should be rejected. */
public boolean isFalseGesture() {
boolean falsingDetected = mCallback.isAntiFalsingNeeded();
if (mFalsingManager.isClassifierEnabled()) {
falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(NOTIFICATION_DISMISS);
} else {
falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
}

View File

@@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.LogBufferFreezer;
import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
import com.android.systemui.statusbar.policy.BatteryStateNotifier;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,18 +45,21 @@ public class SystemUIService extends Service {
private final DumpHandler mDumpHandler;
private final BroadcastDispatcher mBroadcastDispatcher;
private final LogBufferFreezer mLogBufferFreezer;
private final BatteryStateNotifier mBatteryStateNotifier;
@Inject
public SystemUIService(
@Main Handler mainHandler,
DumpHandler dumpHandler,
BroadcastDispatcher broadcastDispatcher,
LogBufferFreezer logBufferFreezer) {
LogBufferFreezer logBufferFreezer,
BatteryStateNotifier batteryStateNotifier) {
super();
mMainHandler = mainHandler;
mDumpHandler = dumpHandler;
mBroadcastDispatcher = broadcastDispatcher;
mLogBufferFreezer = logBufferFreezer;
mBatteryStateNotifier = batteryStateNotifier;
}
@Override
@@ -68,6 +72,11 @@ public class SystemUIService extends Service {
// Finish initializing dump logic
mLogBufferFreezer.attach(mBroadcastDispatcher);
// If configured, set up a battery notification
if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {
mBatteryStateNotifier.startListening();
}
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
throw new RuntimeException();

View File

@@ -160,12 +160,12 @@ public class AuthBiometricFaceView extends AuthBiometricView {
@Override
protected void handleResetAfterError() {
resetErrorView(mContext, mIndicatorView);
resetErrorView();
}
@Override
protected void handleResetAfterHelp() {
resetErrorView(mContext, mIndicatorView);
resetErrorView();
}
@Override
@@ -185,7 +185,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
(newState == STATE_AUTHENTICATING && mSize == AuthDialog.SIZE_MEDIUM)) {
resetErrorView(mContext, mIndicatorView);
resetErrorView();
}
// Do this last since the state variable gets updated.
@@ -204,9 +204,8 @@ public class AuthBiometricFaceView extends AuthBiometricView {
super.onAuthenticationFailed(failureReason);
}
static void resetErrorView(Context context, TextView textView) {
textView.setTextColor(context.getResources().getColor(
R.color.biometric_dialog_gray, context.getTheme()));
textView.setVisibility(View.INVISIBLE);
private void resetErrorView() {
mIndicatorView.setTextColor(mTextColorHint);
mIndicatorView.setVisibility(View.INVISIBLE);
}
}

View File

@@ -78,7 +78,7 @@ public class AuthBiometricFingerprintView extends AuthBiometricView {
private void showTouchSensorString() {
mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor);
mIndicatorView.setTextColor(R.color.biometric_dialog_gray);
mIndicatorView.setTextColor(mTextColorHint);
}
private void updateIcon(int lastState, int newState) {

View File

@@ -82,7 +82,7 @@ public abstract class AuthBiometricView extends LinearLayout {
* Authenticated, dialog animating away soon.
*/
protected static final int STATE_AUTHENTICATED = 6;
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP,
STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED})
@@ -155,8 +155,8 @@ public abstract class AuthBiometricView extends LinearLayout {
private final Injector mInjector;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final int mTextColorError;
private final int mTextColorHint;
protected final int mTextColorError;
protected final int mTextColorHint;
private AuthPanelController mPanelController;
private Bundle mBiometricPromptBundle;
@@ -169,7 +169,7 @@ public abstract class AuthBiometricView extends LinearLayout {
private TextView mSubtitleView;
private TextView mDescriptionView;
protected ImageView mIconView;
@VisibleForTesting protected TextView mIndicatorView;
protected TextView mIndicatorView;
@VisibleForTesting Button mNegativeButton;
@VisibleForTesting Button mPositiveButton;
@VisibleForTesting Button mTryAgainButton;

View File

@@ -43,6 +43,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -623,7 +624,8 @@ class Bubble implements BubbleViewProvider {
private int getUid(final Context context) {
if (mAppUid != -1) return mAppUid;
final PackageManager pm = context.getPackageManager();
final PackageManager pm = StatusBar.getPackageManagerForUser(context,
mUser.getIdentifier());
if (pm == null) return -1;
try {
final ApplicationInfo info = pm.getApplicationInfo(mShortcutInfo.getPackage(), 0);

View File

@@ -1386,10 +1386,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
mDataRepository.removeBubbles(bubblesToBeRemovedFromRepository);
if (update.addedBubble != null && mStackView != null) {
mDataRepository.addBubble(mCurrentUserId, update.addedBubble);
mDataRepository.addBubble(update.addedBubble);
mStackView.addBubble(update.addedBubble);
}
@@ -1400,7 +1400,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// At this point, the correct bubbles are inflated in the stack.
// Make sure the order in bubble data is reflected in bubble row.
if (update.orderChanged && mStackView != null) {
mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
mDataRepository.addBubbles(update.bubbles);
mStackView.updateBubbleOrder(update.bubbles);
}

View File

@@ -16,7 +16,6 @@
package com.android.systemui.bubbles
import android.annotation.SuppressLint
import android.annotation.UserIdInt
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
@@ -51,31 +50,31 @@ internal class BubbleDataRepository @Inject constructor(
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
*/
fun addBubble(@UserIdInt userId: Int, bubble: Bubble) = addBubbles(userId, listOf(bubble))
fun addBubble(bubble: Bubble) = addBubbles(listOf(bubble))
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
*/
fun addBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
fun addBubbles(bubbles: List<Bubble>) {
if (DEBUG) Log.d(TAG, "adding ${bubbles.size} bubbles")
val entities = transform(userId, bubbles).also(volatileRepository::addBubbles)
val entities = transform(bubbles).also(volatileRepository::addBubbles)
if (entities.isNotEmpty()) persistToDisk()
}
/**
* Removes the bubbles from memory, then persists the snapshot to disk asynchronously.
*/
fun removeBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
fun removeBubbles(bubbles: List<Bubble>) {
if (DEBUG) Log.d(TAG, "removing ${bubbles.size} bubbles")
val entities = transform(userId, bubbles).also(volatileRepository::removeBubbles)
val entities = transform(bubbles).also(volatileRepository::removeBubbles)
if (entities.isNotEmpty()) persistToDisk()
}
private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> {
private fun transform(bubbles: List<Bubble>): List<BubbleEntity> {
return bubbles.mapNotNull { b ->
BubbleEntity(
userId,
b.user.identifier,
b.packageName,
b.metadataShortcutId ?: return@mapNotNull null,
b.key,

View File

@@ -48,6 +48,7 @@ import com.android.internal.graphics.ColorUtils;
import com.android.launcher3.icons.BitmapInfo;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.StatusBar;
import java.lang.ref.WeakReference;
import java.util.List;
@@ -146,7 +147,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
}
// App name & app icon
PackageManager pm = c.getPackageManager();
PackageManager pm = StatusBar.getPackageManagerForUser(
c, b.getUser().getIdentifier());
ApplicationInfo appInfo;
Drawable badgedIcon;
Drawable appIcon;

View File

@@ -70,7 +70,7 @@ public class FalsingManagerFake implements FalsingManager {
}
@Override
public boolean isFalseTouch() {
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
return mIsFalseTouch;
}

View File

@@ -262,7 +262,7 @@ public class FalsingManagerImpl implements FalsingManager {
/**
* @return true if the classifier determined that this is not a human interacting with the phone
*/
public boolean isFalseTouch() {
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
if (FalsingLog.ENABLED) {
// We're getting some false wtfs from touches that happen after the device went
// to sleep. Only report missing sessions that happen when the device is interactive.

View File

@@ -187,8 +187,8 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
}
@Override
public boolean isFalseTouch() {
return mInternalFalsingManager.isFalseTouch();
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
return mInternalFalsingManager.isFalseTouch(interactionType);
}
@Override

View File

@@ -189,7 +189,8 @@ public class BrightLineFalsingManager implements FalsingManager {
}
@Override
public boolean isFalseTouch() {
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
mDataProvider.setInteractionType(interactionType);
if (!mDataProvider.isDirty()) {
return mPreviousResult;
}

View File

@@ -116,7 +116,10 @@ public class FalsingDataProvider {
* interactionType is defined by {@link com.android.systemui.classifier.Classifier}.
*/
final void setInteractionType(@Classifier.InteractionType int interactionType) {
this.mInteractionType = interactionType;
if (mInteractionType != interactionType) {
mInteractionType = interactionType;
mDirty = true;
}
}
public boolean isDirty() {

View File

@@ -46,6 +46,7 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.IndentingPrintWriter;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -80,6 +81,7 @@ public class DozeSensors {
private long mDebounceFrom;
private boolean mSettingRegistered;
private boolean mListening;
private boolean mListeningTouchScreenSensors;
@VisibleForTesting
public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum {
@@ -222,22 +224,25 @@ public class DozeSensors {
/**
* If sensors should be registered and sending signals.
*/
public void setListening(boolean listen) {
if (mListening == listen) {
public void setListening(boolean listen, boolean includeTouchScreenSensors) {
if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors) {
return;
}
mListening = listen;
mListeningTouchScreenSensors = includeTouchScreenSensors;
updateListening();
}
/**
* Registers/unregisters sensors based on internal state.
*/
public void updateListening() {
private void updateListening() {
boolean anyListening = false;
for (TriggerSensor s : mSensors) {
s.setListening(mListening);
if (mListening) {
boolean listen = mListening
&& (!s.mRequiresTouchscreen || mListeningTouchScreenSensors);
s.setListening(listen);
if (listen) {
anyListening = true;
}
}
@@ -309,10 +314,14 @@ public class DozeSensors {
/** Dump current state */
public void dump(PrintWriter pw) {
pw.println("mListening=" + mListening);
pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors);
IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " ");
idpw.increaseIndent();
for (TriggerSensor s : mSensors) {
pw.println(" Sensor: " + s.toString());
idpw.println("Sensor: " + s.toString());
}
pw.println(" ProxSensor: " + mProximitySensor.toString());
idpw.println("ProxSensor: " + mProximitySensor.toString());
}
/**

View File

@@ -38,6 +38,7 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.IndentingPrintWriter;
import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -408,15 +409,12 @@ public class DozeTriggers implements DozeMachine.Part {
break;
case DOZE_PULSE_DONE:
mDozeSensors.requestTemporaryDisable();
// A pulse will temporarily disable sensors that require a touch screen.
// Let's make sure that they are re-enabled when the pulse is over.
mDozeSensors.updateListening();
break;
case FINISH:
mBroadcastReceiver.unregister(mBroadcastDispatcher);
mDozeHost.removeCallback(mHostCallback);
mDockManager.removeListener(mDockEventListener);
mDozeSensors.setListening(false);
mDozeSensors.setListening(false, false);
mDozeSensors.setProxListening(false);
mWantSensors = false;
mWantProx = false;
@@ -424,20 +422,16 @@ public class DozeTriggers implements DozeMachine.Part {
break;
default:
}
mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
}
@Override
public void onScreenState(int state) {
mDozeSensors.onScreenState(state);
if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND
|| state == Display.STATE_OFF) {
mDozeSensors.setProxListening(mWantProx);
mDozeSensors.setListening(mWantSensors);
mDozeSensors.setTouchscreenSensorsListening(mWantTouchScreenSensors);
} else {
mDozeSensors.setProxListening(false);
mDozeSensors.setListening(mWantSensors);
}
mDozeSensors.setProxListening(mWantProx && (state == Display.STATE_DOZE
|| state == Display.STATE_DOZE_SUSPEND
|| state == Display.STATE_OFF));
mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
}
private void checkTriggersAtInit() {
@@ -513,7 +507,9 @@ public class DozeTriggers implements DozeMachine.Part {
pw.println(" pulsePending=" + mPulsePending);
pw.println("DozeSensors:");
mDozeSensors.dump(pw);
IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " ");
idpw.increaseIndent();
mDozeSensors.dump(idpw);
}
private class TriggerReceiver extends BroadcastReceiver {

View File

@@ -30,6 +30,7 @@ import com.android.settingslib.Utils
import com.android.systemui.Gefingerpoken
import com.android.systemui.qs.PageIndicator
import com.android.systemui.R
import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.util.animation.PhysicsAnimator
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -315,7 +316,8 @@ class MediaCarouselScrollHandler(
return false
}
private fun isFalseTouch() = falsingProtectionNeeded && falsingManager.isFalseTouch
private fun isFalseTouch() = falsingProtectionNeeded &&
falsingManager.isFalseTouch(NOTIFICATION_DISMISS)
private fun getMaxTranslation() = if (showsSettingsButton) {
settingsButton.width

View File

@@ -16,6 +16,7 @@
package com.android.systemui.media;
import static android.app.Notification.safeCharSequence;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
import android.app.PendingIntent;
@@ -261,7 +262,7 @@ public class MediaControlPanel {
// Song name
TextView titleText = mViewHolder.getTitleText();
titleText.setText(data.getSong());
titleText.setText(safeCharSequence(data.getSong()));
// App title
TextView appName = mViewHolder.getAppName();
@@ -269,7 +270,7 @@ public class MediaControlPanel {
// Artist name
TextView artistText = mViewHolder.getArtistText();
artistText.setText(data.getArtist());
artistText.setText(safeCharSequence(data.getArtist()));
// Transfer chip
mViewHolder.getSeamless().setVisibility(View.VISIBLE);

View File

@@ -70,6 +70,7 @@ private const val DEBUG = true
private const val DEFAULT_LUMINOSITY = 0.25f
private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
const val DEFAULT_COLOR = Color.DKGRAY
private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
emptyList(), emptyList(), "INVALID", null, null, null, true, null)
@@ -380,7 +381,7 @@ class MediaDataManager(
} else {
null
}
val bgColor = artworkBitmap?.let { computeBackgroundColor(it) } ?: Color.DKGRAY
val bgColor = artworkBitmap?.let { computeBackgroundColor(it) } ?: DEFAULT_COLOR
val mediaAction = getResumeMediaAction(resumeAction)
foregroundExecutor.execute {
@@ -560,12 +561,14 @@ class MediaDataManager(
private fun computeBackgroundColor(artworkBitmap: Bitmap?): Int {
var color = Color.WHITE
if (artworkBitmap != null) {
// If we have art, get colors from that
if (artworkBitmap != null && artworkBitmap.width > 1 && artworkBitmap.height > 1) {
// If we have valid art, get colors from that
val p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
.generate()
val swatch = MediaNotificationProcessor.findBackgroundSwatch(p)
color = swatch.rgb
} else {
return DEFAULT_COLOR
}
// Adapt background color, so it's always subdued and text is legible
val tmpHsl = floatArrayOf(0f, 0f, 0f)

View File

@@ -104,9 +104,9 @@ class PrivacyItemController @Inject constructor(
uiExecutor.execute(notifyChanges)
}
var allIndicatorsAvailable = isAllIndicatorsEnabled()
var allIndicatorsAvailable = false
private set
var micCameraAvailable = isMicCameraEnabled()
var micCameraAvailable = false
private set
private val devicePropertiesChangedListener =
@@ -158,10 +158,6 @@ class PrivacyItemController @Inject constructor(
}
init {
deviceConfigProxy.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_PRIVACY,
uiExecutor,
devicePropertiesChangedListener)
dumpManager.registerDumpable(TAG, this)
}

View File

@@ -283,8 +283,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// cancel current pending intent (if any) since clipData isn't used for matching
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
sharingChooserIntent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
context, 0, sharingChooserIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
null, UserHandle.CURRENT);
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
@@ -296,7 +298,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mSmartActionsEnabled)
.setAction(Intent.ACTION_SEND)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
UserHandle.SYSTEM);
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_share),
@@ -325,7 +328,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
editIntent, 0, null, UserHandle.CURRENT);
editIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
// Make sure pending intents for the system user are still unique across users
// by setting the (otherwise unused) request code to the current user id.
@@ -340,7 +343,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mSmartActionsEnabled)
.setAction(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
UserHandle.SYSTEM);
Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_edit),
r.getString(com.android.internal.R.string.screenshot_edit), editAction);
@@ -362,7 +366,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_IMMUTABLE);
Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_delete),
r.getString(com.android.internal.R.string.delete), deleteAction);
@@ -403,7 +409,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
mRandom.nextInt(),
intent,
PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
broadcastActions.add(new Notification.Action.Builder(action.getIcon(), action.title,
broadcastIntent).setContextual(true).addExtras(extras).build());
}

View File

@@ -141,14 +141,14 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
@ImeAnimationFlags
public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
mHiddenTop = hiddenTop;
mShownTop = shownTop;
mTargetShown = imeShouldShow;
if (!isDividerVisible()) {
return 0;
}
final boolean splitIsVisible = !getView().isHidden();
mHiddenTop = hiddenTop;
mShownTop = shownTop;
mTargetShown = imeShouldShow;
mSecondaryHasFocus = getSecondaryHasFocus(displayId);
final boolean splitIsVisible = !getView().isHidden();
final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
&& !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
&& !mSplits.mDivider.isMinimized();

View File

@@ -618,7 +618,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mEntranceAnimationRunning = false;
mExitAnimationRunning = false;
if (!dismissed && !wasMinimizeInteraction) {
WindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
mWindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
}
if (mCallback != null) {
mCallback.onDraggingEnd();
@@ -845,15 +845,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
void enterSplitMode(boolean isHomeStackResizable) {
post(() -> {
final SurfaceControl sc = getWindowSurfaceControl();
if (sc == null) {
return;
}
Transaction t = mTiles.getTransaction();
t.show(sc).apply();
mTiles.releaseTransaction(t);
});
setHidden(false);
SnapTarget miniMid =
mSplitLayout.getMinimizedSnapAlgorithm(isHomeStackResizable).getMiddleTarget();
@@ -880,16 +872,19 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
void exitSplitMode() {
// Reset tile bounds
final SurfaceControl sc = getWindowSurfaceControl();
if (sc == null) {
return;
}
Transaction t = mTiles.getTransaction();
t.hide(sc).apply();
t.hide(sc);
mImeController.setDimsHidden(t, true);
t.apply();
mTiles.releaseTransaction(t);
// Reset tile bounds
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
mWindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
}
public void setMinimizedDockStack(boolean minimized, long animDuration,

View File

@@ -37,7 +37,6 @@ import android.view.WindowManagerGlobal;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.TransactionPool;
@@ -112,10 +111,10 @@ public class WindowManagerProxy {
mExecutor.execute(mSetTouchableRegionRunnable);
}
static void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
WindowContainerTransaction t = new WindowContainerTransaction();
splitLayout.resizeSplits(position, t);
WindowOrganizer.applyTransaction(t);
applySyncTransaction(t);
}
private static boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out,

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -163,7 +165,7 @@ public class DragDownHelper implements Gefingerpoken {
if (!mDragDownCallback.isFalsingCheckNeeded()) {
return false;
}
return mFalsingManager.isFalseTouch() || !mDraggedFarEnough;
return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough;
}
private void captureStartingChild(float x, float y) {

View File

@@ -123,6 +123,7 @@ public class KeyguardIndicationController implements StateListener,
private int mChargingSpeed;
private int mChargingWattage;
private int mBatteryLevel;
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
private float mDisclosureMaxAlpha;
private String mMessageToShowOnScreenOn;
@@ -391,86 +392,103 @@ public class KeyguardIndicationController implements StateListener,
mWakeLock.setAcquired(false);
}
if (mVisible) {
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
if (mDozing) {
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
mTextView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
mTextView.switchIndication(mTransientIndication);
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
String indication = computePowerIndication();
if (animate) {
animateText(mTextView, indication);
} else {
mTextView.switchIndication(indication);
}
} else {
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
mTextView.switchIndication(percentage);
}
return;
}
if (!mVisible) {
return;
}
int userId = KeyguardUpdateMonitor.getCurrentUser();
String trustGrantedIndication = getTrustGrantedIndication();
String trustManagedIndication = getTrustManagedIndication();
// A few places might need to hide the indication, so always start by making it visible
mIndicationArea.setVisibility(View.VISIBLE);
String powerIndication = null;
if (mPowerPluggedIn || mEnableBatteryDefender) {
powerIndication = computePowerIndication();
}
boolean isError = false;
if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
} else if (!TextUtils.isEmpty(mTransientIndication)) {
if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
mTransientIndication, powerIndication);
mTextView.switchIndication(indication);
} else {
mTextView.switchIndication(mTransientIndication);
}
isError = mTransientTextIsError;
} else if (!TextUtils.isEmpty(trustGrantedIndication)
&& mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
if (powerIndication != null) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
trustGrantedIndication, powerIndication);
mTextView.switchIndication(indication);
} else {
mTextView.switchIndication(trustGrantedIndication);
}
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
if (mDozing) {
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
mTextView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
mTextView.switchIndication(mTransientIndication);
} else if (!mBatteryPresent) {
// If there is no battery detected, hide the indication and bail
mIndicationArea.setVisibility(View.GONE);
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
isError = true;
mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
if (DEBUG_CHARGING_SPEED) {
powerIndication += ", " + (mChargingWattage / 1000) + " mW";
}
String indication = computePowerIndication();
if (animate) {
animateText(mTextView, powerIndication);
animateText(mTextView, indication);
} else {
mTextView.switchIndication(powerIndication);
mTextView.switchIndication(indication);
}
} else if (!TextUtils.isEmpty(trustManagedIndication)
&& mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
&& !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
mTextView.switchIndication(trustManagedIndication);
} else {
mTextView.switchIndication(mRestingIndication);
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
mTextView.switchIndication(percentage);
}
mTextView.setTextColor(isError ? Utils.getColorError(mContext)
: mInitialTextColorState);
return;
}
int userId = KeyguardUpdateMonitor.getCurrentUser();
String trustGrantedIndication = getTrustGrantedIndication();
String trustManagedIndication = getTrustManagedIndication();
String powerIndication = null;
if (mPowerPluggedIn || mEnableBatteryDefender) {
powerIndication = computePowerIndication();
}
// Some cases here might need to hide the indication (if the battery is not present)
boolean hideIndication = false;
boolean isError = false;
if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
} else if (!TextUtils.isEmpty(mTransientIndication)) {
if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
mTransientIndication, powerIndication);
mTextView.switchIndication(indication);
hideIndication = !mBatteryPresent;
} else {
mTextView.switchIndication(mTransientIndication);
}
isError = mTransientTextIsError;
} else if (!TextUtils.isEmpty(trustGrantedIndication)
&& mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
if (powerIndication != null) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
trustGrantedIndication, powerIndication);
mTextView.switchIndication(indication);
hideIndication = !mBatteryPresent;
} else {
mTextView.switchIndication(trustGrantedIndication);
}
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
isError = true;
hideIndication = !mBatteryPresent;
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
if (DEBUG_CHARGING_SPEED) {
powerIndication += ", " + (mChargingWattage / 1000) + " mW";
}
if (animate) {
animateText(mTextView, powerIndication);
} else {
mTextView.switchIndication(powerIndication);
}
hideIndication = !mBatteryPresent;
} else if (!TextUtils.isEmpty(trustManagedIndication)
&& mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
&& !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
mTextView.switchIndication(trustManagedIndication);
} else {
mTextView.switchIndication(mRestingIndication);
}
mTextView.setTextColor(isError ? Utils.getColorError(mContext)
: mInitialTextColorState);
if (hideIndication) {
mIndicationArea.setVisibility(View.GONE);
}
}
@@ -654,6 +672,7 @@ public class KeyguardIndicationController implements StateListener,
pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
pw.println(" mDozing: " + mDozing);
pw.println(" mBatteryLevel: " + mBatteryLevel);
pw.println(" mBatteryPresent: " + mBatteryPresent);
pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
pw.println(" computePowerIndication(): " + computePowerIndication());
}
@@ -694,6 +713,7 @@ public class KeyguardIndicationController implements StateListener,
mBatteryLevel = status.level;
mBatteryOverheated = status.isOverheated();
mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn();
mBatteryPresent = status.present;
try {
mChargingTimeRemaining = mPowerPluggedIn
? mBatteryInfo.computeChargeTimeRemaining() : -1;

View File

@@ -30,6 +30,7 @@ import android.view.ViewConfiguration
import com.android.systemui.Gefingerpoken
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
@@ -106,7 +107,7 @@ constructor(
private var velocityTracker: VelocityTracker? = null
private val isFalseTouch: Boolean
get() = falsingManager.isFalseTouch
get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN)
var qsExpanded: Boolean = false
var pulseExpandAbortListener: Runnable? = null
var bouncerShowing: Boolean = false

View File

@@ -33,6 +33,7 @@ import android.graphics.Color;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Parcelable;
@@ -83,6 +84,16 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
public static final int STATE_DOT = 1;
public static final int STATE_HIDDEN = 2;
/**
* Maximum allowed byte count for an icon bitmap
* @see android.graphics.RecordingCanvas.MAX_BITMAP_SIZE
*/
private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
/**
* Maximum allowed width or height for an icon drawable, if we can't get byte count
*/
private static final int MAX_IMAGE_SIZE = 5000;
private static final String TAG = "StatusBarIconView";
private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
= new FloatProperty<StatusBarIconView>("iconAppearAmount") {
@@ -378,6 +389,22 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
return false;
}
if (drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) {
// If it's a bitmap we can check the size directly
int byteCount = ((BitmapDrawable) drawable).getBitmap().getByteCount();
if (byteCount > MAX_BITMAP_SIZE) {
Log.w(TAG, "Drawable is too large (" + byteCount + " bytes) " + mIcon);
return false;
}
} else if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
|| drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) {
// Otherwise, check dimensions
Log.w(TAG, "Drawable is too large (" + drawable.getIntrinsicWidth() + "x"
+ drawable.getIntrinsicHeight() + ") " + mIcon);
return false;
}
if (withClear) {
setImageDrawable(null);
}

View File

@@ -209,7 +209,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
|| (isFastNonDismissGesture && isAbleToShowMenu);
int menuSnapTarget = menuRow.getMenuSnapTarget();
boolean isNonFalseMenuRevealingGesture =
!isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
!isFalseGesture() && isMenuRevealingGestureAwayFromMenu;
if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
&& menuSnapTarget != 0) {
// Menu has not been snapped to previously and this is menu revealing gesture

View File

@@ -560,20 +560,21 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
if (mVocab != null) {
app = mVocab.getOrDefault(mPackageName, -1);
}
// Check if we are within the tightest bounds beyond which
// we would not need to run the ML model.
boolean withinRange = x <= mMLEnableWidth + mLeftInset
|| x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
if (!withinRange) {
// Denotes whether we should proceed with the gesture. Even if it is false, we may want to
// log it assuming it is not invalid due to exclusion.
boolean withinRange = x < mEdgeWidthLeft + mLeftInset
|| x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
if (withinRange) {
int results = -1;
if (mUseMLModel && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) {
withinRange = results == 1;
} else {
// Denotes whether we should proceed with the gesture.
// Even if it is false, we may want to log it assuming
// it is not invalid due to exclusion.
withinRange = x <= mEdgeWidthLeft + mLeftInset
|| x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
// Check if we are within the tightest bounds beyond which we would not need to run the
// ML model
boolean withinMinRange = x < mMLEnableWidth + mLeftInset
|| x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
if (!withinMinRange && mUseMLModel
&& (results = getBackGesturePredictionsCategory(x, y, app)) != -1) {
withinRange = (results == 1);
}
}

View File

@@ -27,6 +27,7 @@ import android.view.ViewConfiguration;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -317,7 +318,9 @@ public class KeyguardAffordanceHelper {
// We snap back if the current translation is not far enough
boolean snapBack = false;
if (mCallback.needsAntiFalsing()) {
snapBack = snapBack || mFalsingManager.isFalseTouch();
snapBack = snapBack || mFalsingManager.isFalseTouch(
mTargetedView == mRightIcon
? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE);
}
snapBack = snapBack || isBelowFalsingThreshold();

View File

@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.View.GONE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -69,6 +70,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager;
@@ -1204,7 +1206,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private boolean flingExpandsQs(float vel) {
if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
return false;
}
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -1214,12 +1216,12 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
private boolean isFalseTouch() {
private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
return false;
}
if (mFalsingManager.isClassifierEnabled()) {
return mFalsingManager.isFalseTouch();
return mFalsingManager.isFalseTouch(interactionType);
}
return !mQsTouchAboveFalsingThreshold;
}

View File

@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
import static java.lang.Float.isNaN;
import android.animation.Animator;
@@ -41,6 +45,7 @@ import com.android.internal.util.LatencyTracker;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -397,7 +402,12 @@ public abstract class PanelViewController {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
}
fling(vel, expand, isFalseTouch(x, y));
@Classifier.InteractionType int interactionType = vel > 0
? QUICK_SETTINGS : (
mKeyguardStateController.canDismissLockScreen()
? UNLOCK : BOUNCER_UNLOCK);
fling(vel, expand, isFalseTouch(x, y, interactionType));
onTrackingStopped(expand);
mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
if (mUpdateFlingOnLayout) {
@@ -492,7 +502,11 @@ public abstract class PanelViewController {
return true;
}
if (isFalseTouch(x, y)) {
@Classifier.InteractionType int interactionType = vel > 0
? QUICK_SETTINGS : (
mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
if (isFalseTouch(x, y, interactionType)) {
return true;
}
if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -511,12 +525,13 @@ public abstract class PanelViewController {
* @param y the final y-coordinate when the finger was lifted
* @return whether this motion should be regarded as a false touch
*/
private boolean isFalseTouch(float x, float y) {
private boolean isFalseTouch(float x, float y,
@Classifier.InteractionType int interactionType) {
if (!mStatusBar.isFalsingThresholdNeeded()) {
return false;
}
if (mFalsingManager.isClassifierEnabled()) {
return mFalsingManager.isFalseTouch();
return mFalsingManager.isFalseTouch(interactionType);
}
if (!mTouchAboveFalsingThreshold) {
return true;

View File

@@ -97,6 +97,9 @@ public interface BatteryController extends DemoMode, Dumpable,
default void onPowerSaveChanged(boolean isPowerSave) {
}
default void onBatteryUnknownStateChanged(boolean isUnknown) {
}
default void onReverseChanged(boolean isReverse, int level, String name) {
}

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
import static android.os.BatteryManager.EXTRA_PRESENT;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -70,6 +72,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
protected int mLevel;
protected boolean mPluggedIn;
protected boolean mCharging;
private boolean mStateUnknown = false;
private boolean mCharged;
protected boolean mPowerSave;
private boolean mAodPowerSave;
@@ -126,6 +129,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
pw.print(" mCharging="); pw.println(mCharging);
pw.print(" mCharged="); pw.println(mCharged);
pw.print(" mPowerSave="); pw.println(mPowerSave);
pw.print(" mStateUnknown="); pw.println(mStateUnknown);
}
@Override
@@ -139,8 +143,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mChangeCallbacks.add(cb);
}
if (!mHasReceivedBattery) return;
// Make sure new callbacks get the correct initial state
cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
cb.onPowerSaveChanged(mPowerSave);
cb.onBatteryUnknownStateChanged(mStateUnknown);
}
@Override
@@ -168,6 +175,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mWirelessCharging = mCharging && intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
== BatteryManager.BATTERY_PLUGGED_WIRELESS;
boolean present = intent.getBooleanExtra(EXTRA_PRESENT, true);
boolean unknown = !present;
if (unknown != mStateUnknown) {
mStateUnknown = unknown;
fireBatteryUnknownStateChanged();
}
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
@@ -316,6 +330,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
}
}
private void fireBatteryUnknownStateChanged() {
synchronized (mChangeCallbacks) {
final int n = mChangeCallbacks.size();
for (int i = 0; i < n; i++) {
mChangeCallbacks.get(i).onBatteryUnknownStateChanged(mStateUnknown);
}
}
}
private void firePowerSaveChanged() {
synchronized (mChangeCallbacks) {
final int N = mChangeCallbacks.size();
@@ -340,6 +363,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
String level = args.getString("level");
String plugged = args.getString("plugged");
String powerSave = args.getString("powersave");
String present = args.getString("present");
if (level != null) {
mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
}
@@ -350,6 +374,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mPowerSave = powerSave.equals("true");
firePowerSaveChanged();
}
if (present != null) {
mStateUnknown = !present.equals("true");
fireBatteryUnknownStateChanged();
}
fireBatteryLevelChanged();
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.policy
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import com.android.systemui.R
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
/**
* Listens for important battery states and sends non-dismissible system notifications if there is a
* problem
*/
class BatteryStateNotifier @Inject constructor(
val controller: BatteryController,
val noMan: NotificationManager,
val delayableExecutor: DelayableExecutor,
val context: Context
) : BatteryController.BatteryStateChangeCallback {
var stateUnknown = false
fun startListening() {
controller.addCallback(this)
}
fun stopListening() {
controller.removeCallback(this)
}
override fun onBatteryUnknownStateChanged(isUnknown: Boolean) {
stateUnknown = isUnknown
if (stateUnknown) {
val channel = NotificationChannel("battery_status", "Battery status",
NotificationManager.IMPORTANCE_DEFAULT)
noMan.createNotificationChannel(channel)
val intent = Intent(Intent.ACTION_VIEW,
Uri.parse(context.getString(R.string.config_batteryStateUnknownUrl)))
val pi = PendingIntent.getActivity(context, 0, intent, 0)
val builder = Notification.Builder(context, channel.id)
.setAutoCancel(false)
.setContentTitle(
context.getString(R.string.battery_state_unknown_notification_title))
.setContentText(
context.getString(R.string.battery_state_unknown_notification_text))
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentIntent(pi)
.setAutoCancel(true)
.setOngoing(true)
noMan.notify(TAG, ID, builder.build())
} else {
scheduleNotificationCancel()
}
}
private fun scheduleNotificationCancel() {
val r = {
if (!stateUnknown) {
noMan.cancel(ID)
}
}
delayableExecutor.executeDelayed(r, DELAY_MILLIS)
}
}
private const val TAG = "BatteryStateNotifier"
private const val ID = 666
private const val DELAY_MILLIS: Long = 4 * 60 * 60 * 1000

View File

@@ -33,7 +33,7 @@ public interface WakeLock {
static final String REASON_WRAP = "wrap";
/**
* Default wake-lock timeout, to avoid battery regressions.
* Default wake-lock timeout in milliseconds, to avoid battery regressions.
*/
long DEFAULT_MAX_TIMEOUT = 20000;
@@ -104,6 +104,7 @@ public interface WakeLock {
if (count == null) {
Log.wtf(TAG, "Releasing WakeLock with invalid reason: " + why,
new Throwable());
return;
} else if (count == 1) {
mActiveClients.remove(why);
} else {

Some files were not shown because too many files have changed in this diff Show More