Merge "Shift+Meta+Space should reverse-rotate subtypes." into nyc-dev
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index b34af0b..84fc6fe 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -98,7 +98,7 @@
assert(uri == null || uri.getAuthority() == null ||
LauncherActivity.isLaunchUri(uri));
refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
- } else if (intent.getAction() == Intent.ACTION_VIEW) {
+ } else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
assert(uri != null);
new OpenUriForViewTask(this).executeOnExecutor(
ProviderExecutor.forAuthority(uri.getAuthority()), uri);
@@ -276,18 +276,6 @@
@Override
public void onDocumentPicked(DocumentInfo doc, Model model) {
- if (doc.isContainer()) {
- openContainerDocument(doc);
- } else {
- openDocument(doc, model);
- }
- }
-
- /**
- * Launches an intent to view the specified document.
- */
- private void openDocument(DocumentInfo doc, Model model) {
-
// Anything on downloads goes through the back through downloads manager
// (that's the MANAGE_DOCUMENT bit).
// This is done for two reasons:
@@ -297,7 +285,13 @@
// like origin URL.
// All other files not on downloads, event APKs, would get no benefit from this
// treatment, thusly the "isDownloads" check.
- if (getCurrentRoot().isDownloads()) {
+
+ // Launch MANAGE_DOCUMENTS only for the root level files, so it's not called for
+ // files in archives. Also, if the activity is already browsing a ZIP from downloads,
+ // then skip MANAGE_DOCUMENTS.
+ final boolean isViewing = Intent.ACTION_VIEW.equals(getIntent().getAction());
+ final boolean isInArchive = mState.stack.size() > 1;
+ if (getCurrentRoot().isDownloads() && !isInArchive && !isViewing) {
// First try managing the document; we expect manager to filter
// based on authority, so we don't grant.
final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
@@ -311,6 +305,17 @@
}
}
+ if (doc.isContainer()) {
+ openContainerDocument(doc);
+ } else {
+ openDocument(doc, model);
+ }
+ }
+
+ /**
+ * Launches an intent to view the specified document.
+ */
+ private void openDocument(DocumentInfo doc, Model model) {
Intent intent = new QuickViewIntentBuilder(
getPackageManager(), getResources(), doc, model).build();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
index 3067714..2045ec8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
@@ -51,6 +52,12 @@
|| touchY > mContent.getY() + mContent.getHeight();
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDismissButton.setText(R.string.clear_all_notifications_text);
+ }
+
public boolean isButtonVisible() {
return mDismissButton.getAlpha() != 0.0f;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
new file mode 100644
index 0000000..03b51c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2016 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.car;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * A {@link BatteryController} that is specific to the Auto use-case. For Auto, the battery icon
+ * displays the battery status of a device that is connected via bluetooth and not the system's
+ * battery.
+ */
+public class CarBatteryController extends BroadcastReceiver implements BatteryController {
+ private static final String TAG = "CarBatteryController";
+
+ // According to the Bluetooth HFP 1.5 specification, battery levels are indicated by a
+ // value from 1-5, where these values represent the following:
+ // 0%% - 0, 1-25%% - 1, 26-50%% - 2, 51-75%% - 3, 76-99%% - 4, 100%% - 5
+ // As a result, set the level as the average within that range.
+ private static final int BATTERY_LEVEL_EMPTY = 0;
+ private static final int BATTERY_LEVEL_1 = 12;
+ private static final int BATTERY_LEVEL_2 = 28;
+ private static final int BATTERY_LEVEL_3 = 63;
+ private static final int BATTERY_LEVEL_4 = 87;
+ private static final int BATTERY_LEVEL_FULL = 100;
+
+ private static final int INVALID_BATTERY_LEVEL = -1;
+
+ private final Context mContext;
+
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private BluetoothHeadsetClient mBluetoothHeadsetClient;
+
+ private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+
+ private int mLevel;
+
+ /**
+ * An interface indicating the container of a View that will display what the information
+ * in the {@link CarBatteryController}.
+ */
+ public interface BatteryViewHandler {
+ void hideBatteryView();
+ void showBatteryView();
+ }
+
+ private BatteryViewHandler mBatteryViewHandler;
+
+ public CarBatteryController(Context context) {
+ mContext = context;
+
+ mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
+ BluetoothProfile.HEADSET_CLIENT);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CarBatteryController state:");
+ pw.print(" mLevel=");
+ pw.println(mLevel);
+ }
+
+ @Override
+ public void setPowerSaveMode(boolean powerSave) {
+ // No-op. No power save mode for the car.
+ }
+
+ @Override
+ public void addStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ mChangeCallbacks.add(cb);
+
+ // There is no way to know if the phone is plugged in or charging via bluetooth, so pass
+ // false for these values.
+ cb.onBatteryLevelChanged(mLevel, false /* pluggedIn */, false /* charging */);
+ cb.onPowerSaveChanged(false /* isPowerSave */);
+ }
+
+ @Override
+ public void removeStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ mChangeCallbacks.remove(cb);
+ }
+
+ public void addBatteryViewHandler(BatteryViewHandler batteryViewHandler) {
+ mBatteryViewHandler = batteryViewHandler;
+ }
+
+ public void startListening() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHeadsetClient.ACTION_AG_EVENT);
+ mContext.registerReceiver(this, filter);
+ }
+
+ public void stopListening() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onReceive(). action: " + action);
+ }
+
+ if (BluetoothHeadsetClient.ACTION_AG_EVENT.equals(action)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Received ACTION_AG_EVENT");
+ }
+
+ int batteryLevel = intent.getIntExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL,
+ INVALID_BATTERY_LEVEL);
+
+ updateBatteryLevel(batteryLevel);
+
+ if (batteryLevel != INVALID_BATTERY_LEVEL && mBatteryViewHandler != null) {
+ mBatteryViewHandler.showBatteryView();
+ }
+ } else if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
+ Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED event: "
+ + oldState + " -> " + newState);
+
+ }
+ BluetoothDevice device =
+ (BluetoothDevice)intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
+ updateBatteryIcon(device, newState);
+ }
+ }
+
+ /**
+ * Converts the battery level to a percentage that can be displayed on-screen and notifies
+ * any {@link BatteryStateChangeCallback}s of this.
+ */
+ private void updateBatteryLevel(int batteryLevel) {
+ if (batteryLevel == INVALID_BATTERY_LEVEL) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Battery level invalid. Ignoring.");
+ }
+ return;
+ }
+
+ // The battery level is a value between 0-5. Let the default battery level be 0.
+ switch (batteryLevel) {
+ case 5:
+ mLevel = BATTERY_LEVEL_FULL;
+ break;
+ case 4:
+ mLevel = BATTERY_LEVEL_4;
+ break;
+ case 3:
+ mLevel = BATTERY_LEVEL_3;
+ break;
+ case 2:
+ mLevel = BATTERY_LEVEL_2;
+ break;
+ case 1:
+ mLevel = BATTERY_LEVEL_1;
+ break;
+ case 0:
+ default:
+ mLevel = BATTERY_LEVEL_EMPTY;
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Battery level: " + batteryLevel + "; setting mLevel as: " + mLevel);
+ }
+
+ notifyBatteryLevelChanged();
+ }
+
+ /**
+ * Updates the display of the battery icon depending on the given connection state from the
+ * given {@link BluetoothDevice}.
+ */
+ private void updateBatteryIcon(BluetoothDevice device, int newState) {
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Device connected");
+ }
+
+ if (mBatteryViewHandler != null) {
+ mBatteryViewHandler.showBatteryView();
+ }
+
+ if (mBluetoothHeadsetClient == null || device == null) {
+ return;
+ }
+
+ // Check if battery information is available and immediately update.
+ Bundle featuresBundle = mBluetoothHeadsetClient.getCurrentAgEvents(device);
+ if (featuresBundle == null) {
+ return;
+ }
+
+ int batteryLevel = featuresBundle.getInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL,
+ INVALID_BATTERY_LEVEL);
+ updateBatteryLevel(batteryLevel);
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Device disconnected");
+ }
+
+ if (mBatteryViewHandler != null) {
+ mBatteryViewHandler.hideBatteryView();
+ }
+ }
+ }
+
+ @Override
+ public boolean isPowerSave() {
+ // Power save is not valid for the car, so always return false.
+ return false;
+ }
+
+ private void notifyBatteryLevelChanged() {
+ for (int i = 0, size = mChangeCallbacks.size(); i < size; i++) {
+ mChangeCallbacks.get(i)
+ .onBatteryLevelChanged(mLevel, false /* pluggedIn */, false /* charging */);
+ }
+ }
+
+ private final ServiceListener mHfpServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = null;
+ }
+ }
+ };
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 4add3cb..811687c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -22,37 +22,75 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.PixelFormat;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
import android.view.WindowManager;
-
+import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.policy.BatteryController;
/**
* A status bar (and navigation bar) tailored for the automotive use case.
*/
-public class CarStatusBar extends PhoneStatusBar {
+public class CarStatusBar extends PhoneStatusBar implements
+ CarBatteryController.BatteryViewHandler {
+ private static final String TAG = "CarStatusBar";
+
private TaskStackListenerImpl mTaskStackListener;
private CarNavigationBarView mCarNavigationBar;
private CarNavigationBarController mController;
private FullscreenUserSwitcher mFullscreenUserSwitcher;
+ private CarBatteryController mCarBatteryController;
+ private BatteryMeterView mBatteryMeterView;
+
@Override
public void start() {
super.start();
mTaskStackListener = new TaskStackListenerImpl();
SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
registerPackageChangeReceivers();
+
+ mCarBatteryController.startListening();
+ }
+
+ @Override
+ public void destroy() {
+ mCarBatteryController.stopListening();
+ super.destroy();
+ }
+
+ @Override
+ protected PhoneStatusBarView makeStatusBarView() {
+ PhoneStatusBarView statusBarView = super.makeStatusBarView();
+
+ mBatteryMeterView = ((BatteryMeterView) statusBarView.findViewById(R.id.battery));
+
+ // By default, the BatteryMeterView should not be visible. It will be toggled visible
+ // when a device has connected by bluetooth.
+ mBatteryMeterView.setVisibility(View.GONE);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "makeStatusBarView(). mBatteryMeterView: " + mBatteryMeterView);
+ }
+
+ return statusBarView;
+ }
+
+ @Override
+ protected BatteryController createBatteryController() {
+ mCarBatteryController = new CarBatteryController(mContext);
+ mCarBatteryController.addBatteryViewHandler(this);
+ return mCarBatteryController;
}
@Override
@@ -85,6 +123,28 @@
}
+ @Override
+ public void showBatteryView() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
+ }
+
+ if (mBatteryMeterView != null) {
+ mBatteryMeterView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void hideBatteryView() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
+ }
+
+ if (mBatteryMeterView != null) {
+ mBatteryMeterView.setVisibility(View.GONE);
+ }
+ }
+
private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index a27ec28..fffb20a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -26,6 +26,7 @@
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Objects;
/**
@@ -37,6 +38,7 @@
private OnGroupChangeListener mListener;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
+ private HeadsUpManager mHeadsUpManager;
public void setOnGroupChangeListener(OnGroupChangeListener listener) {
mListener = listener;
@@ -142,6 +144,9 @@
&& group.summary.notification.getNotification().isGroupSummary()
&& hasIsolatedChildren(group)));
if (prevSuppressed != group.suppressed) {
+ if (group.suppressed) {
+ handleSuppressedSummaryHeadsUpped(group.summary);
+ }
mListener.onGroupsChanged();
}
}
@@ -160,6 +165,15 @@
return count;
}
+ private NotificationData.Entry getIsolatedChild(String groupKey) {
+ for (StatusBarNotification sbn : mIsolatedEntries.values()) {
+ if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
+ return mGroupMap.get(sbn.getKey()).summary;
+ }
+ }
+ return null;
+ }
+
public void onEntryUpdated(NotificationData.Entry entry,
StatusBarNotification oldNotification) {
if (mGroupMap.get(getGroupKey(oldNotification)) != null) {
@@ -332,6 +346,9 @@
// it doesn't lead to an update.
updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
mListener.onGroupsChanged();
+ } else {
+ handleSuppressedSummaryHeadsUpped(entry);
+
}
} else {
if (mIsolatedEntries.containsKey(sbn.getKey())) {
@@ -344,6 +361,32 @@
}
}
+ private void handleSuppressedSummaryHeadsUpped(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+ if (!isGroupSuppressed(sbn.getGroupKey())
+ || !sbn.getNotification().isGroupSummary()
+ || !entry.row.isHeadsUp()) {
+ return;
+ }
+ // The parent of a suppressed group got huned, lets hun the child!
+ NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
+ if (notificationGroup != null) {
+ Iterator<NotificationData.Entry> iterator = notificationGroup.children.iterator();
+ NotificationData.Entry child = iterator.hasNext() ? iterator.next() : null;
+ if (child == null) {
+ child = getIsolatedChild(sbn.getGroupKey());
+ }
+ if (child != null) {
+ if (mHeadsUpManager.isHeadsUp(child.key)) {
+ mHeadsUpManager.updateNotification(child, true);
+ } else {
+ mHeadsUpManager.showNotification(child);
+ }
+ }
+ }
+ mHeadsUpManager.releaseImmediately(entry.key);
+ }
+
private boolean shouldIsolate(StatusBarNotification sbn) {
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
@@ -360,6 +403,10 @@
|| notificationGroup.summary.row.getTranslationY() < 0;
}
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ }
+
public static class NotificationGroup {
public final HashSet<NotificationData.Entry> children = new HashSet<>();
public NotificationData.Entry summary;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 933d5bd..e52a401 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -21,9 +21,7 @@
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -147,6 +145,7 @@
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.CastControllerImpl;
@@ -730,6 +729,7 @@
mHeadsUpManager.addListener(mGroupManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mNotificationData.setHeadsUpManager(mHeadsUpManager);
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
@@ -826,7 +826,7 @@
// Other icons
mLocationController = new LocationControllerImpl(mContext,
mHandlerThread.getLooper()); // will post a notification
- mBatteryController = new BatteryController(mContext);
+ mBatteryController = createBatteryController();
mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
@@ -943,6 +943,10 @@
return mStatusBarView;
}
+ protected BatteryController createBatteryController() {
+ return new BatteryControllerImpl(mContext);
+ }
+
@Override
protected void reInflateViews() {
super.reInflateViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index bb3e116..ea64fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,158 +16,33 @@
package com.android.systemui.statusbar.policy;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.BatteryManager;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.Log;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
-public class BatteryController extends BroadcastReceiver {
- private static final String TAG = "BatteryController";
+public interface BatteryController {
+ /**
+ * Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}.
+ */
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args);
- public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
+ /**
+ * Sets if the current device is in power save mode.
+ */
+ void setPowerSaveMode(boolean powerSave);
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ /**
+ * Returns {@code true} if the device is currently in power save mode.
+ */
+ boolean isPowerSave();
- private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
- private final PowerManager mPowerManager;
- private final Handler mHandler;
+ void addStateChangedCallback(BatteryStateChangeCallback cb);
+ void removeStateChangedCallback(BatteryStateChangeCallback cb);
- private int mLevel;
- private boolean mPluggedIn;
- private boolean mCharging;
- private boolean mCharged;
- private boolean mPowerSave;
- private boolean mTestmode = false;
-
- public BatteryController(Context context) {
- mHandler = new Handler();
- mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
- filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
- filter.addAction(ACTION_LEVEL_TEST);
- context.registerReceiver(this, filter);
-
- updatePowerSave();
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("BatteryController state:");
- pw.print(" mLevel="); pw.println(mLevel);
- pw.print(" mPluggedIn="); pw.println(mPluggedIn);
- pw.print(" mCharging="); pw.println(mCharging);
- pw.print(" mCharged="); pw.println(mCharged);
- pw.print(" mPowerSave="); pw.println(mPowerSave);
- }
-
- public void setPowerSaveMode(boolean powerSave) {
- mPowerManager.setPowerSaveMode(powerSave);
- }
-
- public void addStateChangedCallback(BatteryStateChangeCallback cb) {
- mChangeCallbacks.add(cb);
- cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
- cb.onPowerSaveChanged(mPowerSave);
- }
-
- public void removeStateChangedCallback(BatteryStateChangeCallback cb) {
- mChangeCallbacks.remove(cb);
- }
-
- public void onReceive(final Context context, Intent intent) {
- final String action = intent.getAction();
- if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
- mLevel = (int)(100f
- * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
- / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
- mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
-
- final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
- BatteryManager.BATTERY_STATUS_UNKNOWN);
- mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
- mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
-
- fireBatteryLevelChanged();
- } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
- updatePowerSave();
- } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
- setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
- } else if (action.equals(ACTION_LEVEL_TEST)) {
- mTestmode = true;
- mHandler.post(new Runnable() {
- int curLevel = 0;
- int incr = 1;
- int saveLevel = mLevel;
- boolean savePlugged = mPluggedIn;
- Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
- @Override
- public void run() {
- if (curLevel < 0) {
- mTestmode = false;
- dummy.putExtra("level", saveLevel);
- dummy.putExtra("plugged", savePlugged);
- dummy.putExtra("testmode", false);
- } else {
- dummy.putExtra("level", curLevel);
- dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
- : 0);
- dummy.putExtra("testmode", true);
- }
- context.sendBroadcast(dummy);
-
- if (!mTestmode) return;
-
- curLevel += incr;
- if (curLevel == 100) {
- incr *= -1;
- }
- mHandler.postDelayed(this, 200);
- }
- });
- }
- }
-
- public boolean isPowerSave() {
- return mPowerSave;
- }
-
- private void updatePowerSave() {
- setPowerSave(mPowerManager.isPowerSaveMode());
- }
-
- private void setPowerSave(boolean powerSave) {
- if (powerSave == mPowerSave) return;
- mPowerSave = powerSave;
- if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
- firePowerSaveChanged();
- }
-
- private void fireBatteryLevelChanged() {
- final int N = mChangeCallbacks.size();
- for (int i = 0; i < N; i++) {
- mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
- }
- }
-
- private void firePowerSaveChanged() {
- final int N = mChangeCallbacks.size();
- for (int i = 0; i < N; i++) {
- mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
- }
- }
-
- public interface BatteryStateChangeCallback {
+ /**
+ * A listener that will be notified whenever a change in battery level or power save mode
+ * has occurred.
+ */
+ interface BatteryStateChangeCallback {
void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
void onPowerSaveChanged(boolean isPowerSave);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
new file mode 100644
index 0000000..24207f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Default implementation of a {@link BatteryController}. This controller monitors for battery
+ * level change events that are broadcasted by the system.
+ */
+public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController {
+ private static final String TAG = "BatteryController";
+
+ public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private final PowerManager mPowerManager;
+ private final Handler mHandler;
+
+ protected int mLevel;
+ protected boolean mPluggedIn;
+ protected boolean mCharging;
+ protected boolean mCharged;
+ protected boolean mPowerSave;
+ private boolean mTestmode = false;
+
+ public BatteryControllerImpl(Context context) {
+ mHandler = new Handler();
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
+ filter.addAction(ACTION_LEVEL_TEST);
+ context.registerReceiver(this, filter);
+
+ updatePowerSave();
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("BatteryController state:");
+ pw.print(" mLevel="); pw.println(mLevel);
+ pw.print(" mPluggedIn="); pw.println(mPluggedIn);
+ pw.print(" mCharging="); pw.println(mCharging);
+ pw.print(" mCharged="); pw.println(mCharged);
+ pw.print(" mPowerSave="); pw.println(mPowerSave);
+ }
+
+ @Override
+ public void setPowerSaveMode(boolean powerSave) {
+ mPowerManager.setPowerSaveMode(powerSave);
+ }
+
+ @Override
+ public void addStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ mChangeCallbacks.add(cb);
+ cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ cb.onPowerSaveChanged(mPowerSave);
+ }
+
+ @Override
+ public void removeStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ mChangeCallbacks.remove(cb);
+ }
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
+ mLevel = (int)(100f
+ * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
+ / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
+ mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+
+ final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
+ BatteryManager.BATTERY_STATUS_UNKNOWN);
+ mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
+ mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
+
+ fireBatteryLevelChanged();
+ } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
+ updatePowerSave();
+ } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
+ setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
+ } else if (action.equals(ACTION_LEVEL_TEST)) {
+ mTestmode = true;
+ mHandler.post(new Runnable() {
+ int curLevel = 0;
+ int incr = 1;
+ int saveLevel = mLevel;
+ boolean savePlugged = mPluggedIn;
+ Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ @Override
+ public void run() {
+ if (curLevel < 0) {
+ mTestmode = false;
+ dummy.putExtra("level", saveLevel);
+ dummy.putExtra("plugged", savePlugged);
+ dummy.putExtra("testmode", false);
+ } else {
+ dummy.putExtra("level", curLevel);
+ dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
+ : 0);
+ dummy.putExtra("testmode", true);
+ }
+ context.sendBroadcast(dummy);
+
+ if (!mTestmode) return;
+
+ curLevel += incr;
+ if (curLevel == 100) {
+ incr *= -1;
+ }
+ mHandler.postDelayed(this, 200);
+ }
+ });
+ }
+ }
+
+ @Override
+ public boolean isPowerSave() {
+ return mPowerSave;
+ }
+
+ private void updatePowerSave() {
+ setPowerSave(mPowerManager.isPowerSaveMode());
+ }
+
+ private void setPowerSave(boolean powerSave) {
+ if (powerSave == mPowerSave) return;
+ mPowerSave = powerSave;
+ if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
+ firePowerSaveChanged();
+ }
+
+ protected void fireBatteryLevelChanged() {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ }
+ }
+
+ private void firePowerSaveChanged() {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index ab81712..ebefdde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -185,6 +185,11 @@
if (alert) {
HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
+ if (headsUpEntry == null) {
+ // the entry was released before this update (i.e by a listener) This can happen
+ // with the groupmanager
+ return;
+ }
headsUpEntry.updateEntry();
setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index e32d1d1..e69c662 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1447,7 +1447,8 @@
if (stack == null || StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
- mOverrideConfig = calculateOverrideConfig(mTmpRect, insetBounds);
+ mOverrideConfig = calculateOverrideConfig(mTmpRect, insetBounds,
+ mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
}
if (mFullscreen != oldFullscreen) {
@@ -1457,33 +1458,38 @@
return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}
- private void subtractNonDecorInsets(Rect inOutBounds, Rect inInsetBounds) {
+ private void subtractNonDecorInsets(Rect inOutBounds, Rect inInsetBounds,
+ boolean overrideWidth, boolean overrideHeight) {
mTmpRect2.set(inInsetBounds);
mService.mWindowManager.subtractNonDecorInsets(mTmpRect2);
int leftInset = mTmpRect2.left - inInsetBounds.left;
int topInset = mTmpRect2.top - inInsetBounds.top;
- int rightInset = inInsetBounds.right - mTmpRect2.right;
- int bottomInset = inInsetBounds.bottom - mTmpRect2.bottom;
+ int rightInset = overrideWidth ? 0 : inInsetBounds.right - mTmpRect2.right;
+ int bottomInset = overrideHeight ? 0 : inInsetBounds.bottom - mTmpRect2.bottom;
inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
}
- private void subtractStableInsets(Rect inOutBounds, Rect inInsetBounds) {
+ private void subtractStableInsets(Rect inOutBounds, Rect inInsetBounds,
+ boolean overrideWidth, boolean overrideHeight) {
mTmpRect2.set(inInsetBounds);
mService.mWindowManager.subtractStableInsets(mTmpRect2);
int leftInset = mTmpRect2.left - inInsetBounds.left;
int topInset = mTmpRect2.top - inInsetBounds.top;
- int rightInset = inInsetBounds.right - mTmpRect2.right;
- int bottomInset = inInsetBounds.bottom - mTmpRect2.bottom;
+ int rightInset = overrideWidth ? 0 : inInsetBounds.right - mTmpRect2.right;
+ int bottomInset = overrideHeight ? 0 : inInsetBounds.bottom - mTmpRect2.bottom;
inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
}
- private Configuration calculateOverrideConfig(Rect bounds, Rect insetBounds) {
+ private Configuration calculateOverrideConfig(Rect bounds, Rect insetBounds,
+ boolean overrideWidth, boolean overrideHeight) {
mTmpNonDecorBounds.set(bounds);
mTmpStableBounds.set(bounds);
subtractNonDecorInsets(
- mTmpNonDecorBounds, insetBounds != null ? insetBounds : bounds);
+ mTmpNonDecorBounds, insetBounds != null ? insetBounds : bounds,
+ overrideWidth, overrideHeight);
subtractStableInsets(
- mTmpStableBounds, insetBounds != null ? insetBounds : bounds);
+ mTmpStableBounds, insetBounds != null ? insetBounds : bounds,
+ overrideWidth, overrideHeight);
// For calculating screenWidthDp, screenWidthDp, we use the stable inset screen area,
// i.e. the screen area without the system bars.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7f40079..c991130 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -642,13 +642,14 @@
* Subtracts the insets calculated by intersecting {@param layoutFrame} with {@param insetFrame}
* from {@param frame}. In other words, it applies the insets that would result if
* {@param frame} would be shifted to {@param layoutFrame} and then applying the insets from
- * {@param insetFrame}.
+ * {@param insetFrame}. Also it respects {@param displayFrame} in case window has minimum
+ * width/height applied and insets should be overridden.
*/
- private void subtractInsets(Rect frame, Rect layoutFrame, Rect insetFrame) {
- final int left = Math.max(0, insetFrame.left - layoutFrame.left);
- final int top = Math.max(0, insetFrame.top - layoutFrame.top);
- final int right = Math.max(0, layoutFrame.right - insetFrame.right);
- final int bottom = Math.max(0, layoutFrame.bottom - insetFrame.bottom);
+ private void subtractInsets(Rect frame, Rect layoutFrame, Rect insetFrame, Rect displayFrame) {
+ final int left = Math.max(0, insetFrame.left - Math.max(layoutFrame.left, displayFrame.left));
+ final int top = Math.max(0, insetFrame.top - Math.max(layoutFrame.top, displayFrame.top));
+ final int right = Math.max(0, Math.min(layoutFrame.right, displayFrame.right) - insetFrame.right);
+ final int bottom = Math.max(0, Math.min(layoutFrame.bottom, displayFrame.bottom) - insetFrame.bottom);
frame.inset(left, top, right, bottom);
}
@@ -687,8 +688,7 @@
// The offset from the layout containing frame to the actual containing frame.
final int layoutXDiff;
final int layoutYDiff;
- if (mInsetFrame.isEmpty() && (fullscreenTask
- || layoutInParentFrame())) {
+ if (mInsetFrame.isEmpty() && (fullscreenTask || layoutInParentFrame())) {
// We use the parent frame as the containing frame for fullscreen and child windows
mContainingFrame.set(pf);
mDisplayFrame.set(df);
@@ -733,10 +733,12 @@
layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0;
layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
- subtractInsets(mDisplayFrame, layoutContainingFrame, df);
+ mTmpRect.set(0, 0, mDisplayContent.getDisplayInfo().logicalWidth,
+ mDisplayContent.getDisplayInfo().logicalHeight);
+ subtractInsets(mDisplayFrame, layoutContainingFrame, df, mTmpRect);
if (!layoutInParentFrame()) {
- subtractInsets(mContainingFrame, layoutContainingFrame, pf);
- subtractInsets(mInsetFrame, layoutContainingFrame, pf);
+ subtractInsets(mContainingFrame, layoutContainingFrame, pf, mTmpRect);
+ subtractInsets(mInsetFrame, layoutContainingFrame, pf, mTmpRect);
}
layoutDisplayFrame = df;
layoutDisplayFrame.intersect(layoutContainingFrame);
@@ -855,8 +857,8 @@
getDisplayContent().getLogicalDisplayRect(mTmpRect);
// Override right and/or bottom insets in case if the frame doesn't fit the screen in
// non-fullscreen mode.
- boolean overrideRightInset = !fullscreenTask && mFrame.right > mTmpRect.right;
- boolean overrideBottomInset = !fullscreenTask && mFrame.bottom > mTmpRect.bottom;
+ boolean overrideRightInset = !fullscreenTask && layoutContainingFrame.right > mTmpRect.right;
+ boolean overrideBottomInset = !fullscreenTask && layoutContainingFrame.bottom > mTmpRect.bottom;
mContentInsets.set(mContentFrame.left - layoutContainingFrame.left,
mContentFrame.top - layoutContainingFrame.top,
overrideRightInset ? mTmpRect.right - mContentFrame.right
@@ -2590,7 +2592,7 @@
final int ph = containingFrame.height();
final Task task = getTask();
final boolean nonFullscreenTask = isInMultiWindowMode();
- final boolean fitToDisplay = task != null && !task.isFloating() && !layoutInParentFrame();
+ final boolean fitToDisplay = task != null && !nonFullscreenTask && !layoutInParentFrame();
float x, y;
int w,h;