Merge "DO NOT MERGE - Merge ab/7272582" into stage-aosp-master
This commit is contained in:
committed by
Android (Google) Code Review
commit
58175ace73
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
27
core/res/res/color-car/car_switch_track.xml
Normal file
27
core/res/res/color-car/car_switch_track.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
24
core/res/res/values/attrs_car.xml
Normal file
24
core/res/res/values/attrs_car.xml
Normal 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>
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
24
packages/SystemUI/res/drawable/ic_battery_unknown.xml
Normal file
24
packages/SystemUI/res/drawable/ic_battery_unknown.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -70,7 +70,7 @@ public class FalsingManagerFake implements FalsingManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFalseTouch() {
|
||||
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
|
||||
return mIsFalseTouch;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user