Merge "Handle all configuration changes in PrintActivity as it cannot be destroyed synchronously." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 3fc1673..a9bb90f3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4280,6 +4280,7 @@
public class DownloadManager {
method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+ method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
method public long enqueue(android.app.DownloadManager.Request);
method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -22280,7 +22281,7 @@
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
- method public void unsubscribe(java.lang.String, android.os.Bundle);
+ method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
@@ -23053,6 +23054,7 @@
method public abstract void onStartRecording(android.net.Uri);
method public abstract void onStopRecording();
method public abstract void onTune(android.net.Uri);
+ method public void onTune(android.net.Uri, android.os.Bundle);
}
public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
@@ -23102,6 +23104,7 @@
method public void startRecording(android.net.Uri);
method public void stopRecording();
method public void tune(java.lang.String, android.net.Uri);
+ method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
}
public static abstract class TvRecordingClient.RecordingCallback {
@@ -30268,7 +30271,7 @@
method public android.print.PrinterInfo build();
method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
- method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+ method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
method public android.print.PrinterInfo.Builder setIconResourceId(int);
method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -30320,6 +30323,7 @@
method public boolean isStarted();
method public void setProgress(float);
method public void setStatus(java.lang.CharSequence);
+ method public void setStatus(int);
method public boolean setTag(java.lang.String);
method public boolean start();
}
@@ -30396,6 +30400,7 @@
public class BlockedNumberContract {
method public static boolean canCurrentUserBlockNumbers(android.content.Context);
method public static boolean isBlocked(android.content.Context, java.lang.String);
+ method public static int unblock(android.content.Context, java.lang.String);
field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
field public static final android.net.Uri AUTHORITY_URI;
}
@@ -32388,6 +32393,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -34577,7 +34583,7 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -36008,9 +36014,11 @@
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
method public void pullExternalCall();
+ method public final void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
+ method public final void removeExtras(java.util.List<java.lang.String>);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
method public void stopDtmfTone();
@@ -36139,6 +36147,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36151,6 +36160,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onMerge(android.telecom.Connection);
method public void onMerge();
@@ -36159,14 +36169,17 @@
method public void onStopDtmfTone();
method public void onSwap();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
method public final void removeConnection(android.telecom.Connection);
+ method public final void removeExtras(java.util.List<java.lang.String>);
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setExtras(android.os.Bundle);
+ method public final deprecated void setExtras(android.os.Bundle);
method public final void setOnHold();
method public final void setStatusHints(android.telecom.StatusHints);
method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
@@ -36192,6 +36205,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -36204,6 +36218,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEvent(java.lang.String, android.os.Bundle);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
@@ -36214,6 +36229,9 @@
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public static java.lang.String propertiesToString(int);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -36222,9 +36240,10 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setExtras(android.os.Bundle);
+ method public final deprecated void setExtras(android.os.Bundle);
method public final void setInitialized();
method public final void setInitializing();
method public final void setNextPostDialChar(char);
@@ -36238,12 +36257,11 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
- field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
- field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36261,6 +36279,7 @@
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36483,6 +36502,7 @@
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
@@ -36505,6 +36525,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36523,6 +36544,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -36552,6 +36574,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -41641,9 +41664,11 @@
}
public final class KeyboardShortcutInfo implements android.os.Parcelable {
+ ctor public KeyboardShortcutInfo(java.lang.CharSequence, int, int);
ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
method public int describeContents();
method public char getBaseCharacter();
+ method public int getKeycode();
method public java.lang.CharSequence getLabel();
method public int getModifiers();
method public void writeToParcel(android.os.Parcel, int);
@@ -43654,7 +43679,7 @@
method public abstract boolean onMenuOpened(int, android.view.Menu);
method public abstract void onPanelClosed(int, android.view.Menu);
method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
+ method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public abstract boolean onSearchRequested();
method public abstract boolean onSearchRequested(android.view.SearchEvent);
method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -51114,41 +51139,11 @@
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
- method public java.lang.ProcessBuilder inheritIO();
- method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectError(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
- method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectInput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectInput();
- method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectOutput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
- public static abstract class ProcessBuilder.Redirect {
- method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
- method public java.io.File file();
- method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
- method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
- method public abstract java.lang.ProcessBuilder.Redirect.Type type();
- field public static final java.lang.ProcessBuilder.Redirect INHERIT;
- field public static final java.lang.ProcessBuilder.Redirect PIPE;
- }
-
- public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
- method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
- method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
- }
-
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 48e1bd0..a9324d9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4412,6 +4412,7 @@
public class DownloadManager {
method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+ method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
method public long enqueue(android.app.DownloadManager.Request);
method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -23845,7 +23846,7 @@
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
- method public void unsubscribe(java.lang.String, android.os.Bundle);
+ method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
@@ -32583,7 +32584,7 @@
method public android.print.PrinterInfo build();
method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
- method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+ method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
method public android.print.PrinterInfo.Builder setIconResourceId(int);
method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -32635,6 +32636,7 @@
method public boolean isStarted();
method public void setProgress(float);
method public void setStatus(java.lang.CharSequence);
+ method public void setStatus(int);
method public boolean setTag(java.lang.String);
method public boolean start();
}
@@ -32711,6 +32713,7 @@
public class BlockedNumberContract {
method public static boolean canCurrentUserBlockNumbers(android.content.Context);
method public static boolean isBlocked(android.content.Context, java.lang.String);
+ method public static int unblock(android.content.Context, java.lang.String);
field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
field public static final android.net.Uri AUTHORITY_URI;
}
@@ -34835,6 +34838,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -37027,7 +37031,7 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -38571,9 +38575,11 @@
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
method public void pullExternalCall();
+ method public final void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
+ method public final void removeExtras(java.util.List<java.lang.String>);
method public deprecated void removeListener(android.telecom.Call.Listener);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
@@ -38710,6 +38716,7 @@
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final deprecated long getConnectTimeMillis();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -38724,6 +38731,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onMerge(android.telecom.Connection);
method public void onMerge();
@@ -38732,15 +38740,18 @@
method public void onStopDtmfTone();
method public void onSwap();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
method public final void removeConnection(android.telecom.Connection);
+ method public final void removeExtras(java.util.List<java.lang.String>);
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final deprecated void setConnectTimeMillis(long);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setExtras(android.os.Bundle);
+ method public final deprecated void setExtras(android.os.Bundle);
method public final void setOnHold();
method public final void setStatusHints(android.telecom.StatusHints);
method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
@@ -38767,6 +38778,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -38780,6 +38792,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEvent(java.lang.String, android.os.Bundle);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
@@ -38790,6 +38803,9 @@
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public static java.lang.String propertiesToString(int);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -38798,9 +38814,10 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setExtras(android.os.Bundle);
+ method public final deprecated void setExtras(android.os.Bundle);
method public final void setInitialized();
method public final void setInitializing();
method public final void setNextPostDialChar(char);
@@ -38814,12 +38831,11 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
- field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
- field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -38837,6 +38853,7 @@
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -39114,6 +39131,7 @@
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
@@ -39137,6 +39155,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -39155,6 +39174,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -39185,6 +39205,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -44367,9 +44388,11 @@
}
public final class KeyboardShortcutInfo implements android.os.Parcelable {
+ ctor public KeyboardShortcutInfo(java.lang.CharSequence, int, int);
ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
method public int describeContents();
method public char getBaseCharacter();
+ method public int getKeycode();
method public java.lang.CharSequence getLabel();
method public int getModifiers();
method public void writeToParcel(android.os.Parcel, int);
@@ -46381,7 +46404,7 @@
method public abstract boolean onMenuOpened(int, android.view.Menu);
method public abstract void onPanelClosed(int, android.view.Menu);
method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
+ method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public abstract boolean onSearchRequested();
method public abstract boolean onSearchRequested(android.view.SearchEvent);
method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -54179,41 +54202,11 @@
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
- method public java.lang.ProcessBuilder inheritIO();
- method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectError(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
- method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectInput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectInput();
- method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectOutput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
- public static abstract class ProcessBuilder.Redirect {
- method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
- method public java.io.File file();
- method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
- method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
- method public abstract java.lang.ProcessBuilder.Redirect.Type type();
- field public static final java.lang.ProcessBuilder.Redirect INHERIT;
- field public static final java.lang.ProcessBuilder.Redirect PIPE;
- }
-
- public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
- method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
- method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
- }
-
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
diff --git a/api/test-current.txt b/api/test-current.txt
index c1035af..f774c43 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4280,6 +4280,7 @@
public class DownloadManager {
method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+ method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
method public long enqueue(android.app.DownloadManager.Request);
method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -22345,7 +22346,7 @@
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
- method public void unsubscribe(java.lang.String, android.os.Bundle);
+ method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
@@ -23118,6 +23119,7 @@
method public abstract void onStartRecording(android.net.Uri);
method public abstract void onStopRecording();
method public abstract void onTune(android.net.Uri);
+ method public void onTune(android.net.Uri, android.os.Bundle);
}
public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
@@ -23167,6 +23169,7 @@
method public void startRecording(android.net.Uri);
method public void stopRecording();
method public void tune(java.lang.String, android.net.Uri);
+ method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
}
public static abstract class TvRecordingClient.RecordingCallback {
@@ -30260,7 +30263,7 @@
method public android.print.PrinterId getPrinterId();
method public float getProgress();
method public int getState();
- method public java.lang.CharSequence getStatus();
+ method public java.lang.CharSequence getStatus(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
field public static final int STATE_BLOCKED = 4; // 0x4
@@ -30337,7 +30340,7 @@
method public android.print.PrinterInfo build();
method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
- method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+ method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
method public android.print.PrinterInfo.Builder setIconResourceId(int);
method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -30389,6 +30392,7 @@
method public boolean isStarted();
method public void setProgress(float);
method public void setStatus(java.lang.CharSequence);
+ method public void setStatus(int);
method public boolean setTag(java.lang.String);
method public boolean start();
}
@@ -30465,6 +30469,7 @@
public class BlockedNumberContract {
method public static boolean canCurrentUserBlockNumbers(android.content.Context);
method public static boolean isBlocked(android.content.Context, java.lang.String);
+ method public static int unblock(android.content.Context, java.lang.String);
field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
field public static final android.net.Uri AUTHORITY_URI;
}
@@ -32457,6 +32462,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -34336,6 +34342,7 @@
public class NetworkSecurityPolicy {
method public static android.security.NetworkSecurityPolicy getInstance();
+ method public void handleTrustStorageUpdate();
method public boolean isCleartextTrafficPermitted();
method public boolean isCleartextTrafficPermitted(java.lang.String);
}
@@ -34648,7 +34655,7 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -36079,9 +36086,11 @@
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
method public void pullExternalCall();
+ method public final void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
+ method public final void removeExtras(java.util.List<java.lang.String>);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
method public void stopDtmfTone();
@@ -36210,6 +36219,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36222,6 +36232,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onMerge(android.telecom.Connection);
method public void onMerge();
@@ -36230,14 +36241,17 @@
method public void onStopDtmfTone();
method public void onSwap();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
method public final void removeConnection(android.telecom.Connection);
+ method public final void removeExtras(java.util.List<java.lang.String>);
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setExtras(android.os.Bundle);
+ method public final deprecated void setExtras(android.os.Bundle);
method public final void setOnHold();
method public final void setStatusHints(android.telecom.StatusHints);
method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
@@ -36263,6 +36277,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -36275,6 +36290,7 @@
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEvent(java.lang.String, android.os.Bundle);
method public void onDisconnect();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
@@ -36285,6 +36301,9 @@
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public final void putExtras(android.os.Bundle);
+ method public final void removeExtras(java.util.List<java.lang.String>);
+ method public static java.lang.String propertiesToString(int);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -36293,9 +36312,10 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setExtras(android.os.Bundle);
+ method public final deprecated void setExtras(android.os.Bundle);
method public final void setInitialized();
method public final void setInitializing();
method public final void setNextPostDialChar(char);
@@ -36309,12 +36329,11 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
- field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
- field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36332,6 +36351,7 @@
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36554,6 +36574,7 @@
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
@@ -36576,6 +36597,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36594,6 +36616,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -36623,6 +36646,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -41714,9 +41738,11 @@
}
public final class KeyboardShortcutInfo implements android.os.Parcelable {
+ ctor public KeyboardShortcutInfo(java.lang.CharSequence, int, int);
ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
method public int describeContents();
method public char getBaseCharacter();
+ method public int getKeycode();
method public java.lang.CharSequence getLabel();
method public int getModifiers();
method public void writeToParcel(android.os.Parcel, int);
@@ -43727,7 +43753,7 @@
method public abstract boolean onMenuOpened(int, android.view.Menu);
method public abstract void onPanelClosed(int, android.view.Menu);
method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
+ method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public abstract boolean onSearchRequested();
method public abstract boolean onSearchRequested(android.view.SearchEvent);
method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -51187,41 +51213,11 @@
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
- method public java.lang.ProcessBuilder inheritIO();
- method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectError(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
- method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectInput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectInput();
- method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectOutput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
- public static abstract class ProcessBuilder.Redirect {
- method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
- method public java.io.File file();
- method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
- method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
- method public abstract java.lang.ProcessBuilder.Redirect.Type type();
- field public static final java.lang.ProcessBuilder.Redirect INHERIT;
- field public static final java.lang.ProcessBuilder.Redirect PIPE;
- }
-
- public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
- method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
- method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
- }
-
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index ea53e59..c597ed2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -68,14 +68,11 @@
// ---------------------------------------------------------------------------
-BootAnimation::BootAnimation() : Thread(false), mZip(NULL), mClockEnabled(true) {
+BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) {
mSession = new SurfaceComposerClient();
}
BootAnimation::~BootAnimation() {
- if (mZip != NULL) {
- delete mZip;
- }
}
void BootAnimation::onFirstRef() {
@@ -288,19 +285,15 @@
bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
- ZipFileRO* zipFile = NULL;
- if ((encryptedAnimation &&
- (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
-
- ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
-
- ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
- mZip = zipFile;
+ if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
+ mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
}
-
+ else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) {
+ mZipFileName = OEM_BOOTANIMATION_FILE;
+ }
+ else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) {
+ mZipFileName = SYSTEM_BOOTANIMATION_FILE;
+ }
return NO_ERROR;
}
@@ -309,7 +302,7 @@
bool r;
// We have no bootanimation file, so we use the stock android logo
// animation.
- if (mZip == NULL) {
+ if (mZipFileName.isEmpty()) {
r = android();
} else {
r = movie();
@@ -429,16 +422,17 @@
return true;
}
-bool BootAnimation::readFile(const char* name, String8& outString)
+
+static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
{
- ZipEntryRO entry = mZip->findEntryByName(name);
+ ZipEntryRO entry = zip->findEntryByName(name);
ALOGE_IF(!entry, "couldn't find %s", name);
if (!entry) {
return false;
}
- FileMap* entryMap = mZip->createEntryFileMap(entry);
- mZip->releaseEntry(entry);
+ FileMap* entryMap = zip->createEntryFileMap(entry);
+ zip->releaseEntry(entry);
ALOGE_IF(!entryMap, "entryMap is null");
if (!entryMap) {
return false;
@@ -512,18 +506,18 @@
glBindTexture(GL_TEXTURE_2D, 0);
}
-bool BootAnimation::movie()
+bool BootAnimation::parseAnimationDesc(Animation& animation)
{
String8 desString;
- if (!readFile("desc.txt", desString)) {
+ if (!readFile(animation.zip, "desc.txt", desString)) {
return false;
}
char const* s = desString.string();
// Create and initialize an AudioPlayer if we have an audio_conf.txt file
String8 audioConf;
- if (readFile("audio_conf.txt", audioConf)) {
+ if (readFile(animation.zip, "audio_conf.txt", audioConf)) {
mAudioPlayer = new AudioPlayer;
if (!mAudioPlayer->init(audioConf.string())) {
ALOGE("mAudioPlayer.init failed");
@@ -531,8 +525,6 @@
}
}
- Animation animation;
-
// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
@@ -564,6 +556,7 @@
part.path = path;
part.clockPosY = clockPosY;
part.audioFile = NULL;
+ part.animation = NULL;
if (!parseColor(color, part.backgroundColor)) {
ALOGE("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
@@ -572,13 +565,29 @@
}
animation.parts.add(part);
}
-
+ else if (strcmp(l, "$SYSTEM") == 0) {
+ // ALOGD("> SYSTEM");
+ Animation::Part part;
+ part.playUntilComplete = false;
+ part.count = 1;
+ part.pause = 0;
+ part.audioFile = NULL;
+ part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
+ if (part.animation != NULL)
+ animation.parts.add(part);
+ }
s = ++endl;
}
+ return true;
+}
+
+bool BootAnimation::preloadZip(Animation& animation)
+{
// read all the data structures
const size_t pcount = animation.parts.size();
void *cookie = NULL;
+ ZipFileRO* mZip = animation.zip;
if (!mZip->startIteration(&cookie)) {
return false;
}
@@ -624,6 +633,16 @@
mZip->endIteration(cookie);
+ return true;
+}
+
+bool BootAnimation::movie()
+{
+
+ Animation* animation = loadAnimation(mZipFileName);
+ if (animation == NULL)
+ return false;
+
// Blend required to draw time on top of animation frames.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_FLAT);
@@ -645,6 +664,19 @@
mClockEnabled = clockTextureInitialized;
}
+ playAnimation(*animation);
+ releaseAnimation(animation);
+
+ if (clockTextureInitialized) {
+ glDeleteTextures(1, &mClock.name);
+ }
+
+ return false;
+}
+
+bool BootAnimation::playAnimation(const Animation& animation)
+{
+ const size_t pcount = animation.parts.size();
const int xc = (mWidth - animation.width) / 2;
const int yc = ((mHeight - animation.height) / 2);
nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -657,6 +689,14 @@
const size_t fcount = part.frames.size();
glBindTexture(GL_TEXTURE_2D, 0);
+ // Handle animation package
+ if (part.animation != NULL) {
+ playAnimation(*part.animation);
+ if (exitPending())
+ break;
+ continue; //to next part
+ }
+
for (int r=0 ; !part.count || r<part.count ; r++) {
// Exit any non playuntil complete parts immediately
if(exitPending() && !part.playUntilComplete)
@@ -744,14 +784,46 @@
}
}
}
-
- if (clockTextureInitialized) {
- glDeleteTextures(1, &mClock.name);
- }
-
- return false;
+ return true;
}
+void BootAnimation::releaseAnimation(Animation* animation) const
+{
+ for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
+ e = animation->parts.end(); it != e; ++it) {
+ if (it->animation)
+ releaseAnimation(it->animation);
+ }
+ if (animation->zip)
+ delete animation->zip;
+ delete animation;
+}
+
+BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
+{
+ if (mLoadedFiles.indexOf(fn) >= 0) {
+ ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
+ fn.string());
+ return NULL;
+ }
+ ZipFileRO *zip = ZipFileRO::open(fn);
+ if (zip == NULL) {
+ ALOGE("Failed to open animation zip \"%s\": %s",
+ fn.string(), strerror(errno));
+ return NULL;
+ }
+
+ Animation *animation = new Animation;
+ animation->fileName = fn;
+ animation->zip = zip;
+ mLoadedFiles.add(animation->fileName);
+
+ parseAnimationDesc(*animation);
+ preloadZip(*animation);
+
+ mLoadedFiles.remove(fn);
+ return animation;
+}
// ---------------------------------------------------------------------------
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 83e2b38..d49e1ec 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -76,19 +76,27 @@
bool playUntilComplete;
float backgroundColor[3];
FileMap* audioFile;
+ Animation* animation;
};
int fps;
int width;
int height;
Vector<Part> parts;
+ String8 audioConf;
+ String8 fileName;
+ ZipFileRO* zip;
};
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
status_t initTexture(const Animation::Frame& frame);
bool android();
- bool readFile(const char* name, String8& outString);
bool movie();
void drawTime(const Texture& clockTex, const int yPos);
+ Animation* loadAnimation(const String8&);
+ bool playAnimation(const Animation&);
+ void releaseAnimation(Animation*) const;
+ bool parseAnimationDesc(Animation&);
+ bool preloadZip(Animation &animation);
void checkExit();
@@ -104,8 +112,9 @@
EGLDisplay mSurface;
sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
- ZipFileRO *mZip;
bool mClockEnabled;
+ String8 mZipFileName;
+ SortedVector<String8> mLoadedFiles;
};
// ---------------------------------------------------------------------------
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index 32edd4d..77df151 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -29,8 +29,9 @@
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
+ * @hide
*/
-class KeyframeSet implements Keyframes {
+public class KeyframeSet implements Keyframes {
int mNumKeyframes;
diff --git a/core/java/android/animation/Keyframes.java b/core/java/android/animation/Keyframes.java
index c149bed..e40a86c 100644
--- a/core/java/android/animation/Keyframes.java
+++ b/core/java/android/animation/Keyframes.java
@@ -20,8 +20,9 @@
/**
* This interface abstracts a collection of Keyframe objects and is called by
* ValueAnimator to calculate values between those keyframes for a given animation.
+ * @hide
*/
-interface Keyframes extends Cloneable {
+public interface Keyframes extends Cloneable {
/**
* Sets the TypeEvaluator to be used when calculating animated values. This object
diff --git a/core/java/android/animation/PathKeyframes.java b/core/java/android/animation/PathKeyframes.java
index 8230ac5..50a490e 100644
--- a/core/java/android/animation/PathKeyframes.java
+++ b/core/java/android/animation/PathKeyframes.java
@@ -34,8 +34,9 @@
* Typically, the returned type is a PointF, but the individual components can be extracted
* as either an IntKeyframes or FloatKeyframes.
* </p>
+ * @hide
*/
-class PathKeyframes implements Keyframes {
+public class PathKeyframes implements Keyframes {
private static final int FRACTION_OFFSET = 0;
private static final int X_OFFSET = 1;
private static final int Y_OFFSET = 2;
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 663f297..c6a5152 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -967,7 +967,7 @@
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
- if (mStartDelay == 0) {
+ if (mStartDelay == 0 || mSeekFraction >= 0) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cc1d68e..ee17e8a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -57,6 +57,7 @@
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
+import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
@@ -91,6 +92,8 @@
import android.view.ContextThemeWrapper;
import android.view.DragEvent;
import android.view.DropPermissions;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -1679,10 +1682,16 @@
}
@Override
- public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+ public void onProvideKeyboardShortcuts(
+ List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
if (menu == null) {
return;
}
+ final InputDevice inputDevice = InputManager.getInstance().getInputDevice(deviceId);
+ if (inputDevice == null) {
+ return;
+ }
+ final KeyCharacterMap keyCharacterMap = inputDevice.getKeyCharacterMap();
KeyboardShortcutGroup group = null;
int menuSize = menu.size();
for (int i = 0; i < menuSize; ++i) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2d33a2c..6380801 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -1790,7 +1791,7 @@
public int taskWidth;
public int taskHeight;
- public int screenOrientation;
+ public int screenOrientation = Configuration.ORIENTATION_UNDEFINED;
public TaskThumbnailInfo() {
// Do nothing
@@ -1807,7 +1808,16 @@
public void reset() {
taskWidth = 0;
taskHeight = 0;
- screenOrientation = 0;
+ screenOrientation = Configuration.ORIENTATION_UNDEFINED;
+ }
+
+ /**
+ * Copies from another ThumbnailInfo.
+ */
+ public void copyFrom(TaskThumbnailInfo o) {
+ taskWidth = o.taskWidth;
+ taskHeight = o.taskHeight;
+ screenOrientation = o.screenOrientation;
}
/** @hide */
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7310d67..5116634 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -114,4 +114,15 @@
* values.
*/
public abstract void notifyAppTransitionStarting(int reason);
+
+ /**
+ * Callback for window manager to let activity manager know that the app transition was
+ * cancelled.
+ */
+ public abstract void notifyAppTransitionCancelled();
+
+ /**
+ * Callback for window manager to let activity manager know that the app transition is finished.
+ */
+ public abstract void notifyAppTransitionFinished();
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 167e6cb..2846798 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -154,6 +154,12 @@
private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId";
/**
+ * The task id the activity should be launched into.
+ * @hide
+ */
+ private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
+
+ /**
* Where the docked stack should be positioned.
* @hide
*/
@@ -224,6 +230,7 @@
private int mExitCoordinatorIndex;
private PendingIntent mUsageTimeReport;
private int mLaunchStackId = INVALID_STACK_ID;
+ private int mLaunchTaskId = -1;
private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
private AppTransitionAnimationSpec mAnimSpecs[];
@@ -766,6 +773,7 @@
break;
}
mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
+ mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
if (opts.containsKey(KEY_ANIM_SPECS)) {
Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
@@ -927,6 +935,21 @@
mLaunchStackId = launchStackId;
}
+ /**
+ * Sets the task the activity will be launched in.
+ * @hide
+ */
+ public void setLaunchTaskId(int taskId) {
+ mLaunchTaskId = taskId;
+ }
+
+ /**
+ * @hide
+ */
+ public int getLaunchTaskId() {
+ return mLaunchTaskId;
+ }
+
/** @hide */
public int getDockCreateMode() {
return mDockCreateMode;
@@ -1079,6 +1102,7 @@
break;
}
b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
+ b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
if (mAnimSpecs != null) {
b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5b7dae6..ed590e6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -272,12 +272,17 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
throws NameNotFoundException {
try {
- List<PermissionInfo> pi = mPM.queryPermissionsByGroup(group, flags).getList();
- if (pi != null) {
- return pi;
+ ParceledListSlice<PermissionInfo> parceledList =
+ mPM.queryPermissionsByGroup(group, flags);
+ if (parceledList != null) {
+ List<PermissionInfo> pi = parceledList.getList();
+ if (pi != null) {
+ return pi;
+ }
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -288,7 +293,7 @@
@Override
public PermissionGroupInfo getPermissionGroupInfo(String name,
- int flags) throws NameNotFoundException {
+ int flags) throws NameNotFoundException {
try {
PermissionGroupInfo pgi = mPM.getPermissionGroupInfo(name, flags);
if (pgi != null) {
@@ -302,9 +307,15 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
try {
- return mPM.getAllPermissionGroups(flags).getList();
+ ParceledListSlice<PermissionGroupInfo> parceledList =
+ mPM.getAllPermissionGroups(flags);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -439,9 +450,15 @@
}
@Override
+ @SuppressWarnings("unchecked")
public FeatureInfo[] getSystemAvailableFeatures() {
try {
- final List<FeatureInfo> list = mPM.getSystemAvailableFeatures().getList();
+ ParceledListSlice<FeatureInfo> parceledList =
+ mPM.getSystemAvailableFeatures();
+ if (parceledList == null) {
+ return new FeatureInfo[0];
+ }
+ final List<FeatureInfo> list = parceledList.getList();
final FeatureInfo[] res = new FeatureInfo[list.size()];
for (int i = 0; i < res.length; i++) {
res[i] = list.get(i);
@@ -636,10 +653,15 @@
/** @hide */
@Override
+ @SuppressWarnings("unchecked")
public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
- ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
- return slice.getList();
+ ParceledListSlice<PackageInfo> parceledList =
+ mPM.getInstalledPackages(flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -651,9 +673,12 @@
String[] permissions, int flags) {
final int userId = mContext.getUserId();
try {
- ParceledListSlice<PackageInfo> slice = mPM.getPackagesHoldingPermissions(
- permissions, flags, userId);
- return slice.getList();
+ ParceledListSlice<PackageInfo> parceledList =
+ mPM.getPackagesHoldingPermissions(permissions, flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -664,8 +689,12 @@
public List<ApplicationInfo> getInstalledApplications(int flags) {
final int userId = mContext.getUserId();
try {
- ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId);
- return slice.getList();
+ ParceledListSlice<ApplicationInfo> parceledList =
+ mPM.getInstalledApplications(flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -770,20 +799,25 @@
/** @hide Same as above but for a specific user */
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
- int flags, int userId) {
+ int flags, int userId) {
try {
- return mPM.queryIntentActivities(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
- userId).getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentActivities(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentActivityOptions(
ComponentName caller, Intent[] specifics, Intent intent,
int flags) {
@@ -807,10 +841,13 @@
}
try {
- return mPM
- .queryIntentActivityOptions(caller, specifics, specificTypes, intent,
- intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId())
- .getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentActivityOptions(caller, specifics, specificTypes, intent,
+ intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId());
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -820,13 +857,17 @@
* @hide
*/
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
try {
- return mPM.queryIntentReceivers(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
- userId).getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentReceivers(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -851,13 +892,17 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
try {
- return mPM.queryIntentServices(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
- userId).getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -869,12 +914,18 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentContentProvidersAsUser(
Intent intent, int flags, int userId) {
try {
- return mPM.queryIntentContentProviders(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId)
- .getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentContentProviders(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -901,12 +952,13 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<ProviderInfo> queryContentProviders(String processName,
- int uid, int flags) {
+ int uid, int flags) {
try {
- ParceledListSlice<ProviderInfo> slice
- = mPM.queryContentProviders(processName, uid, flags);
- return slice != null ? slice.getList() : null;
+ ParceledListSlice<ProviderInfo> slice =
+ mPM.queryContentProviders(processName, uid, flags);
+ return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -930,10 +982,16 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<InstrumentationInfo> queryInstrumentation(
String targetPackage, int flags) {
try {
- return mPM.queryInstrumentation(targetPackage, flags).getList();
+ ParceledListSlice<InstrumentationInfo> parceledList =
+ mPM.queryInstrumentation(targetPackage, flags);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1575,18 +1633,30 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
try {
- return mPM.getIntentFilterVerifications(packageName).getList();
+ ParceledListSlice<IntentFilterVerificationInfo> parceledList =
+ mPM.getIntentFilterVerifications(packageName);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
+ @SuppressWarnings("unchecked")
public List<IntentFilter> getAllIntentFilters(String packageName) {
try {
- return mPM.getAllIntentFilters(packageName).getList();
+ ParceledListSlice<IntentFilter> parceledList =
+ mPM.getAllIntentFilters(packageName);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 63a6829..d28f1fb 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -803,7 +803,12 @@
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
- if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+
+ // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
+ // generally not allowed, except if the caller specifies the task id the activity should
+ // be launched in.
+ if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+ && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 79461b4..0bb1097 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -1082,13 +1082,6 @@
}
/**
- * {@inheritDoc}
- */
- @Override
- public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
- }
-
- /**
* @return The activity associated with this dialog, or null if there is no associated activity.
*/
private ComponentName getAssociatedActivity() {
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 536c4a8..8bc1aa3 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1193,13 +1193,52 @@
boolean isMediaScannerScannable, String mimeType, String path, long length,
boolean showNotification) {
return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
- length, showNotification, false);
+ length, showNotification, false, null, null);
+ }
+
+ /**
+ * Adds a file to the downloads database system, so it could appear in Downloads App
+ * (and thus become eligible for management by the Downloads App).
+ * <p>
+ * It is helpful to make the file scannable by MediaScanner by setting the param
+ * isMediaScannerScannable to true. It makes the file visible in media managing
+ * applications such as Gallery App, which could be a useful purpose of using this API.
+ *
+ * @param title the title that would appear for this file in Downloads App.
+ * @param description the description that would appear for this file in Downloads App.
+ * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
+ * scanned by MediaScanner appear in the applications used to view media (for example,
+ * Gallery app).
+ * @param mimeType mimetype of the file.
+ * @param path absolute pathname to the file. The file should be world-readable, so that it can
+ * be managed by the Downloads App and any other app that is used to read it (for example,
+ * Gallery app to display the file, if the file contents represent a video/image).
+ * @param length length of the downloaded file
+ * @param showNotification true if a notification is to be sent, false otherwise
+ * @param uri the original HTTP URI of the download
+ * @param referer the HTTP Referer for the download
+ * @return an ID for the download entry added to the downloads app, unique across the system
+ * This ID is used to make future calls related to this download.
+ */
+ public long addCompletedDownload(String title, String description,
+ boolean isMediaScannerScannable, String mimeType, String path, long length,
+ boolean showNotification, Uri uri, Uri referer) {
+ return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
+ length, showNotification, false, uri, referer);
}
/** {@hide} */
public long addCompletedDownload(String title, String description,
boolean isMediaScannerScannable, String mimeType, String path, long length,
boolean showNotification, boolean allowWrite) {
+ return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
+ length, showNotification, allowWrite, null, null);
+ }
+
+ /** {@hide} */
+ public long addCompletedDownload(String title, String description,
+ boolean isMediaScannerScannable, String mimeType, String path, long length,
+ boolean showNotification, boolean allowWrite, Uri uri, Uri referer) {
// make sure the input args are non-null/non-zero
validateArgumentIsNonEmpty("title", title);
validateArgumentIsNonEmpty("description", description);
@@ -1210,10 +1249,18 @@
}
// if there is already an entry with the given path name in downloads.db, return its id
- Request request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD)
- .setTitle(title)
+ Request request;
+ if (uri != null) {
+ request = new Request(uri);
+ } else {
+ request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD);
+ }
+ request.setTitle(title)
.setDescription(description)
.setMimeType(mimeType);
+ if (referer != null) {
+ request.addRequestHeader("Referer", referer.toString());
+ }
ContentValues values = request.toContentValues(null);
values.put(Downloads.Impl.COLUMN_DESTINATION,
Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index ddd0ae9..d89c0e0 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -537,12 +537,10 @@
setTransitioningViewsVisiblity(View.INVISIBLE, false);
}
TransitionManager.beginDelayedTransition(decorView, transition);
- if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
- mSharedElements.get(0).invalidate();
- }
if (startEnterTransition) {
- setTransitioningViewsVisiblity(View.VISIBLE, true);
+ setTransitioningViewsVisiblity(View.VISIBLE, false);
}
+ decorView.invalidate();
} else {
transitionStarted();
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index d54ffa0..ce017f6 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -268,7 +268,8 @@
if (transition != null && decorView != null && mTransitioningViews != null) {
setTransitioningViewsVisiblity(View.VISIBLE, false);
TransitionManager.beginDelayedTransition(decorView, transition);
- setTransitioningViewsVisiblity(View.INVISIBLE, true);
+ setTransitioningViewsVisiblity(View.INVISIBLE, false);
+ decorView.invalidate();
} else {
transitionStarted();
}
@@ -367,7 +368,7 @@
scheduleGhostVisibilityChange(View.VISIBLE);
setGhostVisibility(View.VISIBLE);
if (viewsTransition != null) {
- setTransitioningViewsVisiblity(View.INVISIBLE, true);
+ setTransitioningViewsVisiblity(View.INVISIBLE, false);
}
decorView.invalidate();
} else {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 6870bbf..f7a4557 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1429,16 +1429,20 @@
final Context context = getContext();
final int version = context != null ? context.getApplicationInfo().targetSdkVersion : 0;
if (version >= Build.VERSION_CODES.N) {
- if (savedInstanceState != null) {
- Parcelable p = savedInstanceState.getParcelable(Activity.FRAGMENTS_TAG);
- if (p != null) {
- if (mChildFragmentManager == null) {
- instantiateChildFragmentManager();
- }
- mChildFragmentManager.restoreAllState(p, mChildNonConfig);
- mChildNonConfig = null;
- mChildFragmentManager.dispatchCreate();
+ restoreChildFragmentState(savedInstanceState, true);
+ }
+ }
+
+ void restoreChildFragmentState(@Nullable Bundle savedInstanceState, boolean provideNonConfig) {
+ if (savedInstanceState != null) {
+ Parcelable p = savedInstanceState.getParcelable(Activity.FRAGMENTS_TAG);
+ if (p != null) {
+ if (mChildFragmentManager == null) {
+ instantiateChildFragmentManager();
}
+ mChildFragmentManager.restoreAllState(p, provideNonConfig ? mChildNonConfig : null);
+ mChildNonConfig = null;
+ mChildFragmentManager.dispatchCreate();
}
}
}
@@ -1692,6 +1696,18 @@
*/
public void onDetach() {
mCalled = true;
+
+ // Destroy the child FragmentManager if we still have it here.
+ // We won't unless we're retaining our instance and if we do,
+ // our child FragmentManager instance state will have already been saved.
+ if (mChildFragmentManager != null) {
+ if (!mRetaining) {
+ throw new IllegalStateException("Child FragmentManager of " + this + " was not "
+ + " destroyed and this fragment is not retaining instance");
+ }
+ mChildFragmentManager.dispatchDestroy();
+ mChildFragmentManager = null;
+ }
}
/**
@@ -2252,16 +2268,7 @@
final Context context = getContext();
final int version = context != null ? context.getApplicationInfo().targetSdkVersion : 0;
if (version < Build.VERSION_CODES.N) {
- if (savedInstanceState != null) {
- Parcelable p = savedInstanceState.getParcelable(Activity.FRAGMENTS_TAG);
- if (p != null) {
- if (mChildFragmentManager == null) {
- instantiateChildFragmentManager();
- }
- mChildFragmentManager.restoreAllState(p, null);
- mChildFragmentManager.dispatchCreate();
- }
- }
+ restoreChildFragmentState(savedInstanceState, false);
}
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 0631943..2852baf 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -941,6 +941,9 @@
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState);
+ } else {
+ f.restoreChildFragmentState(f.mSavedFragmentState, true);
+ f.mState = Fragment.CREATED;
}
f.mRetaining = false;
if (f.mFromLayout) {
@@ -1009,6 +1012,9 @@
f.mSavedFragmentState = null;
}
case Fragment.ACTIVITY_CREATED:
+ if (newState > Fragment.ACTIVITY_CREATED) {
+ f.mState = Fragment.STOPPED;
+ }
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
@@ -1108,7 +1114,7 @@
if (!f.mRetaining) {
f.performDestroy();
} else {
- f.mState = Fragment.INITIALIZING;
+ f.mState = Fragment.CREATED;
}
f.mCalled = false;
@@ -1124,7 +1130,6 @@
f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
- f.mChildFragmentManager = null;
}
}
}
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 6432558..fa67529 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -35,4 +35,9 @@
* Called whenever the pinned stack is done animating a resize.
*/
void onPinnedStackAnimationEnded();
+
+ /**
+ * Called when we launched an activity that we forced to be resizable.
+ */
+ void onActivityForcedResizable(String packageName, int taskId);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index dabc652..e3fb161 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -454,8 +454,21 @@
*/
boolean performDexOptIfNeeded(String packageName, String instructionSet);
- boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles,
- boolean extractOnly, boolean force);
+ /**
+ * Ask the package manager to perform a dex-opt for the given reason. The package
+ * manager will map the reason to a compiler filter according to the current system
+ * configuration.
+ */
+ boolean performDexOpt(String packageName, String instructionSet, boolean checkProfiles,
+ int compileReason, boolean force);
+ /**
+ * Ask the package manager to perform a dex-opt with the given compiler filter.
+ *
+ * Note: exposed only for the shell command to allow moving packages explicitly to a
+ * definite state.
+ */
+ boolean performDexOptMode(String packageName, String instructionSet, boolean checkProfiles,
+ String targetCompilerFilter, boolean force);
void forceDexOpt(String packageName);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index db7ca2a..84605bb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1158,10 +1158,15 @@
StrictJarFile jarFile = null;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
+ // Ignore signature stripping protections when verifying APKs from system partition.
+ // For those APKs we only care about extracting signer certificates, and don't care
+ // about verifying integrity.
+ boolean signatureSchemeRollbackProtectionsEnforced =
+ (parseFlags & PARSE_IS_SYSTEM) == 0;
jarFile = new StrictJarFile(
apkPath,
- !verified // whether to verify JAR signature
- );
+ !verified, // whether to verify JAR signature
+ signatureSchemeRollbackProtectionsEnforced);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Always verify manifest, regardless of source
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 3139151..e8a3438 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -216,6 +216,7 @@
lastLoggedInTime = orig.lastLoggedInTime;
partial = orig.partial;
profileGroupId = orig.profileGroupId;
+ restrictedProfileParentId = orig.restrictedProfileParentId;
guestToRemove = orig.guestToRemove;
}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 9e360e1..20c2168 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -200,14 +200,6 @@
*/
public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
- /**
- * Sent by ConnectivityService to the NetworkAgent to install an APF program in the network
- * chipset for use to filter packets.
- *
- * obj = byte[] containing the APF program bytecode.
- */
- public static final int CMD_PUSH_APF_PROGRAM = BASE + 16;
-
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -327,10 +319,6 @@
preventAutomaticReconnect();
break;
}
- case CMD_PUSH_APF_PROGRAM: {
- installPacketFilter((byte[]) msg.obj);
- break;
- }
}
}
@@ -506,15 +494,6 @@
protected void preventAutomaticReconnect() {
}
- /**
- * Install a packet filter.
- * @param filter an APF program to filter incoming packets.
- * @return {@code true} if filter successfully installed, {@code false} otherwise.
- */
- protected boolean installPacketFilter(byte[] filter) {
- return false;
- }
-
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 748699e..5511a24 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -56,22 +56,6 @@
*/
public String subscriberId;
- /**
- * Version of APF instruction set supported for packet filtering. 0 indicates no support for
- * packet filtering using APF programs.
- */
- public int apfVersionSupported;
-
- /**
- * Maximum size of APF program allowed.
- */
- public int maximumApfProgramSize;
-
- /**
- * Format of packets passed to APF filter. Should be one of ARPHRD_*
- */
- public int apfPacketFormat;
-
public NetworkMisc() {
}
@@ -81,9 +65,6 @@
explicitlySelected = nm.explicitlySelected;
acceptUnvalidated = nm.acceptUnvalidated;
subscriberId = nm.subscriberId;
- apfVersionSupported = nm.apfVersionSupported;
- maximumApfProgramSize = nm.maximumApfProgramSize;
- apfPacketFormat = nm.apfPacketFormat;
}
}
@@ -98,9 +79,6 @@
out.writeInt(explicitlySelected ? 1 : 0);
out.writeInt(acceptUnvalidated ? 1 : 0);
out.writeString(subscriberId);
- out.writeInt(apfVersionSupported);
- out.writeInt(maximumApfProgramSize);
- out.writeInt(apfPacketFormat);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -111,9 +89,6 @@
networkMisc.explicitlySelected = in.readInt() != 0;
networkMisc.acceptUnvalidated = in.readInt() != 0;
networkMisc.subscriberId = in.readString();
- networkMisc.apfVersionSupported = in.readInt();
- networkMisc.maximumApfProgramSize = in.readInt();
- networkMisc.apfPacketFormat = in.readInt();
return networkMisc;
}
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index d847cd0..b32b2cc 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -18,6 +18,7 @@
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
@@ -68,6 +69,7 @@
public static final int MATCH_MOBILE_WILDCARD = 6;
public static final int MATCH_WIFI_WILDCARD = 7;
public static final int MATCH_BLUETOOTH = 8;
+ public static final int MATCH_PROXY = 9;
/**
* Set of {@link NetworkInfo#getType()} that reflect data usage.
@@ -157,6 +159,14 @@
return new NetworkTemplate(MATCH_BLUETOOTH, null, null);
}
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style
+ * networks together.
+ */
+ public static NetworkTemplate buildTemplateProxy() {
+ return new NetworkTemplate(MATCH_PROXY, null, null);
+ }
+
private final int mMatchRule;
private final String mSubscriberId;
@@ -293,6 +303,8 @@
return matchesWifiWildcard(ident);
case MATCH_BLUETOOTH:
return matchesBluetooth(ident);
+ case MATCH_PROXY:
+ return matchesProxy(ident);
default:
throw new IllegalArgumentException("unknown network template");
}
@@ -401,6 +413,13 @@
return false;
}
+ /**
+ * Check if matches Proxy network template.
+ */
+ private boolean matchesProxy(NetworkIdentity ident) {
+ return ident.mType == TYPE_PROXY;
+ }
+
private static String getMatchRuleName(int matchRule) {
switch (matchRule) {
case MATCH_MOBILE_3G_LOWER:
@@ -419,6 +438,8 @@
return "WIFI_WILDCARD";
case MATCH_BLUETOOTH:
return "BLUETOOTH";
+ case MATCH_PROXY:
+ return "PROXY";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e730ad8..be82d56a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -30,6 +30,8 @@
import android.telephony.SignalStrength;
import android.text.format.DateFormat;
import android.util.ArrayMap;
+import android.util.MutableBoolean;
+import android.util.Pair;
import android.util.Printer;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -5317,26 +5319,28 @@
}
if (apps != null) {
- SparseArray<ArrayList<String>> uids = new SparseArray<ArrayList<String>>();
+ SparseArray<Pair<ArrayList<String>, MutableBoolean>> uids = new SparseArray<>();
for (int i=0; i<apps.size(); i++) {
ApplicationInfo ai = apps.get(i);
- ArrayList<String> pkgs = uids.get(ai.uid);
+ Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(
+ UserHandle.getAppId(ai.uid));
if (pkgs == null) {
- pkgs = new ArrayList<String>();
- uids.put(ai.uid, pkgs);
+ pkgs = new Pair<>(new ArrayList<String>(), new MutableBoolean(false));
+ uids.put(UserHandle.getAppId(ai.uid), pkgs);
}
- pkgs.add(ai.packageName);
+ pkgs.first.add(ai.packageName);
}
SparseArray<? extends Uid> uidStats = getUidStats();
final int NU = uidStats.size();
String[] lineArgs = new String[2];
for (int i=0; i<NU; i++) {
- int uid = uidStats.keyAt(i);
- ArrayList<String> pkgs = uids.get(uid);
- if (pkgs != null) {
- for (int j=0; j<pkgs.size(); j++) {
+ int uid = UserHandle.getAppId(uidStats.keyAt(i));
+ Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(uid);
+ if (pkgs != null && !pkgs.second.value) {
+ pkgs.second.value = true;
+ for (int j=0; j<pkgs.first.size(); j++) {
lineArgs[0] = Integer.toString(uid);
- lineArgs[1] = pkgs.get(j);
+ lineArgs[1] = pkgs.first.get(j);
dumpLine(pw, 0 /* uid */, "i" /* category */, UID_DATA,
(Object[])lineArgs);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index de8b690..d0029e1 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -91,6 +91,12 @@
/** The name of the hardware (from the kernel command line or /proc). */
public static final String HARDWARE = getString("ro.hardware");
+ /**
+ * Whether this build was for an emulator device.
+ * @hide
+ */
+ public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
+
/** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
public static final String SERIAL = getString("ro.serialno");
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index 0f2e33d..f13e5b5 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -101,7 +101,8 @@
* {@link #UNDEFINED_TEMPERATURE} if undefined.
* Empty if platform doesn't provide the queried temperature.
*
- * @throws SecurityException if a non profile or device owner tries to call this method.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
public @NonNull float[] getDeviceTemperatures(@DeviceTemperatureType int type,
@TemperatureSource int source) {
@@ -137,7 +138,8 @@
* each unplugged core.
* Empty if CPU usage is not supported on this system.
*
- * @throws SecurityException if a non profile or device owner tries to call this method.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
public @NonNull CpuUsageInfo[] getCpuUsages() {
try {
@@ -153,7 +155,8 @@
* @return an array of float fan speeds in RPM. Empty if there are no fans or fan speed is not
* supported on this system.
*
- * @throws SecurityException if a non profile or device owner tries to call this method.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
public @NonNull float[] getFanSpeeds() {
try {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 100f6a1..24a6cdf 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -293,7 +293,9 @@
/**
* Control network activity of a UID over interfaces with a quota limit.
*/
- void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces);
+ void setUidMeteredNetworkBlacklist(int uid, boolean enable);
+ void setUidMeteredNetworkWhitelist(int uid, boolean enable);
+ boolean setDataSaverModeEnabled(boolean enable);
void setUidCleartextNetworkPolicy(int uid, int policy);
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 17176ec..34c0b14 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,7 +16,6 @@
package android.os.storage;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
@@ -336,7 +335,7 @@
*
* @see DocumentsContract
*/
- public Intent createAccessIntent(@NonNull String directoryName) {
+ public Intent createAccessIntent(String directoryName) {
final Intent intent = new Intent(ACTION_OPEN_EXTERNAL_DIRECTORY);
intent.putExtra(EXTRA_STORAGE_VOLUME, this);
intent.putExtra(EXTRA_DIRECTORY_NAME, directoryName);
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 469a4ea..63bf885 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -61,6 +61,15 @@
void setStatus(in PrintJobId printJobId, in CharSequence status);
/**
+ * Set the status of this print job
+ *
+ * @param printJobId The print job to update
+ * @param status The new status as a string resource
+ * @param appPackageName App package name the resource belongs to
+ */
+ void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName);
+
+ /**
* Handle that a custom icon for a printer was loaded.
*
* @param printerId the id of the printer the icon belongs to
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 7e3a72f..f134943 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -21,7 +21,10 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.annotation.TestApi;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -181,7 +184,11 @@
private float mProgress;
/** A short string describing the status of this job. */
- private CharSequence mStatus;
+ private @Nullable CharSequence mStatus;
+
+ /** A string resource describing the status of this job. */
+ private @StringRes int mStatusRes;
+ private @Nullable CharSequence mStatusResAppPackageName;
/** Advanced printer specific options. */
private Bundle mAdvancedOptions;
@@ -210,6 +217,8 @@
mDocumentInfo = other.mDocumentInfo;
mProgress = other.mProgress;
mStatus = other.mStatus;
+ mStatusRes = other.mStatusRes;
+ mStatusResAppPackageName = other.mStatusResAppPackageName;
mCanceling = other.mCanceling;
mAdvancedOptions = other.mAdvancedOptions;
}
@@ -235,8 +244,14 @@
mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
mProgress = parcel.readFloat();
mStatus = parcel.readCharSequence();
+ mStatusRes = parcel.readInt();
+ mStatusResAppPackageName = parcel.readCharSequence();
mCanceling = (parcel.readInt() == 1);
mAdvancedOptions = parcel.readBundle();
+
+ if (mAdvancedOptions != null) {
+ Preconditions.checkArgument(!mAdvancedOptions.containsKey(null));
+ }
}
/**
@@ -370,10 +385,28 @@
* @hide
*/
public void setStatus(@Nullable CharSequence status) {
+ mStatusRes = 0;
+ mStatusResAppPackageName = null;
+
mStatus = status;
}
/**
+ * Sets the status of the print job.
+ *
+ * @param status The new status as a string resource
+ * @param appPackageName App package name the resource belongs to
+ *
+ * @hide
+ */
+ public void setStatus(@StringRes int status, @NonNull CharSequence appPackageName) {
+ mStatus = null;
+
+ mStatusRes = status;
+ mStatusResAppPackageName = appPackageName;
+ }
+
+ /**
* Sets the owning application id.
*
* @return The owning app id.
@@ -633,6 +666,8 @@
parcel.writeParcelable(mDocumentInfo, 0);
parcel.writeFloat(mProgress);
parcel.writeCharSequence(mStatus);
+ parcel.writeInt(mStatusRes);
+ parcel.writeCharSequence(mStatusResAppPackageName);
parcel.writeInt(mCanceling ? 1 : 0);
parcel.writeBundle(mAdvancedOptions);
}
@@ -659,6 +694,9 @@
builder.append(", progress: " + mProgress);
builder.append(", status: " + (mStatus != null
? mStatus.toString() : null));
+ builder.append(", statusRes: " + mStatusRes);
+ builder.append(", statusResAppPackageName: " + (mStatusResAppPackageName != null
+ ? mStatusResAppPackageName.toString() : null));
builder.append("}");
return builder.toString();
}
@@ -707,12 +745,23 @@
/**
* Get the status of this job.
*
+ * @param pm Package manager used to resolve the string
+ *
* @return the status of this job or null if not set
* @hide
*/
@TestApi
- public @Nullable CharSequence getStatus() {
- return mStatus;
+ public @Nullable CharSequence getStatus(@NonNull PackageManager pm) {
+ if (mStatusRes == 0) {
+ return mStatus;
+ } else {
+ try {
+ return pm.getResourcesForApplication(mStatusResAppPackageName.toString())
+ .getString(mStatusRes);
+ } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
+ return null;
+ }
+ }
}
/**
@@ -789,6 +838,8 @@
* @param value The option value.
*/
public void putAdvancedOption(@NonNull String key, @Nullable String value) {
+ Preconditions.checkNotNull(key, "key cannot be null");
+
if (mPrototype.mAdvancedOptions == null) {
mPrototype.mAdvancedOptions = new Bundle();
}
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 0d2d9f4..1ee6389 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -467,10 +467,12 @@
* {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}.
* </p>
*
+ * @param hasCustomPrinterIcon If the printer has a custom icon or not.
+ *
* @return This builder.
*/
- public @NonNull Builder setHasCustomPrinterIcon() {
- mHasCustomPrinterIcon = true;
+ public @NonNull Builder setHasCustomPrinterIcon(boolean hasCustomPrinterIcon) {
+ mHasCustomPrinterIcon = hasCustomPrinterIcon;
return this;
}
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index 0ae1e18..f0ea6ae 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -52,6 +52,15 @@
*/
void setStatus(in PrintJobId printJobId, in CharSequence status);
+ /**
+ * Set the status of this print job
+ *
+ * @param printJobId The print job to update
+ * @param status The new status as a string resource
+ * @param appPackageName The app package name the string belongs to
+ */
+ void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName);
+
void onPrintersAdded(in ParceledListSlice printers);
void onPrintersRemoved(in ParceledListSlice printerIds);
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 6414b6a..7a7ca23 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -20,11 +20,14 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.content.Context;
import android.os.RemoteException;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.util.Preconditions;
/**
* This class represents a print job from the perspective of a print
@@ -45,7 +48,12 @@
private PrintJobInfo mCachedInfo;
- PrintJob(@NonNull PrintJobInfo jobInfo, @NonNull IPrintServiceClient client) {
+ /** Context that created the object */
+ private final Context mContext;
+
+ PrintJob(@NonNull Context context, @NonNull PrintJobInfo jobInfo,
+ @NonNull IPrintServiceClient client) {
+ mContext = context;
mCachedInfo = jobInfo;
mPrintServiceClient = client;
mDocument = new PrintDocument(mCachedInfo.getId(), client,
@@ -216,11 +224,10 @@
}
/**
- * Blocks the print job. You should call this method if {@link
- * #isStarted()} or {@link #isBlocked()} returns true and you need
- * to block the print job. For example, the user has to add some
- * paper to continue printing. To resume the print job call {@link
- * #start()}.
+ * Blocks the print job. You should call this method if {@link #isStarted()} returns true and
+ * you need to block the print job. For example, the user has to add some paper to continue
+ * printing. To resume the print job call {@link #start()}. To change the reason call
+ * {@link #setStatus(CharSequence)}.
*
* @param reason The human readable, short, and translated reason why the print job is blocked.
* @return Whether the job was blocked.
@@ -233,9 +240,7 @@
PrintService.throwIfNotCalledOnMainThread();
PrintJobInfo info = getInfo();
final int state = info.getState();
- if (state == PrintJobInfo.STATE_STARTED
- || (state == PrintJobInfo.STATE_BLOCKED
- && !TextUtils.equals(info.getStatus(), reason))) {
+ if (state == PrintJobInfo.STATE_STARTED || state == PrintJobInfo.STATE_BLOCKED) {
return setState(PrintJobInfo.STATE_BLOCKED, reason);
}
return false;
@@ -320,6 +325,9 @@
/**
* Sets the status of this print job. This should be a human readable, short, and translated
* description of the current state of the print job.
+ * <p />
+ * This overrides any previously set status set via {@link #setStatus(CharSequence)},
+ * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)},
*
* @param status The new status. If null the status will be empty.
*/
@@ -335,6 +343,29 @@
}
/**
+ * Sets the status of this print job as a string resource.
+ * <p />
+ * This overrides any previously set status set via {@link #setStatus(CharSequence)},
+ * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)},
+ * <p />
+ * To clear the status use {@link #setStatus(CharSequence) <code>setStatus(null)</code>}
+ *
+ * @param status The new status as a String resource.
+ */
+ @MainThread
+ public void setStatus(@StringRes int status) {
+ PrintService.throwIfNotCalledOnMainThread();
+ Preconditions.checkArgument(status != 0, "status has to be != 0");
+
+ try {
+ mPrintServiceClient.setStatusRes(mCachedInfo.getId(), status,
+ mContext.getPackageName());
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting status for job: " + mCachedInfo.getId(), re);
+ }
+ }
+
+ /**
* Sets a tag that is valid in the context of a {@link PrintService}
* and is not interpreted by the system. For example, a print service
* may set as a tag the key of the print job returned by a remote
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 62d214e..8f73518 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -329,7 +329,7 @@
final int printJobInfoCount = printJobInfos.size();
printJobs = new ArrayList<PrintJob>(printJobInfoCount);
for (int i = 0; i < printJobInfoCount; i++) {
- printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
+ printJobs.add(new PrintJob(this, printJobInfos.get(i), mClient));
}
}
if (printJobs != null) {
@@ -549,7 +549,7 @@
+ getPackageName());
}
PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
- onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
+ onRequestCancelPrintJob(new PrintJob(PrintService.this, printJobInfo, mClient));
} break;
case MSG_ON_PRINTJOB_QUEUED: {
@@ -561,7 +561,7 @@
if (DEBUG) {
Log.i(LOG_TAG, "Queued: " + printJobInfo);
}
- onPrintJobQueued(new PrintJob(printJobInfo, mClient));
+ onPrintJobQueued(new PrintJob(PrintService.this, printJobInfo, mClient));
} break;
case MSG_SET_CLIENT: {
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 6fe0189..e90dc9c 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -15,6 +15,7 @@
*/
package android.provider;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
@@ -109,6 +110,8 @@
* Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
* getContentResolver().delete(uri, null, null);
* </pre>
+ * To check if a particular number is blocked, use the method
+ * {@link #isBlocked(Context, String)}.
* </p>
* </dd>
* <dt><b>Query</b></dt>
@@ -120,8 +123,12 @@
* new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
* BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null);
* </pre>
- * To check if a particular number is blocked, use the method
- * {@link #isBlocked(Context, String)}.
+ * </p>
+ * </dd>
+ * <dt><b>Unblock</b></dt>
+ * <dd>
+ * <p>
+ * Use the method {@link #unblock(Context, String)} to unblock numbers.
* </p>
* </dd>
*
@@ -206,9 +213,15 @@
public static final String METHOD_IS_BLOCKED = "is_blocked";
/** @hide */
+ public static final String METHOD_UNBLOCK= "unblock";
+
+ /** @hide */
public static final String RES_NUMBER_IS_BLOCKED = "blocked";
/** @hide */
+ public static final String RES_NUM_ROWS_DELETED = "num_deleted";
+
+ /** @hide */
public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS =
"can_current_user_block_numbers";
@@ -217,9 +230,15 @@
/**
* Returns whether a given number is in the blocked list.
+ *
+ * <p> This matches the {@code phoneNumber} against the
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column, and the E164 representation of the
+ * {@code phoneNumber} with the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+ *
* <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
* context {@code context}, this method will throw an {@link UnsupportedOperationException}.
*/
+ @WorkerThread
public static boolean isBlocked(Context context, String phoneNumber) {
final Bundle res = context.getContentResolver().call(
AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
@@ -227,6 +246,30 @@
}
/**
+ * Unblocks the {@code phoneNumber} if it is blocked.
+ *
+ * <p> Returns the number of rows deleted in the blocked number provider as a result of unblock.
+ *
+ * <p> This deletes all rows where the {@code phoneNumber} matches the
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or the E164 representation of the
+ * {@code phoneNumber} matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+ *
+ * <p>To delete rows based on exact match with specific columns such as
+ * {@link BlockedNumbers#COLUMN_ID} use
+ * {@link android.content.ContentProvider#delete(Uri, String, String[])} with
+ * {@link BlockedNumbers#CONTENT_URI} URI.
+ *
+ * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
+ * context {@code context}, this method will throw an {@link UnsupportedOperationException}.
+ */
+ @WorkerThread
+ public static int unblock(Context context, String phoneNumber) {
+ final Bundle res = context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_UNBLOCK, phoneNumber, null);
+ return res.getInt(RES_NUM_ROWS_DELETED, 0);
+ }
+
+ /**
* Returns {@code true} if blocking numbers is supported for the current user.
* <p> Typically, blocking numbers is only supported for one user at a time.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d8937b4..f7e0e03 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -322,6 +322,20 @@
"android.settings.PRIVACY_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of VPN.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VPN_SETTINGS =
+ "android.settings.VPN_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of Wi-Fi.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -7699,6 +7713,16 @@
public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
/**
+ * Device Idle (Doze) specific settings for watches. See {@code #DEVICE_IDLE_CONSTANTS}
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.DeviceIdleController.Constants
+ */
+ public static final String DEVICE_IDLE_CONSTANTS_WATCH = "device_idle_constants_watch";
+
+ /**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 733a092..9530aca 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -16,6 +16,7 @@
package android.security;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.security.net.config.ApplicationConfig;
@@ -104,4 +105,13 @@
ManifestConfigSource source = new ManifestConfigSource(appContext);
return new ApplicationConfig(source);
}
+
+ /**
+ * Handle an update to the system or user certificate stores.
+ * @hide
+ */
+ @TestApi
+ public void handleTrustStorageUpdate() {
+ ApplicationConfig.getDefaultInstance().handleTrustStorageUpdate();
+ }
}
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 4de36cd..fadea56 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -17,6 +17,7 @@
package android.security.net.config;
import android.util.Pair;
+import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.net.ssl.X509TrustManager;
@@ -146,6 +147,20 @@
return getConfigForHostname(hostname).isCleartextTrafficPermitted();
}
+ public void handleTrustStorageUpdate() {
+ ensureInitialized();
+ mDefaultConfig.handleTrustStorageUpdate();
+ if (mConfigs != null) {
+ Set<NetworkSecurityConfig> updatedConfigs =
+ new HashSet<NetworkSecurityConfig>(mConfigs.size());
+ for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) {
+ if (updatedConfigs.add(entry.second)) {
+ entry.second.handleTrustStorageUpdate();
+ }
+ }
+ }
+ }
+
private void ensureInitialized() {
synchronized(mLock) {
if (mInitialized) {
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index f3272e4..4bcc405 100644
--- a/core/java/android/security/net/config/CertificateSource.java
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -25,4 +25,5 @@
X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
X509Certificate findByIssuerAndSignature(X509Certificate cert);
Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert);
+ void handleTrustStorageUpdate();
}
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index 742d430..45cd0f0 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -64,4 +64,8 @@
public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
return mSource.findAllByIssuerAndSignature(cert);
}
+
+ public void handleTrustStorageUpdate() {
+ mSource.handleTrustStorageUpdate();
+ }
}
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index b2c068c..e3c9d65 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -126,6 +126,13 @@
});
}
+ @Override
+ public void handleTrustStorageUpdate() {
+ synchronized (mLock) {
+ mCertificates = null;
+ }
+ }
+
private static interface CertSelector {
boolean match(X509Certificate cert);
}
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
index ba5dd83..c68f385 100644
--- a/core/java/android/security/net/config/KeyStoreCertificateSource.java
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -105,4 +105,9 @@
}
return certs;
}
+
+ @Override
+ public void handleTrustStorageUpdate() {
+ // Nothing to do.
+ }
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 6d6a92a..b3a37d0 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -117,12 +117,6 @@
}
}
- void onTrustStoreChange() {
- synchronized (mAnchorsLock) {
- mAnchors = null;
- }
- }
-
/** @hide */
public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
@@ -154,6 +148,16 @@
return certs;
}
+ public void handleTrustStorageUpdate() {
+ synchronized (mAnchorsLock) {
+ mAnchors = null;
+ for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+ ref.handleTrustStorageUpdate();
+ }
+ }
+ getTrustManager().handleTrustStorageUpdate();
+ }
+
/**
* Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
*
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index 81cad79..f2c718cd 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -157,4 +157,11 @@
return mIssuers.clone();
}
}
+
+ public void handleTrustStorageUpdate() {
+ synchronized (mIssuersLock) {
+ mIssuers = null;
+ mDelegate.handleTrustStorageUpdate();
+ }
+ }
}
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index 22fbee2..78669c5 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -115,4 +115,9 @@
}
return certs;
}
+
+ @Override
+ public void handleTrustStorageUpdate() {
+ // Nothing to do, resource sources never change.
+ }
}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index d57d0f5..4a5f827 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -111,7 +111,7 @@
if (parser.next() != XmlPullParser.TEXT) {
throw new ParserException(parser, "Missing pin digest");
}
- String digest = parser.getText();
+ String digest = parser.getText().trim();
byte[] decodedDigest = null;
try {
decodedDigest = Base64.decode(digest, 0);
@@ -168,7 +168,7 @@
if (parser.next() != XmlPullParser.TEXT) {
throw new ParserException(parser, "Domain name missing");
}
- String domain = parser.getText().toLowerCase(Locale.US);
+ String domain = parser.getText().trim().toLowerCase(Locale.US);
if (parser.next() != XmlPullParser.END_TAG) {
throw new ParserException(parser, "domain contains additional elements");
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 816ecde..7af0b05 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -367,11 +367,6 @@
@Override
public void onActionModeFinished(ActionMode mode) {
}
-
- /** {@inheritDoc} */
- @Override
- public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
- }
// end Window.Callback methods
// begin public api
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index 302a08d..5d94b06 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -58,11 +58,22 @@
public StrictJarFile(String fileName)
throws IOException, SecurityException {
- this(fileName, true);
+ this(fileName, true, true);
}
- public StrictJarFile(String fileName, boolean verify)
- throws IOException, SecurityException {
+ /**
+ *
+ * @param verify whether to verify the file's JAR signatures and collect the corresponding
+ * signer certificates.
+ * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
+ * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or
+ * {@code false} to ignore any such protections. This parameter is ignored when
+ * {@code verify} is {@code false}.
+ */
+ public StrictJarFile(String fileName,
+ boolean verify,
+ boolean signatureSchemeRollbackProtectionsEnforced)
+ throws IOException, SecurityException {
this.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r");
@@ -73,7 +84,12 @@
if (verify) {
HashMap<String, byte[]> metaEntries = getMetaEntries();
this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
- this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
+ this.verifier =
+ new StrictJarVerifier(
+ fileName,
+ manifest,
+ metaEntries,
+ signatureSchemeRollbackProtectionsEnforced);
Set<String> files = manifest.getEntries().keySet();
for (String file : files) {
if (findEntry(file) == null) {
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
index 0546a5f..6da50ba 100644
--- a/core/java/android/util/jar/StrictJarVerifier.java
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -72,6 +72,7 @@
private final StrictJarManifest manifest;
private final HashMap<String, byte[]> metaEntries;
private final int mainAttributesEnd;
+ private final boolean signatureSchemeRollbackProtectionsEnforced;
private final Hashtable<String, HashMap<String, Attributes>> signatures =
new Hashtable<String, HashMap<String, Attributes>>(5);
@@ -164,13 +165,19 @@
*
* @param name
* the name of the JAR file being verified.
+ *
+ * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
+ * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or
+ * {@code false} to ignore any such protections.
*/
StrictJarVerifier(String name, StrictJarManifest manifest,
- HashMap<String, byte[]> metaEntries) {
+ HashMap<String, byte[]> metaEntries, boolean signatureSchemeRollbackProtectionsEnforced) {
jarName = name;
this.manifest = manifest;
this.metaEntries = metaEntries;
this.mainAttributesEnd = manifest.getMainAttributesEnd();
+ this.signatureSchemeRollbackProtectionsEnforced =
+ signatureSchemeRollbackProtectionsEnforced;
}
/**
@@ -357,40 +364,42 @@
return;
}
- // Check whether APK Signature Scheme v2 signature was stripped.
- String apkSignatureSchemeIdList =
- attributes.getValue(
- ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
- if (apkSignatureSchemeIdList != null) {
- // This field contains a comma-separated list of APK signature scheme IDs which were
- // used to sign this APK. If an ID is known to us, it means signatures of that scheme
- // were stripped from the APK because otherwise we wouldn't have fallen back to
- // verifying the APK using the JAR signature scheme.
- boolean v2SignatureGenerated = false;
- StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
- while (tokenizer.hasMoreTokens()) {
- String idText = tokenizer.nextToken().trim();
- if (idText.isEmpty()) {
- continue;
+ // If requested, check whether APK Signature Scheme v2 signature was stripped.
+ if (signatureSchemeRollbackProtectionsEnforced) {
+ String apkSignatureSchemeIdList =
+ attributes.getValue(
+ ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
+ if (apkSignatureSchemeIdList != null) {
+ // This field contains a comma-separated list of APK signature scheme IDs which
+ // were used to sign this APK. If an ID is known to us, it means signatures of that
+ // scheme were stripped from the APK because otherwise we wouldn't have fallen back
+ // to verifying the APK using the JAR signature scheme.
+ boolean v2SignatureGenerated = false;
+ StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
+ while (tokenizer.hasMoreTokens()) {
+ String idText = tokenizer.nextToken().trim();
+ if (idText.isEmpty()) {
+ continue;
+ }
+ int id;
+ try {
+ id = Integer.parseInt(idText);
+ } catch (Exception ignored) {
+ continue;
+ }
+ if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
+ // This APK was supposed to be signed with APK Signature Scheme v2 but no
+ // such signature was found.
+ v2SignatureGenerated = true;
+ break;
+ }
}
- int id;
- try {
- id = Integer.parseInt(idText);
- } catch (Exception ignored) {
- continue;
- }
- if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
- // This APK was supposed to be signed with APK Signature Scheme v2 but no such
- // signature was found.
- v2SignatureGenerated = true;
- break;
- }
- }
- if (v2SignatureGenerated) {
- throw new SecurityException(signatureFile + " indicates " + jarName + " is signed"
- + " using APK Signature Scheme v2, but no such signature was found."
- + " Signature stripped?");
+ if (v2SignatureGenerated) {
+ throw new SecurityException(signatureFile + " indicates " + jarName
+ + " is signed using APK Signature Scheme v2, but no such signature was"
+ + " found. Signature stripped?");
+ }
}
}
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index a12434c..41c44f1 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -51,8 +52,8 @@
* @param paint The paint used when the layer is drawn into the destination canvas.
* @see View#setLayerPaint(android.graphics.Paint)
*/
- public void setLayerPaint(Paint paint) {
- nSetLayerPaint(mFinalizer.get(), paint.getNativeInstance());
+ public void setLayerPaint(@Nullable Paint paint) {
+ nSetLayerPaint(mFinalizer.get(), paint != null ? paint.getNativeInstance() : 0);
mRenderer.pushLayerUpdate(this);
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 70d0513..707300f 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -95,5 +95,5 @@
/**
* Called when Keyboard Shortcuts are requested for the window.
*/
- void requestAppKeyboardShortcuts(IResultReceiver receiver);
+ void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d8b7421..7af4a1f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -387,7 +387,7 @@
*
* @param receiver The receiver to deliver the results to.
*/
- void requestAppKeyboardShortcuts(IResultReceiver receiver);
+ void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId);
/**
* Retrieves the current stable insets from the primary display.
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
index 2c9006d..c2bd347 100644
--- a/core/java/android/view/KeyboardShortcutInfo.java
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -30,31 +30,46 @@
private final CharSequence mLabel;
private final Icon mIcon;
private final char mBaseCharacter;
+ private final int mKeycode;
private final int mModifiers;
/**
* @param label The label that identifies the action performed by this shortcut.
* @param icon An icon that identifies the action performed by this shortcut.
- * @param baseCharacter The character that triggers the shortcut.
+ * @param keycode The keycode that triggers the shortcut. This should be a valid constant
+ * defined in {@link KeyEvent}.
* @param modifiers The set of modifiers that, combined with the key, trigger the shortcut.
* These should be a combination of {@link KeyEvent#META_CTRL_ON},
- * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and
- * {@link KeyEvent#META_ALT_ON}.
+ * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON},
+ * {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_FUNCTION_ON} and
+ * {@link KeyEvent#META_SYM_ON}.
*
* @hide
*/
public KeyboardShortcutInfo(
- @Nullable CharSequence label, @Nullable Icon icon, char baseCharacter, int modifiers) {
+ @Nullable CharSequence label, @Nullable Icon icon, int keycode, int modifiers) {
mLabel = label;
mIcon = icon;
- checkArgument(baseCharacter != MIN_VALUE);
- mBaseCharacter = baseCharacter;
+ mBaseCharacter = MIN_VALUE;
+ checkArgument(keycode > KeyEvent.KEYCODE_UNKNOWN && keycode <= KeyEvent.getMaxKeyCode());
+ mKeycode = keycode;
mModifiers = modifiers;
}
/**
- * Convenience constructor for shortcuts with a label and no icon.
- *
+ * @param label The label that identifies the action performed by this shortcut.
+ * @param keycode The keycode that triggers the shortcut. This should be a valid constant
+ * defined in {@link KeyEvent}.
+ * @param modifiers The set of modifiers that, combined with the key, trigger the shortcut.
+ * These should be a combination of {@link KeyEvent#META_CTRL_ON},
+ * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and
+ * {@link KeyEvent#META_ALT_ON}.
+ */
+ public KeyboardShortcutInfo(CharSequence label, int keycode, int modifiers) {
+ this(label, null, keycode, modifiers);
+ }
+
+ /**
* @param label The label that identifies the action performed by this shortcut.
* @param baseCharacter The character that triggers the shortcut.
* @param modifiers The set of modifiers that, combined with the key, trigger the shortcut.
@@ -66,14 +81,16 @@
mLabel = label;
checkArgument(baseCharacter != MIN_VALUE);
mBaseCharacter = baseCharacter;
+ mKeycode = KeyEvent.KEYCODE_UNKNOWN;
mModifiers = modifiers;
mIcon = null;
}
private KeyboardShortcutInfo(Parcel source) {
mLabel = source.readCharSequence();
- mIcon = (Icon) source.readParcelable(null);
+ mIcon = source.readParcelable(null);
mBaseCharacter = (char) source.readInt();
+ mKeycode = source.readInt();
mModifiers = source.readInt();
}
@@ -96,7 +113,16 @@
}
/**
- * Returns the base character that, combined with the modifiers, triggers this shortcut.
+ * Returns the base keycode that, combined with the modifiers, triggers this shortcut. If the
+ * base character was set instead, returns {@link KeyEvent#KEYCODE_UNKNOWN}.
+ */
+ public int getKeycode() {
+ return mKeycode;
+ }
+
+ /**
+ * Returns the base character that, combined with the modifiers, triggers this shortcut. If the
+ * keycode was set instead, returns {@link Character#MIN_VALUE}.
*/
public char getBaseCharacter() {
return mBaseCharacter;
@@ -119,6 +145,7 @@
dest.writeCharSequence(mLabel);
dest.writeParcelable(mIcon, 0);
dest.writeInt(mBaseCharacter);
+ dest.writeInt(mKeycode);
dest.writeInt(mModifiers);
}
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index a45c18d..a19254f 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -259,7 +259,7 @@
return nSetLayerType(mNativeRenderNode, layerType);
}
- public boolean setLayerPaint(Paint paint) {
+ public boolean setLayerPaint(@Nullable Paint paint) {
return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0);
}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 1be4810..1a712c3 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -133,7 +134,6 @@
*/
public TextureView(Context context) {
super(context);
- init();
}
/**
@@ -144,7 +144,6 @@
*/
public TextureView(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
}
/**
@@ -158,7 +157,6 @@
*/
public TextureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init();
}
/**
@@ -176,11 +174,6 @@
*/
public TextureView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init();
- }
-
- private void init() {
- mLayerPaint = new Paint();
}
/**
@@ -260,7 +253,7 @@
* method will however be taken into account when rendering the content of
* this TextureView.
*
- * @param layerType The ype of layer to use with this view, must be one of
+ * @param layerType The type of layer to use with this view, must be one of
* {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
* {@link #LAYER_TYPE_HARDWARE}
* @param paint The paint used to compose the layer. This argument is optional
@@ -268,16 +261,16 @@
* {@link #LAYER_TYPE_NONE}
*/
@Override
- public void setLayerType(int layerType, Paint paint) {
- if (paint != mLayerPaint) {
- mLayerPaint = paint == null ? new Paint() : paint;
- invalidate();
- }
+ public void setLayerType(int layerType, @Nullable Paint paint) {
+ setLayerPaint(paint);
}
@Override
- public void setLayerPaint(Paint paint) {
- setLayerType(/* ignored */ 0, paint);
+ public void setLayerPaint(@Nullable Paint paint) {
+ if (paint != mLayerPaint) {
+ mLayerPaint = paint;
+ invalidate();
+ }
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6d35a58..83c6e9e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11741,7 +11741,7 @@
}
}
- /**
+ /**
* Utility method to retrieve the inverse of the current mMatrix property.
* We cache the matrix to avoid recalculating it when transform properties
* have not changed.
@@ -15626,7 +15626,7 @@
*
* @attr ref android.R.styleable#View_layerType
*/
- public void setLayerType(int layerType, Paint paint) {
+ public void setLayerType(int layerType, @Nullable Paint paint) {
if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
+ "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
@@ -15645,8 +15645,7 @@
}
mLayerType = layerType;
- final boolean layerDisabled = (mLayerType == LAYER_TYPE_NONE);
- mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
+ mLayerPaint = mLayerType == LAYER_TYPE_NONE ? null : paint;
mRenderNode.setLayerPaint(mLayerPaint);
// draw() behaves differently if we are on a layer, so we need to
@@ -15680,12 +15679,12 @@
*
* @see #setLayerType(int, android.graphics.Paint)
*/
- public void setLayerPaint(Paint paint) {
+ public void setLayerPaint(@Nullable Paint paint) {
int layerType = getLayerType();
if (layerType != LAYER_TYPE_NONE) {
- mLayerPaint = paint == null ? new Paint() : paint;
+ mLayerPaint = paint;
if (layerType == LAYER_TYPE_HARDWARE) {
- if (mRenderNode.setLayerPaint(mLayerPaint)) {
+ if (mRenderNode.setLayerPaint(paint)) {
invalidateViewProperty(false, false);
}
} else {
@@ -16855,7 +16854,7 @@
canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
} else {
// use layer paint to draw the bitmap, merging the two alphas, but also restore
- int layerPaintAlpha = mLayerPaint.getAlpha();
+ int layerPaintAlpha = mLayerPaint != null ? mLayerPaint.getAlpha() : 255;
mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
mLayerPaint.setAlpha(layerPaintAlpha);
@@ -22187,7 +22186,7 @@
/**
* @hide
*/
- public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> data) {
+ public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> data, int deviceId) {
// Do nothing.
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a2295ce..9418aaf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,7 +16,8 @@
package android.view;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
+import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -96,8 +97,8 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -146,9 +147,6 @@
*/
static final int MAX_TRACKBALL_DELAY = 250;
- private static final int RESIZE_MODE_FREEFORM = 0;
- private static final int RESIZE_MODE_DOCKED_DIVIDER = 1;
-
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
@@ -233,6 +231,7 @@
boolean mIsAnimating;
private boolean mDragResizing;
+ private boolean mInvalidateRootRequested;
private int mResizeMode;
private int mCanvasOffsetX;
private int mCanvasOffsetY;
@@ -1828,12 +1827,12 @@
final boolean dragResizing = freeformResizing || dockedResizing;
if (mDragResizing != dragResizing) {
if (dragResizing) {
- startDragResizing(mPendingBackDropFrame,
- mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
- mPendingStableInsets);
mResizeMode = freeformResizing
? RESIZE_MODE_FREEFORM
: RESIZE_MODE_DOCKED_DIVIDER;
+ startDragResizing(mPendingBackDropFrame,
+ mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
+ mPendingStableInsets, mResizeMode);
} else {
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
@@ -2438,6 +2437,11 @@
@Override
public void onHardwarePostDraw(DisplayListCanvas canvas) {
drawAccessibilityFocusedDrawableIfNeeded(canvas);
+ synchronized (mWindowCallbacks) {
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onPostDraw(canvas);
+ }
+ }
}
/**
@@ -2675,7 +2679,8 @@
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// If accessibility focus moved, always invalidate the root.
- boolean invalidateRoot = accessibilityFocusDirty;
+ boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
+ mInvalidateRootRequested = false;
// Draw with hardware renderer.
mIsAnimating = false;
@@ -2908,6 +2913,14 @@
return mAttachInfo.mAccessibilityFocusDrawable;
}
+ /**
+ * Requests that the root render node is invalidated next time we perform a draw, such that
+ * {@link WindowCallbacks#onPostDraw} gets called.
+ */
+ public void requestInvalidateRootRenderNode() {
+ mInvalidateRootRequested = true;
+ }
+
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
@@ -3594,8 +3607,9 @@
handleDispatchWindowShown();
} break;
case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
- IResultReceiver receiver = (IResultReceiver) msg.obj;
- handleRequestKeyboardShortcuts(receiver);
+ final IResultReceiver receiver = (IResultReceiver) msg.obj;
+ final int deviceId = msg.arg1;
+ handleRequestKeyboardShortcuts(receiver, deviceId);
} break;
case MSG_UPDATE_POINTER_ICON: {
MotionEvent event = (MotionEvent) msg.obj;
@@ -5516,11 +5530,11 @@
mAttachInfo.mTreeObserver.dispatchOnWindowShown();
}
- public void handleRequestKeyboardShortcuts(IResultReceiver receiver) {
+ public void handleRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
Bundle data = new Bundle();
ArrayList<KeyboardShortcutGroup> list = new ArrayList<>();
if (mView != null) {
- mView.requestKeyboardShortcuts(list);
+ mView.requestKeyboardShortcuts(list, deviceId);
}
data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list);
try {
@@ -6482,8 +6496,9 @@
}
}
- public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver) {
- mHandler.obtainMessage(MSG_REQUEST_KEYBOARD_SHORTCUTS, receiver).sendToTarget();
+ public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
+ mHandler.obtainMessage(
+ MSG_REQUEST_KEYBOARD_SHORTCUTS, deviceId, 0, receiver).sendToTarget();
}
/**
@@ -7060,11 +7075,11 @@
}
@Override
- public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
- ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.dispatchRequestKeyboardShortcuts(receiver);
- }
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
+ ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchRequestKeyboardShortcuts(receiver, deviceId);
+ }
}
}
@@ -7088,13 +7103,13 @@
* Start a drag resizing which will inform all listeners that a window resize is taking place.
*/
private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ Rect stableInsets, int resizeMode) {
if (!mDragResizing) {
mDragResizing = true;
synchronized (mWindowCallbacks) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen,
- systemInsets, stableInsets);
+ systemInsets, stableInsets, resizeMode);
}
}
mFullRedrawNeeded = true;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 63f3744..36ee3e6 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -565,9 +565,10 @@
*
* @param data The data list to populate with shortcuts.
* @param menu The current menu, which may be null.
+ * @param deviceId The id for the connected device the shortcuts should be provided for.
*/
default public void onProvideKeyboardShortcuts(
- List<KeyboardShortcutGroup> data, @Nullable Menu menu) { };
+ List<KeyboardShortcutGroup> data, @Nullable Menu menu, int deviceId) { };
}
/** @hide */
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index bed74e9..8f2d2e1 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -154,8 +154,9 @@
}
@Override
- public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
- mWrapped.onProvideKeyboardShortcuts(data, menu);
+ public void onProvideKeyboardShortcuts(
+ List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
+ mWrapped.onProvideKeyboardShortcuts(data, menu, deviceId);
}
}
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
index d2bfca7..b2dc1e9 100644
--- a/core/java/android/view/WindowCallbacks.java
+++ b/core/java/android/view/WindowCallbacks.java
@@ -26,6 +26,11 @@
* @hide
*/
public interface WindowCallbacks {
+
+ public static final int RESIZE_MODE_INVALID = -1;
+ public static final int RESIZE_MODE_FREEFORM = 0;
+ public static final int RESIZE_MODE_DOCKED_DIVIDER = 1;
+
/**
* Called by the system when the window got changed by the user, before the layouter got called.
* It also gets called when the insets changed, or when the window switched between a fullscreen
@@ -51,7 +56,7 @@
* @param stableInsets The stable insets for the window.
*/
void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets);
+ Rect stableInsets, int resizeMode);
/**
* Called when a drag resize ends.
@@ -69,4 +74,13 @@
* @param reportNextDraw Whether it should report when the requested draw finishes.
*/
void onRequestDraw(boolean reportNextDraw);
+
+ /**
+ * Called after all the content has drawn and the callback now has the ability to draw something
+ * on top of everything. Call {@link ViewRootImpl#requestInvalidateRootRenderNode} when this
+ * content needs to be redrawn.
+ *
+ * @param canvas The canvas to draw on.
+ */
+ void onPostDraw(DisplayListCanvas canvas);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 17f1991..03dcf99 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -158,7 +158,7 @@
*
* @hide
*/
- public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver);
+ public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId);
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
/**
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 6e11671..f8c7d68 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -125,7 +125,8 @@
}
@Override
- public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver) {
+ public void requestAppKeyboardShortcuts(
+ final KeyboardShortcutsReceiver receiver, int deviceId) {
IResultReceiver resultReceiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
@@ -136,7 +137,7 @@
};
try {
WindowManagerGlobal.getWindowManagerService()
- .requestAppKeyboardShortcuts(resultReceiver);
+ .requestAppKeyboardShortcuts(resultReceiver, deviceId);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 655c9b3..7f44bac 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -59,4 +59,8 @@
boolean touchExplorationEnabled);
IBinder getWindowToken(int windowId, int userId);
+
+ void enableAccessibilityService(in ComponentName service, int userId);
+
+ void disableAccessibilityService(in ComponentName service, int userId);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1d89cd0..cf5cc3e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -317,6 +317,7 @@
/**
* The InputConnection that was last retrieved from the served view.
*/
+ InputConnection mServedInputConnection;
ControlledInputConnectionWrapper mServedInputConnectionWrapper;
/**
* The completions that were last provided by the served view.
@@ -497,7 +498,7 @@
// from a thread that created mServedView. That could happen
// the current activity is running in the system process.
// In that case, we really should not call
- // mServedInputConnectionWrapper.finishComposingText().
+ // mServedInputConnection.finishComposingText.
if (checkFocusNoStartInput(mHasBeenInactive, false)) {
final int reason = active ?
InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
@@ -561,9 +562,7 @@
@Override
public String toString() {
- return "ControlledInputConnectionWrapper{"
- + "connection=" + getInputConnection()
- + " mActive=" + mActive
+ return "ControlledInputConnectionWrapper{mActive=" + mActive
+ " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
+ "}";
}
@@ -781,8 +780,7 @@
*/
public boolean isAcceptingText() {
checkFocus();
- return mServedInputConnectionWrapper != null &&
- mServedInputConnectionWrapper.getInputConnection() != null;
+ return mServedInputConnection != null;
}
/**
@@ -817,6 +815,7 @@
*/
void clearConnectionLocked() {
mCurrentTextBoxAttribute = null;
+ mServedInputConnection = null;
if (mServedInputConnectionWrapper != null) {
mServedInputConnectionWrapper.deactivate();
mServedInputConnectionWrapper = null;
@@ -849,21 +848,16 @@
* Notifies the served view that the current InputConnection will no longer be used.
*/
private void notifyInputConnectionFinished() {
- if (mServedView == null || mServedInputConnectionWrapper == null) {
- return;
- }
- final InputConnection inputConnection = mServedInputConnectionWrapper.getInputConnection();
- if (inputConnection == null) {
- return;
- }
- // We need to tell the previously served view that it is no
- // longer the input target, so it can reset its state. Schedule
- // this call on its window's Handler so it will be on the correct
- // thread and outside of our lock.
- ViewRootImpl viewRootImpl = mServedView.getViewRootImpl();
- if (viewRootImpl != null) {
- // This will result in a call to reportFinishInputConnection() below.
- viewRootImpl.dispatchFinishInputConnection(inputConnection);
+ if (mServedView != null && mServedInputConnection != null) {
+ // We need to tell the previously served view that it is no
+ // longer the input target, so it can reset its state. Schedule
+ // this call on its window's Handler so it will be on the correct
+ // thread and outside of our lock.
+ ViewRootImpl viewRootImpl = mServedView.getViewRootImpl();
+ if (viewRootImpl != null) {
+ // This will result in a call to reportFinishInputConnection() below.
+ viewRootImpl.dispatchFinishInputConnection(mServedInputConnection);
+ }
}
}
@@ -872,13 +866,7 @@
* @hide
*/
public void reportFinishInputConnection(InputConnection ic) {
- final InputConnection currentConnection;
- if (mServedInputConnectionWrapper == null) {
- currentConnection = null;
- } else {
- currentConnection = mServedInputConnectionWrapper.getInputConnection();
- }
- if (currentConnection != ic) {
+ if (mServedInputConnection != ic) {
ic.finishComposingText();
// To avoid modifying the public InputConnection interface
if (ic instanceof BaseInputConnection) {
@@ -1254,6 +1242,7 @@
mServedConnecting = false;
// Notify the served view that its previous input connection is finished
notifyInputConnectionFinished();
+ mServedInputConnection = ic;
ControlledInputConnectionWrapper servedContext;
final int missingMethodFlags;
if (ic != null) {
@@ -1424,7 +1413,7 @@
return false;
}
- final ControlledInputConnectionWrapper ic;
+ InputConnection ic = null;
synchronized (mH) {
if (mServedView == mNextServedView && !forceNewFocus) {
return false;
@@ -1444,7 +1433,7 @@
return false;
}
- ic = mServedInputConnectionWrapper;
+ ic = mServedInputConnection;
mServedView = mNextServedView;
mCurrentTextBoxAttribute = null;
@@ -2293,7 +2282,7 @@
} else {
p.println(" mCurrentTextBoxAttribute: null");
}
- p.println(" mServedInputConnectionWrapper=" + mServedInputConnectionWrapper);
+ p.println(" mServedInputConnection=" + mServedInputConnection);
p.println(" mCompletions=" + Arrays.toString(mCompletions));
p.println(" mCursorRect=" + mCursorRect);
p.println(" mCursorSelStart=" + mCursorSelStart
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d1b5fc8..18687c9 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -129,8 +129,8 @@
*/
private static final int ANIMATION_STYLE_DEFAULT = -1;
- private final int[] mDrawingLocation = new int[2];
- private final int[] mScreenLocation = new int[2];
+ private final int[] mTmpDrawingLocation = new int[2];
+ private final int[] mTmpScreenLocation = new int[2];
private final Rect mTempRect = new Rect();
private Context mContext;
@@ -222,7 +222,7 @@
mDecorView.getLayoutParams();
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
- mAnchoredGravity));
+ p.width, p.height, mAnchoredGravity));
update(p.x, p.y, -1, -1, true);
}
}
@@ -1123,7 +1123,7 @@
TransitionManager.endTransitions(mDecorView);
- unregisterForViewTreeChanges();
+ detachFromAnchor();
mIsShowing = true;
mIsDropdown = false;
@@ -1206,7 +1206,7 @@
TransitionManager.endTransitions(mDecorView);
- registerForViewTreeChanges(anchor, xoff, yoff, gravity);
+ attachToAnchor(anchor, xoff, yoff, gravity);
mIsShowing = true;
mIsDropdown = true;
@@ -1214,7 +1214,8 @@
final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
preparePopup(p);
- final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
+ final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
+ p.width, p.height, gravity);
updateAboveAnchor(aboveAnchor);
invokePopup(p);
@@ -1494,120 +1495,138 @@
* to reclaim space. If scrolling is not possible or not enough, the popup
* window gets moved on top of the anchor.
* <p>
- * The height must have been set on the layout parameters prior to calling
- * this method.
+ * The results of positioning are placed in {@code outParams}.
*
* @param anchor the view on which the popup window must be anchored
- * @param p the layout parameters used to display the drop down
- * @param xoff horizontal offset used to adjust for background padding
- * @param yoff vertical offset used to adjust for background padding
+ * @param outParams the layout parameters used to display the drop down
+ * @param xOffset absolute horizontal offset from the top of the anchor
+ * @param yOffset absolute vertical offset from the top of the anchor
* @param gravity horizontal gravity specifying popup alignment
* @return true if the popup is translated upwards to fit on screen
*/
- private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
- int yoff, int gravity) {
+ private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+ int xOffset, int yOffset, int width, int height, int gravity) {
final int anchorHeight = anchor.getHeight();
final int anchorWidth = anchor.getWidth();
if (mOverlapAnchor) {
- yoff -= anchorHeight;
+ yOffset -= anchorHeight;
}
- anchor.getLocationInWindow(mDrawingLocation);
- p.x = mDrawingLocation[0] + xoff;
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ // Initially, align to the bottom-left corner of the anchor plus offsets.
+ final int[] drawingLocation = mTmpDrawingLocation;
+ anchor.getLocationInWindow(drawingLocation);
+ outParams.x = drawingLocation[0] + xOffset;
+ outParams.y = drawingLocation[1] + anchorHeight + yOffset;
+ // If we need to adjust for gravity RIGHT, align to the bottom-right
+ // corner of the anchor (still accounting for offsets).
final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
& Gravity.HORIZONTAL_GRAVITY_MASK;
if (hgrav == Gravity.RIGHT) {
- // Flip the location to align the right sides of the popup and
- // anchor instead of left.
- p.x -= mPopupWidth - anchorWidth;
+ outParams.x -= width - anchorWidth;
}
- boolean onTop = false;
+ // Let the window manager know to align the top to y.
+ outParams.gravity = Gravity.LEFT | Gravity.TOP;
- p.gravity = Gravity.LEFT | Gravity.TOP;
+ final int[] screenLocation = mTmpScreenLocation;
+ anchor.getLocationOnScreen(screenLocation);
- anchor.getLocationOnScreen(mScreenLocation);
final Rect displayFrame = new Rect();
anchor.getWindowVisibleDisplayFrame(displayFrame);
- final int screenY = mScreenLocation[1] + anchorHeight + yoff;
+ boolean onTop = false;
+
final View root = anchor.getRootView();
- if (screenY + mPopupHeight > displayFrame.bottom
- || p.x + mPopupWidth - root.getWidth() > 0) {
- // If the drop down disappears at the bottom of the screen, we try
- // to scroll a parent scrollview or move the drop down back up on
- // top of the edit box.
+ final int screenY = screenLocation[1] + anchorHeight + yOffset;
+ final boolean tooFarDown = screenY + height > displayFrame.bottom;
+ final boolean tooFarRight = outParams.x + width > root.getWidth();
+ if (tooFarDown || tooFarRight) {
+ // If the popup extends beyond the visible area, try to scroll the
+ // parent so that it is fully visible.
if (mAllowScrollingAnchorParent) {
final int scrollX = anchor.getScrollX();
final int scrollY = anchor.getScrollY();
- final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
- scrollY + mPopupHeight + anchorHeight + yoff);
+ final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
+ scrollY + height + anchorHeight + yOffset);
anchor.requestRectangleOnScreen(r, true);
}
- // Now we re-evaluate the space available, and decide from that
- // whether the pop-up will go above or below the anchor.
- anchor.getLocationInWindow(mDrawingLocation);
- p.x = mDrawingLocation[0] + xoff;
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ // Update for the new anchor position.
+ anchor.getLocationInWindow(drawingLocation);
+ outParams.x = drawingLocation[0] + xOffset;
+ outParams.y = drawingLocation[1] + anchorHeight + yOffset;
// Preserve the gravity adjustment.
if (hgrav == Gravity.RIGHT) {
- p.x -= mPopupWidth - anchorWidth;
+ outParams.x -= width - anchorWidth;
}
- // Determine whether there is more space above or below the anchor.
- anchor.getLocationOnScreen(mScreenLocation);
- onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
- (mScreenLocation[1] - yoff - displayFrame.top);
- if (!mOverlapAnchor) {
+ final int newScreenY = screenLocation[1] + anchorHeight + yOffset;
+ final boolean stillTooFarDown = newScreenY + height > displayFrame.bottom;
+ if (stillTooFarDown) {
+ // If the popup is still too far down, re-evaluate the space
+ // available and decide whether the pop-up will go above or
+ // below the anchor.
+ anchor.getLocationOnScreen(screenLocation);
+
+ final int below = displayFrame.bottom - screenLocation[1] - anchorHeight - yOffset;
+ final int above = screenLocation[1] - displayFrame.top + yOffset;
+ onTop = above > below;
+
if (onTop) {
- p.gravity = Gravity.LEFT | Gravity.BOTTOM;
- p.y = root.getHeight() - mDrawingLocation[1] + yoff;
- } else {
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ // Move everything up.
+ if (mOverlapAnchor) {
+ yOffset += anchorHeight;
+ }
+ outParams.y = drawingLocation[1] - height + yOffset;
}
}
}
if (mClipToScreen) {
- final int winOffsetX = mScreenLocation[0] - mDrawingLocation[0];
- final int winOffsetY = mScreenLocation[1] - mDrawingLocation[1];
- p.x += winOffsetX;
- p.y += winOffsetY;
- final int displayFrameWidth = displayFrame.right - displayFrame.left;
- final int right = p.x + p.width;
+ // Use screen coordinates for comparison against display frame.
+ final int winOffsetX = screenLocation[0] - drawingLocation[0];
+ final int winOffsetY = screenLocation[1] - drawingLocation[1];
+ outParams.x += winOffsetX;
+ outParams.y += winOffsetY;
+
+ final int right = outParams.x + width;
if (right > displayFrame.right) {
- p.x -= right - displayFrame.right;
+ // The popup is too far right, move it back in.
+ outParams.x -= right - displayFrame.right;
}
- if (p.x < displayFrame.left) {
- p.x = displayFrame.left;
- p.width = Math.min(p.width, displayFrameWidth);
+ if (outParams.x < displayFrame.left) {
+ // The popup is too far left, move it back in and clip if it's
+ // still too large.
+ outParams.x = displayFrame.left;
+
+ final int displayFrameWidth = displayFrame.width();
+ width = Math.min(width, displayFrameWidth);
}
- if (mOverlapAnchor) {
- final int bottom = p.y + p.height;
- if (bottom > displayFrame.bottom) {
- p.y -= bottom - displayFrame.bottom;
- }
- } else {
- if (onTop) {
- final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
- if (popupTop < 0) {
- p.y += popupTop;
- }
- } else {
- p.y = Math.max(p.y, displayFrame.top);
- }
+ final int bottom = outParams.y + height;
+ if (bottom > displayFrame.bottom) {
+ // The popup is too far down, move it back in.
+ outParams.y -= bottom - displayFrame.bottom;
}
- p.x -= winOffsetX;
- p.y -= winOffsetY;
+
+ if (outParams.y < displayFrame.top) {
+ // The popup is too far up, move it back in and clip if
+ // it's still too large.
+ outParams.y = displayFrame.top;
+
+ final int displayFrameHeight = displayFrame.height();
+ height = Math.min(height, displayFrameHeight);
+ }
+
+ outParams.x -= winOffsetX;
+ outParams.y -= winOffsetY;
}
- p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
+ outParams.width = width;
+ outParams.height = height;
return onTop;
}
@@ -1665,7 +1684,7 @@
anchor.getWindowVisibleDisplayFrame(displayFrame);
}
- final int[] anchorPos = mDrawingLocation;
+ final int[] anchorPos = mTmpDrawingLocation;
anchor.getLocationOnScreen(anchorPos);
final int bottomEdge = displayFrame.bottom;
@@ -1754,7 +1773,7 @@
}
// Clears the anchor view.
- unregisterForViewTreeChanges();
+ detachFromAnchor();
if (mOnDismissListener != null) {
mOnDismissListener.onDismiss();
@@ -2018,43 +2037,40 @@
}
final WeakReference<View> oldAnchor = mAnchor;
+ final int gravity = mAnchoredGravity;
+
final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
- registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity);
+ attachToAnchor(anchor, xoff, yoff, gravity);
} else if (needsUpdate) {
// No need to register again if this is a DropDown, showAsDropDown already did.
mAnchorXoff = xoff;
mAnchorYoff = yoff;
}
- final WindowManager.LayoutParams p =
- (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+ final LayoutParams p = (LayoutParams) mDecorView.getLayoutParams();
+ final int oldGravity = p.gravity;
+ final int oldWidth = p.width;
+ final int oldHeight = p.height;
+ final int oldX = p.x;
+ final int oldY = p.y;
if (updateDimension) {
if (width == -1) {
width = mPopupWidth;
- } else {
- mPopupWidth = width;
- p.width = width;
}
if (height == -1) {
height = mPopupHeight;
- } else {
- mPopupHeight = height;
- p.height = height;
}
}
- final int x = p.x;
- final int y = p.y;
- if (updateLocation) {
- updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, mAnchoredGravity));
- } else {
- updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
- mAnchoredGravity));
- }
+ final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
+ width, height, gravity);
+ updateAboveAnchor(aboveAnchor);
- update(p.x, p.y, width, height, x != p.x || y != p.y);
+ final boolean paramsChanged = oldGravity != p.gravity || oldX != p.x || oldY != p.y
+ || oldWidth != p.width || oldHeight != p.height;
+ update(p.x, p.y, p.width, p.height, paramsChanged);
}
/**
@@ -2067,7 +2083,7 @@
public void onDismiss();
}
- private void unregisterForViewTreeChanges() {
+ private void detachFromAnchor() {
final View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null) {
final ViewTreeObserver vto = anchor.getViewTreeObserver();
@@ -2084,8 +2100,8 @@
mIsAnchorRootAttached = false;
}
- private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) {
- unregisterForViewTreeChanges();
+ private void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
+ detachFromAnchor();
final ViewTreeObserver vto = anchor.getViewTreeObserver();
if (vto != null) {
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index c0dce22..7bd64e5 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -26,6 +26,7 @@
import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
import java.util.HashSet;
@@ -72,15 +73,17 @@
public void register(Context context, Looper thread, UserHandle user,
boolean externalStorage) {
+ register(context, user, externalStorage,
+ (thread == null) ? BackgroundThread.getHandler() : new Handler(thread));
+ }
+
+ public void register(Context context, UserHandle user,
+ boolean externalStorage, Handler handler) {
if (mRegisteredContext != null) {
throw new IllegalStateException("Already registered");
}
mRegisteredContext = context;
- if (thread == null) {
- mRegisteredHandler = BackgroundThread.getHandler();
- } else {
- mRegisteredHandler = new Handler(thread);
- }
+ mRegisteredHandler = Preconditions.checkNotNull(handler);
if (user != null) {
context.registerReceiverAsUser(this, user, sPackageFilt, null, mRegisteredHandler);
context.registerReceiverAsUser(this, user, sNonDataFilt, null, mRegisteredHandler);
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index ed4722d..2a9264d 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -21,7 +21,6 @@
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Slog;
-import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -140,14 +139,14 @@
}
public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
- int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException {
+ int dexFlags, String compilerFilter, String volumeUuid) throws InstallerException {
dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
- null /*outputPath*/, dexFlags, volumeUuid, useProfiles);
+ null /*outputPath*/, dexFlags, compilerFilter, volumeUuid);
}
public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
- int dexoptNeeded, String outputPath, int dexFlags, String volumeUuid,
- boolean useProfiles) throws InstallerException {
+ int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter,
+ String volumeUuid) throws InstallerException {
execute("dexopt",
apkPath,
uid,
@@ -156,8 +155,27 @@
dexoptNeeded,
outputPath,
dexFlags,
- volumeUuid,
- useProfiles ? '1' : '0');
+ compilerFilter,
+ volumeUuid);
+ }
+
+ public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+ String rawReply = executeForResult("merge_profiles", uid, pkgName);
+ if (rawReply == null) {
+ throw new IllegalStateException("Unexpected null reply");
+ }
+ final String res[] = rawReply.split(" ");
+
+ if ((res == null) || (res.length != 2)) {
+ throw new InstallerException("Invalid size result: " + rawReply);
+ }
+
+ // Just as a sanity check. Anything != "true" will be interpreted as false by parseBoolean.
+ if (!res[1].equals("true") && !res[1].equals("false")) {
+ throw new InstallerException("Invalid boolean result: " + rawReply);
+ }
+
+ return Boolean.parseBoolean(res[1]);
}
private boolean connect() {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b658f87..5980ab6 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -501,12 +501,14 @@
for (String classPathElement : classPathElements) {
// System server is fully AOTed and never profiled
// for profile guided compilation.
+ // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
final int dexoptNeeded = DexFile.getDexOptNeeded(
- classPathElement, instructionSet, DexFile.COMPILATION_TYPE_FULL);
+ classPathElement, instructionSet, "speed",
+ false /* newProfile */);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
- dexoptNeeded, 0 /*dexFlags*/, null /*volumeUuid*/,
- false /*useProfiles*/);
+ dexoptNeeded, 0 /*dexFlags*/, "speed",
+ null /*volumeUuid*/);
}
}
} catch (IOException | InstallerException e) {
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 6931193..738aaca 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -16,6 +16,8 @@
package com.android.internal.policy;
+import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
+
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -69,6 +71,7 @@
private ColorDrawable mNavigationBarColor;
private boolean mOldFullscreen;
private boolean mFullscreen;
+ private final int mResizeMode;
private final Rect mOldSystemInsets = new Rect();
private final Rect mOldStableInsets = new Rect();
private final Rect mSystemInsets = new Rect();
@@ -77,7 +80,7 @@
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
- boolean fullscreen, Rect systemInsets, Rect stableInsets) {
+ boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode) {
setName("ResizeFrame");
mRenderer = renderer;
@@ -98,6 +101,7 @@
mStableInsets.set(stableInsets);
mOldSystemInsets.set(systemInsets);
mOldStableInsets.set(stableInsets);
+ mResizeMode = resizeMode;
synchronized (this) {
redrawLocked(initialBounds, fullscreen, mSystemInsets, mStableInsets);
}
@@ -266,11 +270,16 @@
mLastXOffset = xOffset;
mLastYOffset = yOffset;
- mRenderer.setContentDrawBounds(
- mLastXOffset,
- mLastYOffset,
- mLastXOffset + mLastContentWidth,
- mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ // Only clip the content to the bounds if we are not fullscreen. In the other case, we
+ // actually need to draw outside these.
+ if (mResizeMode == RESIZE_MODE_FREEFORM) {
+ mRenderer.setContentDrawBounds(
+ mLastXOffset,
+ mLastYOffset,
+ mLastXOffset + mLastContentWidth,
+ mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ }
+
// If this was the first call and redrawLocked got already called prior
// to us, we should re-issue a redrawLocked now.
return firstCall
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index fbd8fb5..9904893 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -39,8 +39,11 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.RemoteException;
@@ -49,6 +52,7 @@
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ContextThemeWrapper;
+import android.view.DisplayListCanvas;
import android.view.Gravity;
import android.view.InputQueue;
import android.view.KeyEvent;
@@ -88,6 +92,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -211,6 +216,11 @@
private boolean mApplyFloatingVerticalInsets = false;
private boolean mApplyFloatingHorizontalInsets = false;
+ private int mResizeMode = RESIZE_MODE_INVALID;
+ private final int mResizeShadowSize;
+ private final Paint mVerticalResizeShadowPaint = new Paint();
+ private final Paint mHorizontalResizeShadowPaint = new Paint();
+
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);
@@ -233,6 +243,10 @@
setWindow(window);
updateLogTag(params);
+
+ mResizeShadowSize = context.getResources().getDimensionPixelSize(
+ R.dimen.resize_shadow_size);
+ initResizingPaints();
}
void setBackgroundFallback(int resId) {
@@ -699,6 +713,10 @@
// our shadow elevation.
updateElevation();
mAllowUpdateElevation = true;
+
+ if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
+ getViewRootImpl().requestInvalidateRootRenderNode();
+ }
}
@Override
@@ -1907,7 +1925,7 @@
@Override
public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ Rect stableInsets, int resizeMode) {
if (mWindow.isDestroyed()) {
// If the owner's window is gone, we should not be able to come here anymore.
releaseThreadedRenderer();
@@ -1923,7 +1941,7 @@
initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
- stableInsets);
+ stableInsets, resizeMode);
// Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
// If we want to get the shadow shown while resizing, we would need to elevate a new
@@ -1932,12 +1950,16 @@
updateColorViews(null /* insets */, false);
}
+ mResizeMode = resizeMode;
+ getViewRootImpl().requestInvalidateRootRenderNode();
}
@Override
public void onWindowDragResizeEnd() {
releaseThreadedRenderer();
updateColorViews(null /* insets */, false);
+ mResizeMode = RESIZE_MODE_INVALID;
+ getViewRootImpl().requestInvalidateRootRenderNode();
}
@Override
@@ -1960,6 +1982,41 @@
}
}
+ @Override
+ public void onPostDraw(DisplayListCanvas canvas) {
+ drawResizingShadowIfNeeded(canvas);
+ }
+
+ private void initResizingPaints() {
+ final int startColor = mContext.getResources().getColor(
+ R.color.resize_shadow_start_color, null);
+ final int endColor = mContext.getResources().getColor(
+ R.color.resize_shadow_end_color, null);
+ final int middleColor = (startColor + endColor) / 2;
+ mHorizontalResizeShadowPaint.setShader(new LinearGradient(
+ 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
+ new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
+ mVerticalResizeShadowPaint.setShader(new LinearGradient(
+ 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
+ new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
+ }
+
+ private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) {
+ if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
+ || mWindow.isTranslucent()
+ || (mWindow.getAttributes().flags & FLAG_SHOW_WALLPAPER) != 0) {
+ return;
+ }
+ canvas.save();
+ canvas.translate(0, getHeight() - mFrameOffsets.bottom);
+ canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
+ canvas.restore();
+ canvas.save();
+ canvas.translate(getWidth() - mFrameOffsets.right, 0);
+ canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
+ canvas.restore();
+ }
+
/** Release the renderer thread which is usually done when the user stops resizing. */
private void releaseThreadedRenderer() {
if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
@@ -2070,10 +2127,10 @@
* @hide
*/
@Override
- public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list) {
+ public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!mWindow.isDestroyed() && st != null && mWindow.getCallback() != null) {
- mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu);
+ mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu, deviceId);
}
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 7637eec..794a6d6 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -157,6 +157,7 @@
InputQueue.Callback mTakeInputQueueCallback;
boolean mIsFloating;
+ private boolean mIsTranslucent;
private LayoutInflater mLayoutInflater;
@@ -490,6 +491,10 @@
return mIsFloating;
}
+ public boolean isTranslucent() {
+ return mIsTranslucent;
+ }
+
/**
* Return a LayoutInflater instance that can be used to inflate XML view layout
* resources for use in this Window.
@@ -2400,6 +2405,8 @@
requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
}
+ mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
+
final Context context = getContext();
final int targetSdk = context.getApplicationInfo().targetSdkVersion;
final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 64c5b8d..9e5c238 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -65,7 +65,7 @@
void cancelPreloadRecentApps();
void showScreenPinningRequest();
- void toggleKeyboardShortcutsMenu();
+ void toggleKeyboardShortcutsMenu(int deviceId);
/**
* Notifies the status bar that an app transition is pending to delay applying some flags with
@@ -88,6 +88,11 @@
*/
void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
+ /**
+ * Notifies the status bar that an app transition is done.
+ */
+ void appTransitionFinished();
+
void showAssistDisclosure();
void startAssist(in Bundle args);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 8acf5d3..ee3f937 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -71,7 +71,7 @@
void preloadRecentApps();
void cancelPreloadRecentApps();
- void toggleKeyboardShortcutsMenu();
+ void toggleKeyboardShortcutsMenu(int deviceId);
/**
* Notifies the status bar that an app transition is pending to delay applying some flags with
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 53cb56e..c46851e 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -35,6 +35,20 @@
}
/**
+ * Ensures that an expression checking an argument is true.
+ *
+ * @param expression the expression to check
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(boolean expression, final Object errorMessage) {
+ if (!expression) {
+ throw new IllegalArgumentException(String.valueOf(errorMessage));
+ }
+ }
+
+ /**
* Ensures that an string reference passed as a parameter to the calling
* method is not empty.
*
diff --git a/core/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
index 4a196f8..5f390be 100644
--- a/core/java/com/android/internal/util/ScreenShapeHelper.java
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -1,27 +1,20 @@
package com.android.internal.util;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.os.Build;
import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
import android.view.ViewRootImpl;
-import com.android.internal.R;
-
/**
* @hide
*/
public class ScreenShapeHelper {
- private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
-
/**
* Return the bottom pixel window outset of a window given its style attributes.
* @return An outset dimension in pixels or 0 if no outset should be applied.
*/
public static int getWindowOutsetBottomPx(Resources resources) {
- if (IS_EMULATOR) {
+ if (Build.IS_EMULATOR) {
return SystemProperties.getInt(ViewRootImpl.PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX, 0);
} else {
return resources.getInteger(com.android.internal.R.integer.config_windowOutsetBottom);
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index ab918c8..530e00c 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -111,6 +111,6 @@
}
@Override
- public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
}
}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index afa1554..3be15f3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -16,8 +16,6 @@
package com.android.internal.view;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -59,8 +57,7 @@
private static final int DO_CLEAR_META_KEY_STATES = 130;
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
- @NonNull
- private final WeakReference<InputConnection> mInputConnection;
+ private WeakReference<InputConnection> mInputConnection;
private Looper mMainLooper;
private Handler mH;
@@ -89,11 +86,6 @@
mH = new MyHandler(mMainLooper);
}
- @Nullable
- public InputConnection getInputConnection() {
- return mInputConnection.get();
- }
-
abstract protected boolean isActive();
/**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c685b0c..c6112d9 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -193,9 +193,9 @@
$(TOP)/system/media/camera/include \
$(TOP)/system/netd/include \
external/pdfium/core/include/fpdfapi \
- external/pdfium/core/include/fpdfdoc \
external/pdfium/fpdfsdk/include \
external/pdfium/public \
+ external/pdfium \
external/skia/include/private \
external/skia/src/core \
external/skia/src/effects \
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 1ead618..528541d 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -723,6 +723,7 @@
// Make sure that the recycled bitmap has the correct alpha type.
mRecycledBitmap->setAlphaType(bitmap->alphaType());
+ bitmap->notifyPixelsChanged();
bitmap->lockPixels();
mNeedsCopy = false;
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 2998c99..ab393f2 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -232,12 +232,6 @@
obj->addPath(*src, *matrix);
}
- static void offset__FFPath(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy, jlong dstHandle) {
- SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
- obj->offset(dx, dy, dst);
- }
-
static void offset__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->offset(dx, dy);
@@ -508,7 +502,6 @@
{"native_addPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
{"native_addPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
{"native_addPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
- {"native_offset","(JFFJ)V", (void*) SkPathGlue::offset__FFPath},
{"native_offset","(JFF)V", (void*) SkPathGlue::offset__FF},
{"native_setLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint},
{"native_transform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath},
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index 0177635..2c840bd 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -196,7 +196,7 @@
return;
}
- CFX_AffineMatrix matrix;
+ CFX_Matrix matrix;
SkMatrix* skTransform = reinterpret_cast<SkMatrix*>(transformPtr);
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index 6ddfacf..27f3493 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -205,11 +205,10 @@
clip.bottom = destBottom;
fxgeDevice->SetClip_Rect(&clip);
- CPDF_RenderContext* pageContext = new CPDF_RenderContext;
+ CPDF_RenderContext* pageContext = new CPDF_RenderContext(pPage);
pContext->m_pContext = pageContext;
- pageContext->Create(pPage);
- CFX_AffineMatrix matrix;
+ CFX_Matrix matrix;
if (!transform) {
pPage->GetDisplayMatrix(matrix, destLeft, destTop, destRight - destLeft,
destBottom - destTop, 0);
@@ -232,8 +231,8 @@
}
pageContext->AppendObjectList(pPage, &matrix);
- pContext->m_pRenderer = new CPDF_ProgressiveRenderer;
- pContext->m_pRenderer->Start(pageContext, fxgeDevice, renderOptions, NULL);
+ pContext->m_pRenderer = new CPDF_ProgressiveRenderer(pageContext, fxgeDevice, renderOptions);
+ pContext->m_pRenderer->Start(NULL);
fxgeDevice->RestoreState();
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60f05448..8abb7e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -706,7 +706,9 @@
android:description="@string/permgroupdesc_phone"
android:priority="500" />
- <!-- Allows read only access to phone state.
+ <!-- Allows read only access to phone state, including the phone number of the device,
+ current cellular network information, the status of any ongoing calls, and a list of any
+ {@link android.telecom.PhoneAccount}s registered on the device.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
minSdkVersion}</a> and <a
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 809e525..ba6d30a 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -51,7 +51,7 @@
<LinearLayout
android:id="@+id/media_actions"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginStart="10dp"
android:layout_marginBottom="12dp"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index be140a5..1f958b4 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Meer opsies"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s - %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s-%2$s%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interne gedeelde berging"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kaart"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g>-SD-kaart"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-datastokkie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a87090a..2623053 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ተጨማሪ አማራጮች"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s፣ %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s፣ %2$s፣ %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"የውስጥ የተጋራ ማከማቻ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ካርድ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> ኤስዲ ካርድ"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"የዩኤስቢ አንጻፊ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 01d47cf..b828197 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1260,8 +1260,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"المزيد من الخيارات"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s، %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s، %2$s، %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"السعة التخزينية المشتركة الداخلية"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"بطاقة SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"بطاقة SD من <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"محرك أقراص USB"</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 58d81b2..25a979b 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Əlavə seçimlər"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Daxili paylaşılan yaddaş"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kart"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kart"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB drayv"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index b8c6fd3..3eff6f3 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Još opcija"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Unutrašnji deljeni memorijski prostor"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kartica"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartica"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB disk"</string>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index 502e1e9..7b85628 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Больш налад"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Унутранае абагуленае сховішча"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-карта <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-дыск"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 62d0d372..4515e80 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Още опции"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"„%1$s“ – %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"„%1$s“, „%2$s“ – %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Вътрешно споделено хранилище"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD карта от <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB устройство"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index cbe8eca..03faf2f 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -28,11 +28,11 @@
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
<string name="fileSizeSuffix" msgid="8897567456150907538">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="durationDays" msgid="6652371460511178259">"<xliff:g id="DAYS">%1$d</xliff:g> দিন"</string>
- <string name="durationDayHours" msgid="2713107458736744435">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘন্টা"</string>
- <string name="durationDayHour" msgid="7293789639090958917">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘন্টা"</string>
- <string name="durationHours" msgid="4266858287167358988">"<xliff:g id="HOURS">%1$d</xliff:g> ঘন্টা"</string>
- <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> ঘন্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
- <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> ঘন্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
+ <string name="durationDayHours" msgid="2713107458736744435">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘণ্টা"</string>
+ <string name="durationDayHour" msgid="7293789639090958917">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘণ্টা"</string>
+ <string name="durationHours" msgid="4266858287167358988">"<xliff:g id="HOURS">%1$d</xliff:g> ঘণ্টা"</string>
+ <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> ঘণ্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
+ <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> ঘণ্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
<string name="durationMinutes" msgid="3134226679883579347">"<xliff:g id="MINUTES">%1$d</xliff:g> মিনিট"</string>
<string name="durationMinute" msgid="7155301744174623818">"<xliff:g id="MINUTES">%1$d</xliff:g> মিনিট"</string>
<string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> মিনিট <xliff:g id="SECONDS">%2$d</xliff:g> সেকেন্ড"</string>
@@ -829,8 +829,8 @@
<string name="preposition_for_year" msgid="5040395640711867177">"<xliff:g id="YEAR">%s</xliff:g> এ"</string>
<string name="day" msgid="8144195776058119424">"দিন"</string>
<string name="days" msgid="4774547661021344602">"দিন"</string>
- <string name="hour" msgid="2126771916426189481">"ঘন্টা"</string>
- <string name="hours" msgid="894424005266852993">"ঘন্টা"</string>
+ <string name="hour" msgid="2126771916426189481">"ঘণ্টা"</string>
+ <string name="hours" msgid="894424005266852993">"ঘণ্টা"</string>
<string name="minute" msgid="9148878657703769868">"মি"</string>
<string name="minutes" msgid="5646001005827034509">"মিনিট"</string>
<string name="second" msgid="3184235808021478">"সেকেন্ড"</string>
@@ -848,8 +848,8 @@
<item quantity="other"><xliff:g id="COUNT">%d</xliff:g> মিনিট</item>
</plurals>
<plurals name="duration_hours" formatted="false" msgid="6826233369186668274">
- <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ঘন্টা</item>
- <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> ঘন্টা</item>
+ <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ঘণ্টা</item>
+ <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> ঘণ্টা</item>
</plurals>
<string name="VideoView_error_title" msgid="3534509135438353077">"ভিডিও সমস্যা"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"এই ভিডিওটি এই ডিভাইসে স্ট্রিমিং করার জন্য বৈধ নয়৷"</string>
@@ -869,7 +869,7 @@
<string name="paste_as_plain_text" msgid="5427792741908010675">"প্লেইন টেক্সট হিসাবে আটকান"</string>
<string name="replace" msgid="5781686059063148930">"প্রতিস্থাপন করুন..."</string>
<string name="delete" msgid="6098684844021697789">"মুছুন"</string>
- <string name="copyUrl" msgid="2538211579596067402">"URL অনুলিপি করুন"</string>
+ <string name="copyUrl" msgid="2538211579596067402">"URL কপি করুন"</string>
<string name="selectTextMode" msgid="1018691815143165326">"পাঠ্য নির্বাচন করুন"</string>
<string name="undo" msgid="7905788502491742328">"পূর্বাবস্থায় ফিরুন"</string>
<string name="redo" msgid="7759464876566803888">"পুনরায় করুন"</string>
@@ -1192,8 +1192,8 @@
<string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"বাড়ানোর জন্য উপরের দিকে এবং কমানোর জন্য নীচের দিকে স্লাইড করুন৷"</string>
<string name="time_picker_increment_minute_button" msgid="8865885114028614321">"মিনিট বাড়ান"</string>
<string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"মিনিট কমান"</string>
- <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"ঘন্টা বাড়ান"</string>
- <string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"ঘন্টা কমান"</string>
+ <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"ঘণ্টা বাড়ান"</string>
+ <string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"ঘণ্টা কমান"</string>
<string name="time_picker_increment_set_pm_button" msgid="4147590696151230863">"PM সেট করুন"</string>
<string name="time_picker_decrement_set_am_button" msgid="8302140353539486752">"AM সেট করুন"</string>
<string name="date_picker_increment_month_button" msgid="5369998479067934110">"মাস বাড়ান"</string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"আরো বিকল্প"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"অভ্যন্তরীণ শেয়ার করা সঞ্চয়স্থান"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD কার্ড"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD কার্ড"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ড্রাইভ"</string>
@@ -1453,9 +1452,9 @@
<string name="immersive_cling_description" msgid="3482371193207536040">"প্রস্থান করতে উপর থেকে নীচের দিকে সোয়াইপ করুন"</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"বুঝেছি"</string>
<string name="done_label" msgid="2093726099505892398">"সম্পন্ন হয়েছে"</string>
- <string name="hour_picker_description" msgid="6698199186859736512">"বৃত্তাকার ঘন্টা নির্বাচকের স্লাইডার"</string>
+ <string name="hour_picker_description" msgid="6698199186859736512">"বৃত্তাকার ঘণ্টা নির্বাচকের স্লাইডার"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"বৃত্তাকার মিনিট নির্বাচকের স্লাইডার"</string>
- <string name="select_hours" msgid="6043079511766008245">"ঘন্টা নির্বাচন করুন"</string>
+ <string name="select_hours" msgid="6043079511766008245">"ঘণ্টা নির্বাচন করুন"</string>
<string name="select_minutes" msgid="3974345615920336087">"মিনিট নির্বাচন করুন"</string>
<string name="select_day" msgid="7774759604701773332">"মাস এবং দিন নির্বাচন করুন"</string>
<string name="select_year" msgid="7952052866994196170">"বছর নির্বাচন করুন"</string>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 6e7d8d2..7e9f44f 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Više opcija"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interno dijeljena pohrana"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kartica"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartica"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB disk"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 9044a1a..e59c936 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Més opcions"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Emmagatzematge intern compartit"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Targeta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Targeta SD de: <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unitat USB"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 8875ead..391b012 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Flere valgmuligheder"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Intern delt lagerplads"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort fra <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-drev"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a1183e9..d95bdda 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Weitere Optionen"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s. %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s. %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interner gemeinsamer Speicher"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-Karte"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-Karte von <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-Speichergerät"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4e5f8fb..ce0805f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Περισσότερες επιλογές"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Εσωτερικός κοινόχρηστος αποθηκευτικός χώρος"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Κάρτα SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Κάρτα SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Μονάδα USB"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2484fe5..27cd9d2 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Más opciones"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Almacenamiento interno compartido"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Tarjeta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Tarjeta SD de <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unidad USB"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index ed5db6c..8b8f29f 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Aukera gehiago"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Barneko biltegiratze partekatua"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD txartela"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD txartela"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB unitatea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b143038..e67e058 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"سایر گزینهها"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"حافظه داخلی مشترک"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"کارت SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"کارت SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"درایو USB"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 8bb613e..b1ad7e7 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Lisää asetuksia"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Sisäinen jaettu tallennustila"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kortti"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kortti: <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-asema"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 3375e18..09993ac 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Plus d\'options"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Stockage interne partagé"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Carte SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Carte mémoire SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Clé USB"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 741d0e1..30ff9fb 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Plus d\'options"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Espace de stockage interne partagé"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Carte SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Carte SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Clé USB"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index be83dab..0d479a5 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Máis opcións"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Almacenamento compartido interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Tarxeta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Tarxeta SD de <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unidade USB"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 2ce73f4..bc74c9d 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Više opcija"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Unutarnja dijeljena pohrana"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kartica"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartica"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB pogon"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 8921e88..030975e 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ավելի շատ ընտրանքներ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Համօգտագործվող ներքին հիշողություն"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD քարտ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD քարտ <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB սարքավար"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index e7c706d..819ea80 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Opsi lainnya"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Penyimpanan bersama internal"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kartu SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kartu SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 5edb125..91624e7 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Fleiri valkostir"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Innbyggð samnýtt geymsla"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort frá <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-drif"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 73775a0..9937e0d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Altre opzioni"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Archivio condiviso interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Scheda SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Scheda SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unità USB"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index b06434e..443c1be 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"אפשרויות נוספות"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"אחסון משותף פנימי"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"כרטיס SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"כרטיס SD של <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"כונן USB"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 14d64b0..e81beb3 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"その他のオプション"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s、%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s、%2$s、%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"内部共有ストレージ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SDカード"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g>製SDカード"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USBドライブ"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 92e57b7..3e463f1 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"მეტი ვარიანტები"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"შიდა გაზიარებული მეხსიერება"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ბარათი"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD ბარათი"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB დისკი"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 85621ed..342db02 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Басқа опциялар"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Ішкі ортақ қойма"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD картасы"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD картасы"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB дискі"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index e9be6b6..fdee3aa 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -1226,8 +1226,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ជម្រើសច្រើនទៀត"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ឧបករណ៍ផ្ទុកដែលចែករំលែកខាងក្នុង"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"កាតអេសឌី"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"កាត SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"ឧបករណ៍ផ្ទុក USB"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 2e04419..b31b2bb 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ಇನ್ನಷ್ಟು ಆಯ್ಕೆಗಳು"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ಆಂತರಿಕವಾಗಿ ಹಂಚಲಾದ ಸಂಗ್ರಹಣೆ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ಕಾರ್ಡ್"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD ಕಾರ್ಡ್"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ಡ್ರೈವ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index b61df80..51418d9 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"옵션 더보기"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"내부 공유 저장공간"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 카드"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 카드"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB 드라이브"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 55b2b7c..32f3d9a 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -900,8 +900,8 @@
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s менен түзөтүү"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Төмөнкү менен бөлүшүү"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"%1$s менен бөлүшүү"</string>
- <string name="whichSendToApplication" msgid="8272422260066642057">"Колдонуп жөнөтүү"</string>
- <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s колдонуп жөнөтүү"</string>
+ <string name="whichSendToApplication" msgid="8272422260066642057">"Колдонмо тандаңыз"</string>
+ <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s аркылуу жөнөтүү"</string>
<string name="whichHomeApplication" msgid="4307587691506919691">"Башкы бет колдонмосун тандаңыз"</string>
<string name="whichHomeApplicationNamed" msgid="4493438593214760979">"Башкы бет колдонмосу катары %1$s пайдалануу"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Бул аракет үчүн демейки боюнча колдонулсун."</string>
@@ -1225,8 +1225,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Дагы параметрлер"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Бөлүшүлгөн ички сактагыч"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD карта"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB түзмөк"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 5d1c37e..94fb2db 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ໂຕເລືອກອື່ນ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນພາຍໃນ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD card"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> ແຜ່ນ SD"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ດຣ້າຍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1460b9b..c4c4fa2 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Daugiau parinkčių"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Vidinė bendroji atmintis"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kortelė"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"„<xliff:g id="MANUFACTURER">%s</xliff:g>“ SD kortelė"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Atmintukas"</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index c4ef82c..d9dae7a 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -1226,8 +1226,7 @@
<!-- no translation found for action_bar_home_description_format (7965984360903693903) -->
<skip />
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Внатрешно заедничко место за складирање"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"СД картичка"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> СД-картичка"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"УСБ-меморија"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 9b96501..16ededc 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"കൂടുതൽ ഓപ്ഷനുകള്"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"പങ്കിട്ട ആന്തരിക സ്റ്റോറേജ്"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD കാർഡ്"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD കാർഡ്"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ഡ്രൈവ്"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index ef34b46..0f31862 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Нэмэлт сонголтууд"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Дотоод хуваалцсан санах ой"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD карт"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD карт"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB диск"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 7baa543..9a35efd 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"अधिक पर्याय"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"अंतर्गत सामायिक केलेला संचय"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD कार्ड"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD कार्ड"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ड्राइव्ह"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index c2e3bfe..7c8ce0f 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Lagi pilihan"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Storan kongsi dalaman"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kad SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kad SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Pemacu USB"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 459eccd..b6f3c37 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -1230,8 +1230,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"थप विकल्पहरू"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"साझेदारी गरिएको आन्तरिक भण्डारण"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD कार्ड"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD कार्ड"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ड्राइभ"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index f74b93c..8de9102 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ਹੋਰ ਚੋਣਾਂ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ਅੰਦਰੂਨੀ ਸਾਂਝੀ ਕੀਤੀ ਗਈ ਸਟੋਰੇਜ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ਕਾਰਡ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD ਕਾਰਡ"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ਡ੍ਰਾਇਵ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6e4091a..f20e97d 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Więcej opcji"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Wewnętrzna pamięć współdzielona"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Karta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Karta SD (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Dysk USB"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 62ee01c..2212f9f 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno compartilhado"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 331cd9d..6cb94c3 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno partilhado"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unidade USB"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 62ee01c..2212f9f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno compartilhado"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index dd4faad..31ec3a0 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mai multe opțiuni"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Memorie internă comună"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Card SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Card SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unitate USB"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 4d618ac..5a79523 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ещё"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Внутренний общий накопитель"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-карта <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-накопитель"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 5dbb922..4f34cd0 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -1226,8 +1226,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"තවත් විකල්ප"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"අභ්යන්තර බෙදා ගත් ගබඩාව"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD පත"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD කාඩ්පත"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ධාවකය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 75a2129..4ba64b1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Viac možností"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interné zdieľané úložisko"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD karta"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD karta <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Disk USB"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 579880a..eb3d4e0 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Več možnosti"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Notranja shramba v skupni rabi"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kartica SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kartica SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Pogon USB"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index b39f297..94258e2 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Opsione të tjera"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Hapësira ruajtëse e brendshme e ndarë"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Karta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Karta SD nga <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-ja"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 74fc273..bfc22e4 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Још опција"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Унутрашњи дељени меморијски простор"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD картица"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD картица"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB диск"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 610fa07..3f2a4a3 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Fler alternativ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Delat internt lagringsutrymme"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-enhet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 3d4bc60d..2e1a7a4 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1226,8 +1226,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Chaguo zaidi"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Hifadhi ya ndani inayoshirikiwa"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kadi ya SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kadi ya SD iliyotengenezwa na <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Hifadhi ya USB"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 9411f5b..b34caed 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"மேலும் விருப்பங்கள்"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"பகிர்ந்த சேமிப்பகம் (அகம்)"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD கார்டு"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD கார்டு"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB டிரைவ்"</string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 58d3d91..c0716e9 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -26,12 +26,4 @@
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
<string translatable="false" name="config_defaultPictureInPictureBounds">"1328 54 1808 324"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
- is located in center. -->
- <string translatable="false" name="config_centeredPictureInPictureBounds">"596 280 1324 690"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
- when the PIP is shown with Recents. -->
- <string translatable="false" name="config_pictureInPictureBoundsInRecents">"1484 96 1804 276"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 353da8a..d0b6f43 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ตัวเลือกเพิ่มเติม"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ที่จัดเก็บข้อมูลที่ใช้ร่วมกันภายใน"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"การ์ด SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"การ์ด SD ของ <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"ไดรฟ์ USB"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 41ada1c..698ae61 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Higit pang mga pagpipilian"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Internal na nakabahaging storage"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD card"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD card"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB drive"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3a04463..52c32e7 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Diğer seçenekler"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Dahili olarak paylaşılan depolama alanı"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kart"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartı"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB sürücü"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 565cdd9..41a1e82 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"مزید اختیارات"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"اندرونی اشتراک کردہ اسٹوریج"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD کارڈ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD کارڈ"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ڈرائیو"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 1a81245..e3b9e9a 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -900,7 +900,7 @@
<string name="whichSendApplication" msgid="6902512414057341668">"Ulashish…"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"“%1$s” orqali ulashish"</string>
<string name="whichSendToApplication" msgid="8272422260066642057">"Ilovani tanlang"</string>
- <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s yordamida yuborish"</string>
+ <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s orqali yuborish"</string>
<string name="whichHomeApplication" msgid="4307587691506919691">"Bosh ilovani tanlash"</string>
<string name="whichHomeApplicationNamed" msgid="4493438593214760979">"%1$s: Bosh ilova sifatida foydalanish"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Ushbu amaldan standart sifatida foydalanish"</string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ko‘proq sozlamalar"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Ichki umumiy xotira"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD karta"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartasi"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB xotira"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 7302f7a..76743f2 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Tùy chọn khác"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Bộ nhớ trong dùng chung"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Thẻ SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Thẻ SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Ổ USB"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4c90e1c..3b0c075 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -899,7 +899,7 @@
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"使用%1$s编辑"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"分享方式"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"使用%1$s分享"</string>
- <string name="whichSendToApplication" msgid="8272422260066642057">"通过以下应用发送:"</string>
+ <string name="whichSendToApplication" msgid="8272422260066642057">"通过以下应用发送"</string>
<string name="whichSendToApplicationNamed" msgid="7768387871529295325">"通过1$s发送"</string>
<string name="whichHomeApplication" msgid="4307587691506919691">"选择主屏幕应用"</string>
<string name="whichHomeApplicationNamed" msgid="4493438593214760979">"将“%1$s”设为主屏幕应用"</string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"更多选项"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s:%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s - %2$s:%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"内部共享的存储空间"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD卡"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 卡"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"U 盘"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index a782d40..6e62d1c 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"更多選項"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s:%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s - %2$s:%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"內部共用儲存空間"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 卡"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 卡"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB 隨身碟"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 23d72dc..ddd486b 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Izinketho ezingaphezulu"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Isitoreji esabiwe sangaphakathi"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Ikhadi le-SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> ikhadi le-SD"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Idrayivu ye-USB"</string>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 48aa440..bddd225 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -180,4 +180,7 @@
<!-- Status bar color for semi transparent mode. -->
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
+
+ <color name="resize_shadow_start_color">#2a000000</color>
+ <color name="resize_shadow_end_color">#00000000</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 01b2c47..6ecaa1f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2462,14 +2462,6 @@
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
<string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
- is located in center. -->
- <string translatable="false" name="config_centeredPictureInPictureBounds">"0 0 300 300"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
- when the PIP is shown with Recents. -->
- <string translatable="false" name="config_pictureInPictureBoundsInRecents">"0 0 100 100"</string>
-
<!-- Controls the snap mode for the docked stack divider
0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio
1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 081d613..71d9a1f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -22,6 +22,8 @@
<dimen name="thumbnail_width">192dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
<dimen name="thumbnail_height">192dp</dimen>
+ <!-- The amount to scale a fullscreen screenshot thumbnail. -->
+ <item name="thumbnail_fullscreen_scale" type="fraction">60%</item>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
@@ -450,4 +452,6 @@
<item type="dimen" name="aerr_padding_list_top">15dp</item>
<item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item>
+
+ <dimen name="resize_shadow_size">5dp</dimen>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ce7d80b..3f46768 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -308,8 +308,6 @@
<java-symbol type="bool" name="config_guestUserEphemeral" />
<java-symbol type="bool" name="config_localDisplaysMirrorContent" />
<java-symbol type="string" name="config_defaultPictureInPictureBounds" />
- <java-symbol type="string" name="config_centeredPictureInPictureBounds" />
- <java-symbol type="string" name="config_pictureInPictureBoundsInRecents" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
@@ -1513,6 +1511,10 @@
<java-symbol type="dimen" name="docked_stack_minimize_thickness" />
<java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
<java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
+ <java-symbol type="fraction" name="thumbnail_fullscreen_scale" />
+ <java-symbol type="dimen" name="resize_shadow_size" />
+ <java-symbol type="color" name="resize_shadow_start_color" />
+ <java-symbol type="color" name="resize_shadow_end_color" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
diff --git a/core/tests/coretests/src/android/graphics/PathOffsetTest.java b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
new file mode 100644
index 0000000..950f873
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 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 android.graphics;
+
+
+import android.graphics.Bitmap.Config;
+import android.graphics.Path.Direction;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PathOffsetTest {
+
+ private static final int SQUARE = 10;
+ private static final int WIDTH = 100;
+ private static final int HEIGHT = 100;
+ private static final int START_X = 10;
+ private static final int START_Y = 20;
+ private static final int OFFSET_X = 30;
+ private static final int OFFSET_Y = 40;
+
+ @Test
+ @SmallTest
+ public void testPathOffset() {
+ Path actualPath = new Path();
+ actualPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
+ assertTrue(actualPath.isSimplePath);
+ actualPath.offset(OFFSET_X, OFFSET_Y);
+ assertTrue(actualPath.isSimplePath);
+
+ Path expectedPath = new Path();
+ expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
+ START_Y + OFFSET_Y + SQUARE, Direction.CW);
+
+ assertPaths(actualPath, expectedPath);
+ }
+
+ @Test
+ @SmallTest
+ public void testPathOffsetWithDestination() {
+ Path initialPath = new Path();
+ initialPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
+ Path actualPath = new Path();
+ assertTrue(initialPath.isSimplePath);
+ assertTrue(actualPath.isSimplePath);
+ initialPath.offset(OFFSET_X, OFFSET_Y, actualPath);
+ assertTrue(actualPath.isSimplePath);
+
+ Path expectedPath = new Path();
+ expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
+ START_Y + OFFSET_Y + SQUARE, Direction.CW);
+
+ assertPaths(actualPath, expectedPath);
+ }
+
+ private static void assertPaths(Path actual, Path expected) {
+ Bitmap actualBitmap = drawAndGetBitmap(actual);
+ Bitmap expectedBitmap = drawAndGetBitmap(expected);
+ assertTrue(actualBitmap.sameAs(expectedBitmap));
+ }
+
+ private static Bitmap drawAndGetBitmap(Path path) {
+ Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Config.ARGB_8888);
+ bitmap.eraseColor(Color.BLACK);
+ Paint paint = new Paint();
+ paint.setColor(Color.RED);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawPath(path, paint);
+ return bitmap;
+ }
+
+}
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index d554a50..cbed96c 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -20,6 +20,8 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.text.Layout.Alignment;
import static android.text.Layout.Alignment.*;
+import android.text.TextPaint;
+import android.text.method.EditorState;
import android.util.Log;
import junit.framework.TestCase;
@@ -33,6 +35,10 @@
* @Suppress
*/
public class StaticLayoutTest extends TestCase {
+ private static final int DEFAULT_OUTER_WIDTH = 150;
+ private static final Alignment DEFAULT_ALIGN = Alignment.ALIGN_CENTER;
+ private static final float SPACE_MULTI = 1.0f;
+ private static final float SPACE_ADD = 0.0f;
/**
* Basic test showing expected behavior and relationship between font
@@ -321,4 +327,91 @@
assertEquals(topPad, l.getTopPadding());
assertEquals(botPad, l.getBottomPadding());
}
+
+ private void moveCursorToRightCursorableOffset(EditorState state, TextPaint paint) {
+ assertEquals("The editor has selection", state.mSelectionStart, state.mSelectionEnd);
+ final Layout layout = builder().setText(state.mText.toString()).setPaint(paint).build();
+ final int newOffset = layout.getOffsetToRightOf(state.mSelectionStart);
+ state.mSelectionStart = state.mSelectionEnd = newOffset;
+ }
+
+ private void moveCursorToLeftCursorableOffset(EditorState state, TextPaint paint) {
+ assertEquals("The editor has selection", state.mSelectionStart, state.mSelectionEnd);
+ final Layout layout = builder().setText(state.mText.toString()).setPaint(paint).build();
+ final int newOffset = layout.getOffsetToLeftOf(state.mSelectionStart);
+ state.mSelectionStart = state.mSelectionEnd = newOffset;
+ }
+
+ /**
+ * Tests for keycap, variation selectors, flags are in CTS.
+ * See {@link android.text.cts.StaticLayoutTest}.
+ */
+ public void testEmojiOffset() {
+ EditorState state = new EditorState();
+ TextPaint paint = new TextPaint();
+
+ // Odd numbered regional indicator symbols.
+ // U+1F1E6 is REGIONAL INDICATOR SYMBOL LETTER A, U+1F1E8 is REGIONAL INDICATOR SYMBOL
+ // LETTER C.
+ state.setByString("| U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+1F1E6 U+1F1E8 | U+1F1E6 U+1F1E8 U+1F1E6");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 | U+1F1E6");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6 |");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6 |");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 | U+1F1E6");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("U+1F1E6 U+1F1E8 | U+1F1E6 U+1F1E8 U+1F1E6");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("| U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("| U+1F1E6 U+1F1E8 U+1F1E6 U+1F1E8 U+1F1E6");
+ moveCursorToLeftCursorableOffset(state, paint);
+
+ // Zero width sequence
+ final String zwjSequence = "U+1F468 U+200D U+2764 U+FE0F U+200D U+1F468";
+ state.setByString("| " + zwjSequence + " " + zwjSequence + " " + zwjSequence);
+ moveCursorToRightCursorableOffset(state, paint);
+ state.assertEquals(zwjSequence + " | " + zwjSequence + " " + zwjSequence);
+ moveCursorToRightCursorableOffset(state, paint);
+ state.assertEquals(zwjSequence + " " + zwjSequence + " | " + zwjSequence);
+ moveCursorToRightCursorableOffset(state, paint);
+ state.assertEquals(zwjSequence + " " + zwjSequence + " " + zwjSequence + " |");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.assertEquals(zwjSequence + " " + zwjSequence + " " + zwjSequence + " |");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.assertEquals(zwjSequence + " " + zwjSequence + " | " + zwjSequence);
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.assertEquals(zwjSequence + " | " + zwjSequence + " " + zwjSequence);
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.assertEquals("| " + zwjSequence + " " + zwjSequence + " " + zwjSequence);
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.assertEquals("| " + zwjSequence + " " + zwjSequence + " " + zwjSequence);
+ moveCursorToLeftCursorableOffset(state, paint);
+
+ // Emoji modifiers
+ // U+261D is WHITE UP POINTING INDEX, U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
+ state.setByString("| U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+261D U+1F3FB | U+261D U+1F3FB U+261D U+1F3FB");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+261D U+1F3FB U+261D U+1F3FB | U+261D U+1F3FB");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB |");
+ moveCursorToRightCursorableOffset(state, paint);
+ state.setByString("U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB |");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("U+261D U+1F3FB U+261D U+1F3FB | U+261D U+1F3FB");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("U+261D U+1F3FB | U+261D U+1F3FB U+261D U+1F3FB");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("| U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB");
+ moveCursorToLeftCursorableOffset(state, paint);
+ state.setByString("| U+261D U+1F3FB U+261D U+1F3FB U+261D U+1F3FB");
+ moveCursorToLeftCursorableOffset(state, paint);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
index 1966313..7519627 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
@@ -291,9 +291,6 @@
@Override
public void onActionModeFinished(ActionMode mode) {}
-
- @Override
- public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {}
}
private static final class MockActionModeCallback implements ActionMode.Callback {
diff --git a/docs/html/guide/topics/data/data-storage.jd b/docs/html/guide/topics/data/data-storage.jd
index 46db371..a745d00 100644
--- a/docs/html/guide/topics/data/data-storage.jd
+++ b/docs/html/guide/topics/data/data-storage.jd
@@ -178,6 +178,20 @@
android.content.Context#MODE_WORLD_READABLE}, and {@link
android.content.Context#MODE_WORLD_WRITEABLE}.</p>
+<p class="note"><strong>Note:</strong> The constants {@link
+android.content.Context#MODE_WORLD_READABLE} and {@link
+android.content.Context#MODE_WORLD_WRITEABLE} have been deprecated since API level 17.
+Starting from Android N their use will result in a {@link java.lang.SecurityException}
+to be thrown.
+This means that apps targeting Android N and higher
+cannot share private files by name, and attempts to share a "file://" URI will result in a
+{@link android.os.FileUriExposedException} to be thrown. If your app needs to share private
+files with other apps, it may use a {@link android.support.v4.content.FileProvider} with
+the {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}.
+See also <a
+href="{@docRoot}training/secure-file-sharing/index.html">Sharing Files</a>.
+</p>
+
<p>To read a file from internal storage:</p>
<ol>
diff --git a/docs/html/training/basics/data-storage/files.jd b/docs/html/training/basics/data-storage/files.jd
index 49a9169..58a1d5f 100644
--- a/docs/html/training/basics/data-storage/files.jd
+++ b/docs/html/training/basics/data-storage/files.jd
@@ -59,7 +59,7 @@
<p><b>Internal storage:</b></p>
<ul>
<li>It's always available.</li>
-<li>Files saved here are accessible by only your app by default.</li>
+<li>Files saved here are accessible by only your app.</li>
<li>When the user uninstalls your app, the system removes all your app's files from
internal storage.</li>
</ul>
@@ -83,6 +83,12 @@
with other apps or allow the user to access with a computer.</p>
</div>
+<p class="note">
+<strong>Note:</strong> Before Android N, internal files could be made accessible to other
+apps by means of relaxing file system permissions. This is no longer the case. If you wish
+to make the content of a private file accessible to other apps, your app may use the
+{@link android.support.v4.content.FileProvider}. See <a
+href="{@docRoot}training/secure-file-sharing/index.html">Sharing Files</a>.</p>
<p class="note" style="clear:both">
<strong>Tip:</strong> Although apps are installed onto the internal storage by
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 5efc00c..b6a209f 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -45,15 +45,11 @@
int outlineLeft, int outlineTop, int outlineRight, int outlineBottom,
float outlineRadius, int outlineAlpha, float decodeScale) {
opticalRect = new Rect(opticalLeft, opticalTop, opticalRight, opticalBottom);
- outlineRect = new Rect(outlineLeft, outlineTop, outlineRight, outlineBottom);
+ opticalRect.scale(decodeScale);
- if (decodeScale != 1.0f) {
- // if bitmap was scaled when decoded, scale the insets from the metadata values
- opticalRect.scale(decodeScale);
+ outlineRect = scaleInsets(outlineLeft, outlineTop,
+ outlineRight, outlineBottom, decodeScale);
- // round inward while scaling outline, as the outline should always be conservative
- outlineRect.scaleRoundIn(decodeScale);
- }
this.outlineRadius = outlineRadius * decodeScale;
this.outlineAlpha = outlineAlpha / 255.0f;
}
@@ -62,6 +58,23 @@
public final Rect outlineRect;
public final float outlineRadius;
public final float outlineAlpha;
+
+ /**
+ * Scales up the rect by the given scale, ceiling values, so actual outline Rect
+ * grows toward the inside.
+ */
+ public static Rect scaleInsets(int left, int top, int right, int bottom, float scale) {
+ if (scale == 1.0f) {
+ return new Rect(left, top, right, bottom);
+ }
+
+ Rect result = new Rect();
+ result.left = (int) Math.ceil(left * scale);
+ result.top = (int) Math.ceil(top * scale);
+ result.right = (int) Math.ceil(right * scale);
+ result.bottom = (int) Math.ceil(bottom * scale);
+ return result;
+ }
}
private final Bitmap mBitmap;
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index da3deff..de391af 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -16,6 +16,9 @@
package android.graphics;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
@@ -91,10 +94,22 @@
/** Replace the contents of this with the contents of src.
*/
- public void set(Path src) {
- if (this != src) {
- isSimplePath = src.isSimplePath;
- native_set(mNativePath, src.mNativePath);
+ public void set(@NonNull Path src) {
+ if (this == src) {
+ return;
+ }
+ isSimplePath = src.isSimplePath;
+ native_set(mNativePath, src.mNativePath);
+ if (!isSimplePath) {
+ return;
+ }
+
+ if (rects != null && src.rects != null) {
+ rects.set(src.rects);
+ } else if (rects != null && src.rects == null) {
+ rects.setEmpty();
+ } else if (src.rects != null) {
+ rects = new Region(src.rects);
}
}
@@ -685,13 +700,13 @@
* @param dst The translated path is written here. If this is null, then
* the original path is modified.
*/
- public void offset(float dx, float dy, Path dst) {
- long dstNative = 0;
+ public void offset(float dx, float dy, @Nullable Path dst) {
if (dst != null) {
- dstNative = dst.mNativePath;
- dst.isSimplePath = false;
+ dst.set(this);
+ } else {
+ dst = this;
}
- native_offset(mNativePath, dx, dy, dstNative);
+ dst.offset(dx, dy);
}
/**
@@ -701,7 +716,15 @@
* @param dy The amount in the Y direction to offset the entire path
*/
public void offset(float dx, float dy) {
- isSimplePath = false;
+ if (isSimplePath && rects == null) {
+ // nothing to offset
+ return;
+ }
+ if (isSimplePath && dx == Math.rint(dx) && dy == Math.rint(dy)) {
+ rects.translate((int) dx, (int) dy);
+ } else {
+ isSimplePath = false;
+ }
native_offset(mNativePath, dx, dy);
}
@@ -823,7 +846,6 @@
private static native void native_addPath(long nPath, long src, float dx, float dy);
private static native void native_addPath(long nPath, long src);
private static native void native_addPath(long nPath, long src, long matrix);
- private static native void native_offset(long nPath, float dx, float dy, long dst_path);
private static native void native_offset(long nPath, float dx, float dy);
private static native void native_setLastPoint(long nPath, float dx, float dy);
private static native void native_transform(long nPath, long matrix, long dst_path);
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 0416159..2848949 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -112,7 +112,7 @@
* Given a start and stop distance, return in dst the intervening
* segment(s). If the segment is zero-length, return false, else return
* true. startD and stopD are pinned to legal values (0..getLength()).
- * If startD <= stopD then return false (and leave dst untouched).
+ * If startD >= stopD then return false (and leave dst untouched).
* Begin the segment with a moveTo if startWithMoveTo is true.
*
* <p>On {@link android.os.Build.VERSION_CODES#KITKAT} and earlier
@@ -121,6 +121,19 @@
* such as <code>dst.rLineTo(0, 0)</code>.</p>
*/
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) {
+ // Skia used to enforce this as part of it's API, but has since relaxed that restriction
+ // so to maintain consistency in our API we enforce the preconditions here.
+ float length = getLength();
+ if (startD < 0) {
+ startD = 0;
+ }
+ if (stopD > length) {
+ stopD = length;
+ }
+ if (startD >= stopD) {
+ return false;
+ }
+
dst.isSimplePath = false;
return native_getSegment(native_instance, startD, stopD, dst.ni(), startWithMoveTo);
}
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 93ef3f0..7f579a2 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -647,16 +647,4 @@
}
}
- /**
- * Scales up the rect by the given scale, rounding values toward the inside.
- * @hide
- */
- public void scaleRoundIn(float scale) {
- if (scale != 1.0f) {
- left = (int) Math.ceil(left * scale);
- top = (int) Math.ceil(top * scale);
- right = (int) Math.floor(right * scale);
- bottom = (int) Math.floor(bottom * scale);
- }
- }
}
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 51221b4..2b950d3 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -243,13 +243,15 @@
}
/**
- * Invokes {@link #loadDrawable(Context)} on a background thread
- * and then runs <code>andThen</code> on the UI thread when finished.
+ * Invokes {@link #loadDrawable(Context)} on a background thread and notifies the <code>
+ * {@link OnDrawableLoadedListener#onDrawableLoaded listener} </code> on the {@code handler}
+ * when finished.
*
* @param context {@link Context Context} in which to load the drawable; see
* {@link #loadDrawable(Context)}
- * @param listener a callback to run on the provided
- * @param handler {@link Handler} on which to run <code>andThen</code>.
+ * @param listener to be {@link OnDrawableLoadedListener#onDrawableLoaded notified} when
+ * {@link #loadDrawable(Context)} finished
+ * @param handler {@link Handler} on which to notify the {@code listener}
*/
public void loadDrawableAsync(Context context, final OnDrawableLoadedListener listener,
Handler handler) {
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 5b1cc80..d962385 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -706,18 +706,9 @@
final NinePatch.InsetStruct insets = ninePatch.getBitmap().getNinePatchInsets();
if (insets != null) {
- if (mOutlineInsets == null) {
- mOutlineInsets = new Rect();
- }
- final Rect outlineInsets = insets.outlineRect;
- mOutlineInsets.left = Drawable.scaleFromDensity(
- outlineInsets.left, sourceDensity, targetDensity, false);
- mOutlineInsets.top = Drawable.scaleFromDensity(
- outlineInsets.top, sourceDensity, targetDensity, false);
- mOutlineInsets.right = Drawable.scaleFromDensity(
- outlineInsets.right, sourceDensity, targetDensity, false);
- mOutlineInsets.bottom = Drawable.scaleFromDensity(
- outlineInsets.bottom, sourceDensity, targetDensity, false);
+ Rect outlineRect = insets.outlineRect;
+ mOutlineInsets = NinePatch.InsetStruct.scaleInsets(outlineRect.left, outlineRect.top,
+ outlineRect.right, outlineRect.bottom, targetDensity / (float) sourceDensity);
mOutlineRadius = Drawable.scaleFromDensity(
insets.outlineRadius, sourceDensity, targetDensity);
} else {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index 156f45f6..be390ff 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -193,12 +193,12 @@
putSignatureImpl("NONEwithECDSA",
PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE");
- putSignatureImpl("ECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1");
- put("Alg.Alias.Signature.SHA1withECDSA", "ECDSA");
- put("Alg.Alias.Signature.ECDSAwithSHA1", "ECDSA");
+ putSignatureImpl("SHA1withECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1");
+ put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
+ put("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
// iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1)
- put("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA");
- put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "ECDSA");
+ put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
+ put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "SHA1withECDSA");
// iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
putSignatureImpl("SHA224withECDSA",
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 06b712e..ea4f4eb 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -506,6 +506,22 @@
renderer.renderGlop(state, glop);
}
+void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, const BakedOpState& state) {
+ SkPaint paint;
+ paint.setColor(op.color);
+ paint.setXfermodeMode(op.mode);
+
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshUnitQuad()
+ .setFillPaint(paint, state.alpha)
+ .setTransform(Matrix4::identity(), TransformFlags::None)
+ .setModelViewMapUnitToRect(state.computedState.clipState->rect)
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
renderer.renderFunctor(op, state);
}
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index fae8e48..0ebb886 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -572,6 +572,12 @@
deferOvalOp(*resolvedOp);
}
+void FrameBuilder::deferColorOp(const ColorOp& op) {
+ BakedOpState* bakedState = tryBakeUnboundedOpState(op);
+ if (!bakedState) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
+}
+
void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
BakedOpState* bakedState = tryBakeUnboundedOpState(op);
if (!bakedState) return; // quick rejected
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index c305f65..2246cf9c 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -21,6 +21,8 @@
#include <cstdio>
#include <errno.h>
#include <inttypes.h>
+#include <limits>
+#include <cmath>
#include <sys/mman.h>
namespace android {
@@ -202,6 +204,40 @@
}
+static bool shouldReplace(SlowFrame& existing, SlowFrame& candidate) {
+ if (candidate.whenHours - existing.whenHours >= 24) {
+ // If the old slowframe is over 24 hours older than the candidate,
+ // replace it. It's too stale
+ return true;
+ }
+ if (candidate.frametimeMs > existing.frametimeMs) {
+ return true;
+ }
+ return false;
+}
+
+void JankTracker::updateSlowest(const FrameInfo& frame) {
+ uint16_t durationMs = static_cast<uint16_t>(std::min(
+ ns2ms(frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::IntendedVsync]),
+ static_cast<nsecs_t>(std::numeric_limits<uint16_t>::max())));
+ uint16_t startHours = static_cast<uint16_t>(std::lround(
+ ns2s(frame[FrameInfoIndex::IntendedVsync]) / 3600.0f));
+ SlowFrame* toReplace = nullptr;
+ SlowFrame thisFrame{startHours, durationMs};
+ // First find the best candidate for replacement
+ for (SlowFrame& existing : mData->slowestFrames) {
+ // If we should replace the current data with the replacement candidate,
+ // it means the current data is worse than the replacement candidate
+ if (!toReplace || shouldReplace(existing, *toReplace)) {
+ toReplace = &existing;
+ }
+ }
+ // Now see if we should replace it
+ if (shouldReplace(*toReplace, thisFrame)) {
+ *toReplace = thisFrame;
+ }
+}
+
void JankTracker::addFrame(const FrameInfo& frame) {
mData->totalFrameCount++;
// Fast-path for jank-free frames
@@ -215,6 +251,11 @@
return;
}
+ // For slowest frames we are still interested in frames that are otherwise
+ // exempt (such as first-draw). Although those frames don't directly impact
+ // smoothness, they do impact responsiveness.
+ updateSlowest(frame);
+
if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
return;
}
@@ -247,6 +288,11 @@
dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
+ dprintf(fd, "\nSlowest frames over last 24h: ");
+ for (auto& slowFrame : data->slowestFrames) {
+ if (!slowFrame.frametimeMs) continue;
+ dprintf(fd, "%ums ", slowFrame.frametimeMs);
+ }
for (int i = 0; i < NUM_BUCKETS; i++) {
dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
}
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 3887e5e..1a4a489 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -39,6 +39,11 @@
NUM_BUCKETS,
};
+struct SlowFrame {
+ uint16_t whenHours; // When this occurred in CLOCK_MONOTONIC in hours
+ uint16_t frametimeMs; // How long the frame took in ms
+};
+
// Try to keep as small as possible, should match ASHMEM_SIZE in
// GraphicsStatsService.java
struct ProfileData {
@@ -49,6 +54,8 @@
uint32_t totalFrameCount;
uint32_t jankFrameCount;
nsecs_t statStartTime;
+
+ std::array<SlowFrame, 10> slowestFrames;
};
// TODO: Replace DrawProfiler with this
@@ -71,6 +78,7 @@
private:
void freeData();
void setFrameInterval(nsecs_t frameIntervalNanos);
+ void updateSlowest(const FrameInfo& frame);
static uint32_t findPercentile(const ProfileData* data, int p);
static void dumpData(const ProfileData* data, int fd);
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 96a57b6..64c604a 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -90,6 +90,7 @@
UNMERGEABLE_OP_FN(ArcOp) \
UNMERGEABLE_OP_FN(BitmapMeshOp) \
UNMERGEABLE_OP_FN(BitmapRectOp) \
+ UNMERGEABLE_OP_FN(ColorOp) \
UNMERGEABLE_OP_FN(FunctorOp) \
UNMERGEABLE_OP_FN(LinesOp) \
UNMERGEABLE_OP_FN(OvalOp) \
@@ -256,6 +257,16 @@
const float* radius;
};
+struct ColorOp : RecordedOp {
+ // Note: unbounded op that will fillclip, so no bounds/matrix needed
+ ColorOp(const ClipBase* localClip, int color, SkXfermode::Mode mode)
+ : RecordedOp(RecordedOpId::ColorOp, Rect(), Matrix4::identity(), localClip, nullptr)
+ , color(color)
+ , mode(mode) {}
+ const int color;
+ const SkXfermode::Mode mode;
+};
+
struct FunctorOp : RecordedOp {
// Note: undefined record-time bounds, since this op fills the clip
// TODO: explicitly define bounds
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 4eeadb7..f43dade 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -234,10 +234,10 @@
// android/graphics/Canvas draw operations
// ----------------------------------------------------------------------------
void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
- SkPaint paint;
- paint.setColor(color);
- paint.setXfermodeMode(mode);
- drawPaint(paint);
+ addOp(alloc().create_trivial<ColorOp>(
+ getRecordedClip(),
+ color,
+ mode));
}
void RecordingCanvas::drawPaint(const SkPaint& paint) {
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 1eb4fa0..acb88e2 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -191,13 +191,16 @@
const SkPaint* paint) override;
// Text
- virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
- const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
- float boundsRight, float boundsBottom, float totalAdvance) override;
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) override;
virtual bool drawTextAbsolutePos() const override { return false; }
+protected:
+ virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) override;
+ virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+
private:
const ClipBase* getRecordedClip() {
return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc());
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index b1ecb71..5d24fa0 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -147,13 +147,6 @@
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
- virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
- const SkPaint& paint, float x, float y,
- float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
- float totalAdvance) override;
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) override;
-
virtual bool drawTextAbsolutePos() const override { return true; }
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
@@ -169,6 +162,14 @@
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
virtual void callDrawGLFunction(Functor* functor) override;
+protected:
+ virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) override;
+ virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+
private:
struct SaveRec {
int saveCount;
@@ -761,14 +762,8 @@
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) {
- // Set align to left for drawing, as we don't want individual
- // glyphs centered or right-aligned; the offset above takes
- // care of all alignment.
- SkPaint paintCopy(paint);
- paintCopy.setTextAlign(SkPaint::kLeft_Align);
-
static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
- mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
+ mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paint);
drawTextDecorations(x, y, totalAdvance, paint);
}
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 8c3eea3..7bfa15a 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -166,6 +166,11 @@
bounds.offset(x, y);
}
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ paint.setTextAlign(Paint::kLeft_Align);
+
DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
paint, x, y, bounds, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index dc669f0..5dbda43 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -53,6 +53,7 @@
} // namespace SaveFlags
namespace uirenderer {
+class SkiaCanvasProxy;
namespace VectorDrawable {
class Tree;
};
@@ -205,19 +206,6 @@
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) = 0;
- // Text
- /**
- * drawText: count is of glyphs
- * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
- */
- virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
- const SkPaint& paint, float x, float y,
- float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
- float totalAdvance) = 0;
- /** drawTextOnPath: count is of glyphs */
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) = 0;
-
/**
* Specifies if the positions passed to ::drawText are absolute or relative
* to the (x,y) value provided.
@@ -244,6 +232,22 @@
protected:
void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+
+ /**
+ * drawText: count is of glyphs
+ * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
+ */
+ virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) = 0;
+ /** drawTextOnPath: count is of glyphs */
+ virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) = 0;
+
+ friend class DrawTextFunctor;
+ friend class DrawTextOnPathFunctor;
+ friend class uirenderer::SkiaCanvasProxy;
};
}; // namespace android
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 69c321c..9599c30 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -30,6 +30,7 @@
public:
Paint();
Paint(const Paint& paint);
+ Paint(const SkPaint& paint);
~Paint();
Paint& operator=(const Paint& other);
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 1172a0e..b27672c 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -29,6 +29,11 @@
mHyphenEdit(paint.mHyphenEdit) {
}
+Paint::Paint(const SkPaint& paint) : SkPaint(paint),
+ mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0),
+ mFontVariant(VARIANT_DEFAULT) {
+}
+
Paint::~Paint() {
}
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a4aee61..059e9ae 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -16,6 +16,7 @@
#include "TestUtils.h"
+#include "hwui/Paint.h"
#include "DeferredLayerUpdater.h"
#include "LayerRenderer.h"
@@ -41,17 +42,20 @@
sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
- std::function<void(Matrix4*)> transformSetupCallback) {
+ const SkMatrix& transform) {
+ Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState());
+
+ sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer);
+ layerUpdater->setSize(width, height);
+ layerUpdater->setTransform(&transform);
+
+ // updateLayer so it's ready to draw
bool isOpaque = true;
bool forceFilter = true;
GLenum renderTarget = GL_TEXTURE_EXTERNAL_OES;
-
- Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState());
LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, forceFilter,
- renderTarget, Matrix4::identity().data);
- transformSetupCallback(&(layer->getTransform()));
+ renderTarget, Matrix4::identity().data);
- sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer);
return layerUpdater;
}
@@ -87,50 +91,17 @@
*outTotalAdvance = totalAdvance;
}
-void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+
+void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, float x, float y) {
- // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
- LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
- "must use glyph encoding");
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- float totalAdvance;
- Rect bounds;
- layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds);
-
- // apply alignment via x parameter (which JNI layer would have done)
- if (paint.getTextAlign() == SkPaint::kCenter_Align) {
- x -= totalAdvance / 2;
- } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
- x -= totalAdvance;
- }
-
- bounds.translate(x, y);
-
- // Force left alignment, since alignment offset is already baked in
- SkPaint alignPaintCopy(paint);
- alignPaintCopy.setTextAlign(SkPaint::kLeft_Align);
- canvas->drawGlyphs(glyphs.data(), positions.data(), glyphs.size(), alignPaintCopy, x, y,
- bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
+ auto utf16 = asciiToUtf16(text);
+ canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, 0, paint, nullptr);
}
-void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, const SkPath& path) {
- // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
- LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
- "must use glyph encoding");
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
- std::vector<glyph_t> glyphs;
- while (*text != '\0') {
- SkUnichar unichar = SkUTF8_NextUnichar(&text);
- glyphs.push_back(autoCache.getCache()->unicharToGlyph(unichar));
- }
- canvas->drawGlyphsOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
+ auto utf16 = asciiToUtf16(text);
+ canvas->drawTextOnPath(utf16.get(), strlen(text), 0, path, 0, 0, paint, nullptr);
}
void TestUtils::TestTask::run() {
@@ -143,12 +114,13 @@
renderState.onGLContextDestroyed();
}
-std::unique_ptr<uint16_t[]> TestUtils::utf8ToUtf16(const char* str) {
- const size_t strLen = strlen(str);
- const ssize_t utf16Len = utf8_to_utf16_length((uint8_t*) str, strLen);
- std::unique_ptr<uint16_t[]> dst(new uint16_t[utf16Len + 1]);
- utf8_to_utf16((uint8_t*) str, strLen, (char16_t*) dst.get());
- return dst;
+std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
+ const int length = strlen(str);
+ std::unique_ptr<uint16_t[]> utf16(new uint16_t[length]);
+ for (int i = 0; i < length; i++) {
+ utf16.get()[i] = str[i];
+ }
+ return utf16;
}
} /* namespace uirenderer */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index a5e7a5f..2d1e2e9 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -122,7 +122,7 @@
static sp<DeferredLayerUpdater> createTextureLayerUpdater(
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
- std::function<void(Matrix4*)> transformSetupCallback);
+ const SkMatrix& transform);
template<class CanvasType>
static std::unique_ptr<DisplayList> createDisplayList(int width, int height,
@@ -207,13 +207,13 @@
std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
float* outTotalAdvance, Rect* outBounds);
- static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+ static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, float x, float y);
- static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+ static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, const SkPath& path);
- static std::unique_ptr<uint16_t[]> utf8ToUtf16(const char* str);
+ static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str);
private:
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 52039ef..63c067b 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -42,7 +42,7 @@
}
void doFrame(int frameNr) override {
- std::unique_ptr<uint16_t[]> text = TestUtils::utf8ToUtf16(
+ std::unique_ptr<uint16_t[]> text = TestUtils::asciiToUtf16(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
ssize_t textLength = 26 * 2;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 0aabfb1..dca56d4 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -344,8 +344,9 @@
EXPECT_TRUE(stroke.contains(fill));
EXPECT_FALSE(fill.contains(stroke));
+ // outset by half the stroke width
Rect outsetFill(fill);
- outsetFill.outset(10);
+ outsetFill.outset(5);
EXPECT_EQ(stroke, outsetFill);
}
};
@@ -386,9 +387,7 @@
};
auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
- [](Matrix4* transform) {
- transform->loadTranslate(5, 5, 0);
- });
+ SkMatrix::MakeTrans(5, 5));
auto node = TestUtils::createNode(0, 0, 200, 200,
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
@@ -428,7 +427,31 @@
EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
}
-RENDERTHREAD_TEST(FrameBuilder, renderNode) {
+RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
+ class ColorTestRenderer : public TestRendererBase {
+ public:
+ void onColorOp(const ColorOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
+ << "Color op should be expanded to bounds of surrounding";
+ }
+ };
+
+ auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setClipToBounds(false);
+ canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ });
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ TestUtils::createSyncedNodeList(unclippedColorView),
+ sLightGeometry, Caches::getInstance());
+ ColorTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
+}
+
+TEST(FrameBuilder, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 6ab5110..1c240db 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -23,6 +23,9 @@
#include <tests/common/TestUtils.h>
#include <utils/Color.h>
+#include <SkGradientShader.h>
+#include <SkShader.h>
+
namespace android {
namespace uirenderer {
@@ -223,9 +226,9 @@
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
auto op = *(dl->getOps()[0]);
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+ EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
EXPECT_EQ(nullptr, op.localClip);
- EXPECT_TRUE(op.unmappedBounds.contains(Rect(200, 200))) << "Expect recording/clip bounds";
+ EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
}
TEST(RecordingCanvas, backgroundAndImage) {
@@ -567,36 +570,69 @@
TEST(RecordingCanvas, refPaint) {
SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- paint.setTextAlign(SkPaint::kLeft_Align);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
paint.setColor(SK_ColorBLUE);
- // first three should use same paint
+ // first two should use same paint
canvas.drawRect(0, 0, 200, 10, paint);
SkPaint paintCopy(paint);
canvas.drawRect(0, 10, 200, 20, paintCopy);
- TestUtils::drawUtf8ToCanvas(&canvas, "helloworld", paint, 50, 25);
// only here do we use different paint ptr
paint.setColor(SK_ColorRED);
canvas.drawRect(0, 20, 200, 30, paint);
});
auto ops = dl->getOps();
- ASSERT_EQ(4u, ops.size());
+ ASSERT_EQ(3u, ops.size());
- // first three are the same
+ // first two are the same
EXPECT_NE(nullptr, ops[0]->paint);
EXPECT_NE(&paint, ops[0]->paint);
EXPECT_EQ(ops[0]->paint, ops[1]->paint);
- EXPECT_EQ(ops[0]->paint, ops[2]->paint);
// last is different, but still copied / non-null
- EXPECT_NE(nullptr, ops[3]->paint);
- EXPECT_NE(ops[0]->paint, ops[3]->paint);
- EXPECT_NE(&paint, ops[3]->paint);
+ EXPECT_NE(nullptr, ops[2]->paint);
+ EXPECT_NE(ops[0]->paint, ops[2]->paint);
+ EXPECT_NE(&paint, ops[2]->paint);
+}
+
+TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
+ SkPaint paint;
+ SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
+ SkShader::TileMode::kClamp_TileMode,
+ SkShader::TileMode::kClamp_TileMode));
+ paint.setShader(shader);
+ canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
+ });
+ auto& bitmaps = dl->getBitmapResources();
+ EXPECT_EQ(1u, bitmaps.size());
+}
+
+TEST(RecordingCanvas, refBitmapInShader_composeShader) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
+ SkPaint paint;
+ SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
+ SkShader::TileMode::kClamp_TileMode,
+ SkShader::TileMode::kClamp_TileMode));
+
+ SkPoint center;
+ center.set(50, 50);
+ SkColor colors[2];
+ colors[0] = Color::Black;
+ colors[1] = Color::White;
+ SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
+ SkShader::TileMode::kRepeat_TileMode));
+
+ SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
+ SkXfermode::Mode::kMultiply_Mode));
+ paint.setShader(composeShader);
+ canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
+ });
+ auto& bitmaps = dl->getBitmapResources();
+ EXPECT_EQ(1u, bitmaps.size());
}
TEST(RecordingCanvas, drawText) {
@@ -605,7 +641,7 @@
paint.setAntiAlias(true);
paint.setTextSize(20);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+ std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
});
@@ -629,7 +665,7 @@
paint.setAntiAlias(true);
paint.setTextSize(20);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+ std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
});
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index a5b3179..4bf0852 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1888,6 +1888,19 @@
for (ExifTag tag : IFD_POINTER_TAGS) {
setAttribute(tag.name, null);
}
+ // Remove old thumbnail data
+ setAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name, null);
+ setAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, null);
+
+ // Remove null value tags.
+ for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
+ for (Object obj : mAttributes[hint].entrySet().toArray()) {
+ Map.Entry entry = (Map.Entry) obj;
+ if (entry.getValue() == null) {
+ mAttributes[hint].remove(entry.getKey());
+ }
+ }
+ }
// Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
// offset when there is one or more tags in the thumbnail IFD.
@@ -1900,25 +1913,12 @@
if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, "0");
}
- // Remove old thumbnail data
- setAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name, null);
- setAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, null);
if (mHasThumbnail) {
mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, "0");
mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
String.valueOf(mThumbnailLength));
}
- // Remove null value tags.
- for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
- for (Object obj : mAttributes[hint].entrySet().toArray()) {
- Map.Entry entry = (Map.Entry) obj;
- if (entry.getValue() == null) {
- mAttributes[hint].remove(entry.getKey());
- }
- }
- }
-
// Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
// value which has a bigger size than 4 bytes.
for (int i = 0; i < 5; ++i) {
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index fe2796c..c1805cb 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -57,8 +57,9 @@
* <h3>Standard Extra Data</h3>
*
* <p>These are the current standard fields that can be used as extra data via
- * {@link #subscribe(String, Bundle, SubscriptionCallback)}, {@link #unsubscribe(String, Bundle)},
- * and {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
+ * {@link #subscribe(String, Bundle, SubscriptionCallback)},
+ * {@link #unsubscribe(String, SubscriptionCallback)}, and
+ * {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
*
* <ul>
* <li> {@link #EXTRA_PAGE}
@@ -383,7 +384,7 @@
}
/**
- * Unsubscribes for changes to the children of the specified media id.
+ * Unsubscribes for changes to the children of the specified media id through a callback.
* <p>
* The query callback will no longer be invoked for results associated with
* this id once this method returns.
@@ -391,13 +392,13 @@
*
* @param parentId The id of the parent media item whose list of children
* will be unsubscribed.
- * @param options A bundle sent to the media browse service to subscribe.
+ * @param callback A callback sent to the media browse service to subscribe.
*/
- public void unsubscribe(@NonNull String parentId, @NonNull Bundle options) {
- if (options == null) {
- throw new IllegalArgumentException("options are null");
+ public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null");
}
- unsubscribeInternal(parentId, options);
+ unsubscribeInternal(parentId, callback);
}
/**
@@ -490,7 +491,7 @@
}
}
- private void unsubscribeInternal(String parentId, Bundle options) {
+ private void unsubscribeInternal(String parentId, SubscriptionCallback callback) {
// Check arguments.
if (TextUtils.isEmpty(parentId)) {
throw new IllegalArgumentException("parentId is empty.");
@@ -500,16 +501,21 @@
Subscription sub = mSubscriptions.get(parentId);
// Tell the service if necessary.
- if (sub != null && sub.removeCallback(options) && mState == CONNECT_STATE_CONNECTED) {
+ if (mState == CONNECT_STATE_CONNECTED && sub != null) {
try {
- // NOTE: Do not call removeSubscriptionWithOptions when options are null. Otherwise,
- // it will break the action of support library which expects removeSubscription will
- // be called when options are null.
- if (options == null) {
+ if (callback == null) {
mServiceBinder.removeSubscription(parentId, mServiceCallbacks);
} else {
- mServiceBinder.removeSubscriptionWithOptions(
- parentId, options, mServiceCallbacks);
+ final List<SubscriptionCallback> callbacks = sub.getCallbacks();
+ final List<Bundle> optionsList = sub.getOptionsList();
+ for (int i = callbacks.size() - 1; i >= 0; --i) {
+ if (callbacks.get(i) == callback) {
+ mServiceBinder.removeSubscriptionWithOptions(
+ parentId, optionsList.get(i), mServiceCallbacks);
+ callbacks.remove(i);
+ optionsList.remove(i);
+ }
+ }
}
} catch (RemoteException ex) {
// Process is crashing. We will disconnect, and upon reconnect we will
@@ -517,7 +523,8 @@
Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
}
}
- if (sub != null && sub.isEmpty()) {
+
+ if (sub != null && (sub.isEmpty() || callback == null)) {
mSubscriptions.remove(parentId);
}
}
@@ -1118,16 +1125,5 @@
mCallbacks.add(callback);
mOptionsList.add(options);
}
-
- public boolean removeCallback(Bundle options) {
- for (int i = 0; i < mOptionsList.size(); ++i) {
- if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
- mCallbacks.remove(i);
- mOptionsList.remove(i);
- return true;
- }
- }
- return false;
- }
}
}
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 7c9591d..acf94f4c 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -1039,6 +1039,15 @@
}
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.TV_INPUT_HIDDEN_INPUTS, builder.toString(), userId);
+
+ // Notify of the TvInputInfo changes.
+ TvInputManager tm = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+ for (String inputId : hiddenInputIds) {
+ TvInputInfo info = tm.getTvInputInfo(inputId);
+ if (info != null) {
+ tm.updateTvInputInfo(info);
+ }
+ }
}
/**
@@ -1069,6 +1078,15 @@
}
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.TV_INPUT_CUSTOM_LABELS, builder.toString(), userId);
+
+ // Notify of the TvInputInfo changes.
+ TvInputManager tm = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+ for (String inputId : customLabels.keySet()) {
+ TvInputInfo info = tm.getTvInputInfo(inputId);
+ if (info != null) {
+ tm.updateTvInputInfo(info);
+ }
+ }
}
private static void ensureValidField(String value) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 612a147..97ef6d8 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1689,19 +1689,20 @@
public abstract void onTune(Uri channelUri);
/**
- * Called when the application requests to tune to a given channel for TV program recording.
+ * Calls {@link #onTune(Uri)}. Override this method in order to handle domain-specific
+ * features that are only known between certain TV inputs and their clients.
*
* <p>The application may call this method before starting or after stopping recording, but
* not during recording.
*
- * <p>The session must call {@link #notifyTuned()} if the tune request was fulfilled, or
+ * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or
* {@link #notifyError(int)} otherwise.
*
* @param channelUri The URI of a channel.
- * @param params Extra parameters.
- * @hide
+ * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers
+ * will not create conflicting keys.
*/
- @SystemApi
public void onTune(Uri channelUri, Bundle params) {
onTune(channelUri);
}
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index da1002d..a5ff29f 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -91,22 +91,23 @@
* Tunes to a given channel for TV program recording. The first tune request will create a new
* recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
- * session, this method throws an exception.
+ * session, this method throws an exception. This can be used to provide domain-specific
+ * features that are only known between certain client and their TV inputs.
*
* <p>The application may call this method before starting or after stopping recording, but not
* during recording.
*
* <p>The recording session will respond by calling
- * {@link RecordingCallback#onTuned()} if the tune request was fulfilled, or
+ * {@link RecordingCallback#onTuned(Uri)} if the tune request was fulfilled, or
* {@link RecordingCallback#onError(int)} otherwise.
*
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel.
- * @param params Extra parameters.
+ * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers will
+ * not create conflicting keys.
* @throws IllegalStateException If recording is already started.
- * @hide
*/
- @SystemApi
public void tune(String inputId, Uri channelUri, Bundle params) {
if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
if (TextUtils.isEmpty(inputId)) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 0e7013c..aa99248 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -355,6 +355,18 @@
}
}
+ /**
+ * Returns object size in 64-bit integer.
+ *
+ * The object size stored in MtpObjectInfo is unsigned 32-bit integer.
+ * The methods reads 64-bit object size from the object property so that it can fetch 4GB+
+ * object size correctly.
+ * @hide
+ */
+ public long getObjectSizeLong(int handle, int format) throws IOException {
+ return native_get_object_size_long(handle, format);
+ }
+
// used by the JNI code
private long mNativeContext;
@@ -381,4 +393,5 @@
private native int native_submit_event_request();
private native MtpEvent native_reap_event_request(int handle);
private native void native_discard_event_request(int handle);
+ private native long native_get_object_size_long(int handle, int format) throws IOException;
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index f593685..ae86632 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -574,6 +574,9 @@
* Remove the subscription.
*/
private boolean removeSubscription(String id, ConnectionRecord connection, Bundle options) {
+ if (options == null) {
+ return connection.subscriptions.remove(id) != null;
+ }
boolean removed = false;
List<Bundle> optionsList = connection.subscriptions.get(id);
if (optionsList != null) {
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 0ecb750..f7c6770 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -42,6 +42,7 @@
#include "MtpDeviceInfo.h"
#include "MtpStorageInfo.h"
#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
using namespace android;
@@ -700,6 +701,40 @@
device->discardEventRequest(seq);
}
+static jlong android_mtp_MtpDevice_get_object_size_long(
+ JNIEnv *env, jobject thiz, jint handle, jint format) {
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
+ return 0;
+ }
+
+ std::unique_ptr<MtpProperty> property(
+ device->getObjectPropDesc(MTP_PROPERTY_OBJECT_SIZE, format));
+ if (!property) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property desc.");
+ return 0;
+ }
+
+ if (property->getDataType() != MTP_TYPE_UINT64) {
+ env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
+ return 0;
+ }
+
+ if (!device->getObjectPropValue(handle, property.get())) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
+ return 0;
+ }
+
+ const jlong object_size = static_cast<jlong>(property->getCurrentValue().u.u64);
+ if (object_size < 0) {
+ env->ThrowNew(clazz_io_exception, "Object size is too large to express as jlong.");
+ return 0;
+ }
+
+ return object_size;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -733,6 +768,8 @@
{"native_reap_event_request", "(I)Landroid/mtp/MtpEvent;",
(void *)android_mtp_MtpDevice_reap_event_request},
{"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request},
+
+ {"native_get_object_size_long", "(II)J", (void *)android_mtp_MtpDevice_get_object_size_long},
};
int register_android_mtp_MtpDevice(JNIEnv *env)
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index aea8585..f21fd88 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -27,7 +27,8 @@
<activity
android:name="com.android.captiveportallogin.CaptivePortalLoginActivity"
android:label="@string/action_bar_label"
- android:theme="@style/AppTheme" >
+ android:theme="@style/AppTheme"
+ android:configChanges="keyboardHidden|orientation|screenSize" >
<intent-filter>
<action android:name="android.net.conn.CAPTIVE_PORTAL"/>
<category android:name="android.intent.category.DEFAULT"/>
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index be08385..14609b2 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -44,7 +44,7 @@
<activity
android:name=".LauncherActivity"
android:theme="@android:style/Theme.NoDisplay"
- android:icon="@drawable/ic_files_app"
+ android:icon="@mipmap/ic_launcher_download"
android:label="@string/downloads_label">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -55,7 +55,7 @@
<activity
android:name=".FilesActivity"
android:theme="@style/DocumentsTheme"
- android:icon="@drawable/ic_files_app"
+ android:icon="@mipmap/ic_launcher_download"
android:label="@string/downloads_label"
android:documentLaunchMode="intoExisting">
<intent-filter>
diff --git a/packages/DocumentsUI/res/color/item_root_primary_text.xml b/packages/DocumentsUI/res/color/item_root_primary_text.xml
index 551245f..a5a65b2 100644
--- a/packages/DocumentsUI/res/color/item_root_primary_text.xml
+++ b/packages/DocumentsUI/res/color/item_root_primary_text.xml
@@ -15,8 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:state_activated="true" android:color="?android:colorAccent" />
- <item android:state_focused="false" android:state_activated="true" android:color="?android:colorAccent" />
+ <item android:state_focused="true" android:state_activated="true" android:color="@color/root_activated_color" />
+ <item android:state_focused="false" android:state_activated="true" android:color="@color/root_activated_color" />
<item android:state_enabled="false" android:alpha="@*android:dimen/disabled_alpha_material_light" android:color="@*android:color/primary_text_default_material_light" />
<item android:color="@*android:color/primary_text_default_material_light" />
</selector>
diff --git a/packages/DocumentsUI/res/drawable/ic_files_app.xml b/packages/DocumentsUI/res/drawable/ic_files_app.xml
deleted file mode 100644
index 76e3ba6..0000000
--- a/packages/DocumentsUI/res/drawable/ic_files_app.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_launcher_download"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/layout/dialog_file_name.xml b/packages/DocumentsUI/res/layout/dialog_file_name.xml
index 5ed476f..3a95a13 100644
--- a/packages/DocumentsUI/res/layout/dialog_file_name.xml
+++ b/packages/DocumentsUI/res/layout/dialog_file_name.xml
@@ -17,6 +17,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:fitsSystemWindows="true"
android:padding="?android:attr/listPreferredItemPaddingEnd">
<EditText
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 7aac620..a889b9f 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -21,6 +21,7 @@
android:orientation="horizontal"
android:baselineAligned="false"
android:gravity="center_vertical"
+ android:fitsSystemWindows="true"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
<FrameLayout
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_download.png
similarity index 100%
rename from packages/DocumentsUI/res/drawable-hdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_download.png
similarity index 100%
rename from packages/DocumentsUI/res/drawable-mdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_download.png
similarity index 100%
rename from packages/DocumentsUI/res/drawable-xhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_download.png
similarity index 100%
rename from packages/DocumentsUI/res/drawable-xxhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_download.png
similarity index 100%
rename from packages/DocumentsUI/res/drawable-xxxhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 5abb9d7..a3fdd78 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Kon nie dokument hernoem nie"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sommige lêers is omgeskakel"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Gee <xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang tot <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>-gids op <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Gee <xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang tot jou data, insluitend foto\'s en video\'s, op <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Moenie weer vra nie"</string>
<string name="allow" msgid="7225948811296386551">"Laat toe"</string>
<string name="deny" msgid="2081879885755434506">"Weier"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index b77b82cc..df73bed 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ሰነዱን ዳግም መሰየም አልተሳካም"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"አንዳንድ ፋይሎች ተለውጠዋል"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> በ<xliff:g id="STORAGE"><i>^3</i></xliff:g> ላይ የ<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ማውጫ መደረሻ ይሰጠው?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"በ<xliff:g id="STORAGE"><i>^2</i></xliff:g> ላይ ያሉትን ፎቶዎች እና ቪዲዮዎች ጨምሮ የውሂብዎ መዳረሻ ለ<xliff:g id="APPNAME"><b>^1</b></xliff:g> ይሰጥ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ዳግም አትጠይቅ"</string>
<string name="allow" msgid="7225948811296386551">"ይፍቀዱ"</string>
<string name="deny" msgid="2081879885755434506">"ያስተባብሉ"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index 1625043..d92eb5f 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -139,6 +139,7 @@
<string name="rename_error" msgid="4203041674883412606">"أخفقت إعادة تسمية المستند."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"تم تحويل بعض الملفات"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"هل تريد منح التطبيق <xliff:g id="APPNAME"><b>^1</b></xliff:g> حق الوصول إلى الدليل <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> على <xliff:g id="STORAGE"><i>^3</i></xliff:g>؟"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"هل تريد منح <xliff:g id="APPNAME"><b>^1</b></xliff:g> حق الوصول إلى بياناتك، بما في ذلك الصور ومقاطع الفيديو على <xliff:g id="STORAGE"><i>^2</i></xliff:g>؟"</string>
<string name="never_ask_again" msgid="4295278542972859268">"عدم السؤال مرة أخرى"</string>
<string name="allow" msgid="7225948811296386551">"السماح"</string>
<string name="deny" msgid="2081879885755434506">"رفض"</string>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 0f0eda4..632fa6b 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Sənəd adını dəyişmək uğursuz oldu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bəzi fayllar konvertasiya edilib"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> yaddaşında <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> kataloquna <xliff:g id="APPNAME"><b>^1</b></xliff:g> girişi təqdim edilsin?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> yaddaşında foto və videolar daxil olmaqla datanıza <xliff:g id="APPNAME"><b>^1</b></xliff:g> girişi təmin edilsin?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Bir daha soruşmayın"</string>
<string name="allow" msgid="7225948811296386551">"İcazə verin"</string>
<string name="deny" msgid="2081879885755434506">"Rədd et"</string>
diff --git a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
index 7bbf942..2d679ea 100644
--- a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
@@ -118,6 +118,7 @@
<string name="rename_error" msgid="4203041674883412606">"Preimenovanje dokumenta nije uspelo"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke datoteke su konvertovane"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Želite li da aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> odobrite pristup direktorijumu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> na memorijskom prostoru <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Želite da li da dozvolite da aplikacija <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristupa podacima, uključujući slike i video snimke, na lokaciji <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne pitaj ponovo"</string>
<string name="allow" msgid="7225948811296386551">"Dozvoli"</string>
<string name="deny" msgid="2081879885755434506">"Odbij"</string>
diff --git a/packages/DocumentsUI/res/values-be-rBY/strings.xml b/packages/DocumentsUI/res/values-be-rBY/strings.xml
index 4621d9b..abd4679 100644
--- a/packages/DocumentsUI/res/values-be-rBY/strings.xml
+++ b/packages/DocumentsUI/res/values-be-rBY/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Не атрымалася перайменаваць дакумент"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Некаторыя файлы былі сканвертаваныя"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Даць праграме <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ да дырэкторыі <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> у <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Даць <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ да вашых даных, у тым ліку фатаграфій і відэа, на <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Больш не пытацца"</string>
<string name="allow" msgid="7225948811296386551">"Дазволіць"</string>
<string name="deny" msgid="2081879885755434506">"Забараніць"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 329f92f..4822adb 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Преименуването на документа не бе успешно"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Някои файлове бяха преобразувани"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Да се предостави ли на <xliff:g id="APPNAME"><b>^1</b></xliff:g> достъп до директорията „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“ в/ъв <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Да се предостави ли на <xliff:g id="APPNAME"><b>^1</b></xliff:g> достъп до данните ви в хранилището (<xliff:g id="STORAGE"><i>^2</i></xliff:g>), включително снимки и видеоклипове?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Без повторно питане"</string>
<string name="allow" msgid="7225948811296386551">"Разрешаване"</string>
<string name="deny" msgid="2081879885755434506">"Отказване"</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 936e2dd..5709692 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -32,7 +32,7 @@
<string name="menu_share" msgid="3075149983979628146">"শেয়ার করুন"</string>
<string name="menu_delete" msgid="8138799623850614177">"মুছুন"</string>
<string name="menu_select_all" msgid="8323579667348729928">"সবগুলি নির্বাচন করুন"</string>
- <string name="menu_copy" msgid="3612326052677229148">"এতে অনুলিপি করুন…"</string>
+ <string name="menu_copy" msgid="3612326052677229148">"এতে কপি করুন…"</string>
<string name="menu_move" msgid="1828090633118079817">"এতে সরান..."</string>
<string name="menu_new_window" msgid="1226032889278727538">"নতুন উইন্ডো"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"প্রতিলিপি করুন"</string>
@@ -42,7 +42,7 @@
<string name="menu_file_size_show" msgid="3240323619260823076">"ফাইলের আকার দেখান"</string>
<string name="menu_file_size_hide" msgid="8881975928502581042">"ফাইলের আকার লুকান"</string>
<string name="button_select" msgid="527196987259139214">"নির্বাচন করুন"</string>
- <string name="button_copy" msgid="8706475544635021302">"অনুলিপি করুন"</string>
+ <string name="button_copy" msgid="8706475544635021302">"কপি করুন"</string>
<string name="button_move" msgid="2202666023104202232">"সরান"</string>
<string name="button_dismiss" msgid="3714065566893946085">"খারিজ করুন"</string>
<string name="button_retry" msgid="4392027584153752797">"আবার চেষ্টা করুন"</string>
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"দস্তাবেজের পুনঃনামকরণ ব্যর্থ হয়েছে৷"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"কিছু ফাইল রূপান্তরিত হয়েছে"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> কে <xliff:g id="STORAGE"><i>^3</i></xliff:g> এ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> সংগ্রহ অ্যাক্সেস করার মঞ্জুরি দিতে চান?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> এ থাকা ফটো ও ভিডিওগুলি সমেত <xliff:g id="APPNAME"><b>^1</b></xliff:g> কে আপনার ডেটা অ্যাক্সেস করার অনুমতি দেবেন?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"আর জিজ্ঞাসা করবেন না"</string>
<string name="allow" msgid="7225948811296386551">"অনুমতি দিন"</string>
<string name="deny" msgid="2081879885755434506">"আস্বীকার করুন"</string>
diff --git a/packages/DocumentsUI/res/values-bs-rBA/strings.xml b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
index d23c884..9c94a05 100644
--- a/packages/DocumentsUI/res/values-bs-rBA/strings.xml
+++ b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
@@ -118,6 +118,7 @@
<string name="rename_error" msgid="4203041674883412606">"Nije uspjelo preimenovanje dokumenta"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke od datoteka su pretvorene"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Omogućiti <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristup direktoriju <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> sa <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Želite li odobriti aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristup svojim podacima, uključujući fotografije i video zapise, na <xliff:g id="STORAGE"><i>^2</i></xliff:g> ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne pitaj ponovo"</string>
<string name="allow" msgid="7225948811296386551">"Dozvoli"</string>
<string name="deny" msgid="2081879885755434506">"Odbijte"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 044350a..4ecec9f 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"No s\'ha pogut canviar el nom del document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"S\'han convertit alguns fitxers"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vols que l\'aplicació <xliff:g id="APPNAME"><b>^1</b></xliff:g> tingui accés al directori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> de l\'emmagatzematge <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vols que l\'aplicació <xliff:g id="APPNAME"><b>^1</b></xliff:g> tingui accés a les dades de l\'emmagatzematge <xliff:g id="STORAGE"><i>^2</i></xliff:g>, incloses les fotos i els vídeos?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"No m\'ho demanis més"</string>
<string name="allow" msgid="7225948811296386551">"Permet"</string>
<string name="deny" msgid="2081879885755434506">"Denega"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index ca6f3f1..3c6e81d 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Dokument se nepodařilo přejmenovat."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Některé soubory byly převedeny"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Chcete aplikaci <xliff:g id="APPNAME"><b>^1</b></xliff:g> udělit přístup k adresáři <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> v úložišti <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Chcete aplikaci <xliff:g id="APPNAME"><b>^1</b></xliff:g> udělit přístup ke svým datům v úložišti <xliff:g id="STORAGE"><i>^2</i></xliff:g>, včetně fotek a videí?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Příště se neptat"</string>
<string name="allow" msgid="7225948811296386551">"Povolit"</string>
<string name="deny" msgid="2081879885755434506">"Odepřít"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index c4e7770..679506b 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumentet kunne ikke omdøbes"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Nogle filer er konverteret"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vil du give <xliff:g id="APPNAME"><b>^1</b></xliff:g> adgang til mappen <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> på <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vil du give <xliff:g id="APPNAME"><b>^1</b></xliff:g> adgang til dine data, herunder billeder og videoer på <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Spørg ikke igen"</string>
<string name="allow" msgid="7225948811296386551">"Tillad"</string>
<string name="deny" msgid="2081879885755434506">"Afvis"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 766222c..b48afb0 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Dokument konnte nicht umbenannt werden"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Einige Dateien wurden konvertiert"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> Zugriff auf das Verzeichnis <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> auf <xliff:g id="STORAGE"><i>^3</i></xliff:g> geben?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Möchtest du <xliff:g id="APPNAME"><b>^1</b></xliff:g> Zugriff auf deine Daten auf <xliff:g id="STORAGE"><i>^2</i></xliff:g> geben, einschließlich Fotos und Videos?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nicht mehr fragen"</string>
<string name="allow" msgid="7225948811296386551">"Zulassen"</string>
<string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 3bbcf776..3872c40 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Αποτυχία μετονομασίας εγγράφου"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Ορισμένα αρχεία μετατράπηκαν"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Να εκχωρηθεί στην εφαρμογή <xliff:g id="APPNAME"><b>^1</b></xliff:g> πρόσβαση στον κατάλογο <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> στον αποθηκευτικό χώρο <xliff:g id="STORAGE"><i>^3</i></xliff:g>;"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Θέλετε να εκχωρήσετε πρόσβαση στα δεδομένα σας στην εφαρμογή <xliff:g id="APPNAME"><b>^1</b></xliff:g>, συμπεριλαμβανομένων των φωτογραφιών και των βίντεό σας, στον αποθηκευτικό χώρο <xliff:g id="STORAGE"><i>^2</i></xliff:g>;"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Να μην ερωτηθώ ξανά"</string>
<string name="allow" msgid="7225948811296386551">"Να επιτρέπεται"</string>
<string name="deny" msgid="2081879885755434506">"Άρνηση"</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index d28b675..8a56f6c 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to your data, including photos and videos, on <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Don\'t ask again"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index d28b675..8a56f6c 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to your data, including photos and videos, on <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Don\'t ask again"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index d28b675..8a56f6c 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to your data, including photos and videos, on <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Don\'t ask again"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 6d847b5..3f36f41 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"No se pudo cambiar el nombre del documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Se convirtieron algunos archivos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"¿Otorgar acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> al directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> en <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"¿Quieres otorgar acceso a la app de <xliff:g id="APPNAME"><b>^1</b></xliff:g> a tus datos, incluidas tus fotos y videos en <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"No volver a preguntar"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 15b5694..a0da06c 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Error al cambiar el nombre del documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Se han convertido algunos archivos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"¿Permitir que <xliff:g id="APPNAME"><b>^1</b></xliff:g> acceda al directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> (<xliff:g id="STORAGE"><i>^3</i></xliff:g>)?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"¿Permitir que <xliff:g id="APPNAME"><b>^1</b></xliff:g> acceda a tus datos, incluidos los vídeos y las fotos de <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"No volver a preguntar"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index ed60aa9..d49b320 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumendi ümbernimetamine ebaõnnestus"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Mõned failid teisendati"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Kas anda rakendusele <xliff:g id="APPNAME"><b>^1</b></xliff:g> juurdepääs kataloogile <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> salvestusruumis <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Kas anda rakendusele <xliff:g id="APPNAME"><b>^1</b></xliff:g> juurdepääs teie andmetele (sh fotod ja videod) salvestusruumis <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ära enam küsi"</string>
<string name="allow" msgid="7225948811296386551">"Luba"</string>
<string name="deny" msgid="2081879885755434506">"Keela"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index f3d68b0d..085f148 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Ezin izan zaio aldatu izena dokumentuari"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Artxibo batzuk bihurtu dira"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> aplikazioari <xliff:g id="STORAGE"><i>^3</i></xliff:g> unitateko <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> direktorioa atzitzeko baimena eman nahi diozu?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> aplikazioari zure datuak atzitzea baimendu nahi diozu, besteak beste, <xliff:g id="STORAGE"><i>^2</i></xliff:g> biltegian dituzun argazkiak eta bideoak?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ez galdetu berriro"</string>
<string name="allow" msgid="7225948811296386551">"Onartu"</string>
<string name="deny" msgid="2081879885755434506">"Ukatu"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index 80e1aa8..114c7d0 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"نام سند تغییر نکرد"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"بعضی از فایلها تبدیل شدند"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"به <xliff:g id="APPNAME"><b>^1</b></xliff:g> اجازه داده شود به فهرست راهنمای <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> در <xliff:g id="STORAGE"><i>^3</i></xliff:g> دسترسی داشته باشد؟"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"به <xliff:g id="APPNAME"><b>^1</b></xliff:g> اجازه میدهید به دادههایتان دسترسی پیدا کند، از جمله عکسها و ویدیوهایتان در <xliff:g id="STORAGE"><i>^2</i></xliff:g>؟"</string>
<string name="never_ask_again" msgid="4295278542972859268">"دوباره سؤال نشود"</string>
<string name="allow" msgid="7225948811296386551">"ارزیابیشده"</string>
<string name="deny" msgid="2081879885755434506">"اجازه ندارد"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 279aad7..46f863f 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumentin nimen muuttaminen epäonnistui."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Joitakin tiedostoja muunnettiin."</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Myönnetäänkö sovellukselle <xliff:g id="APPNAME"><b>^1</b></xliff:g> sijainnissa <xliff:g id="STORAGE"><i>^3</i></xliff:g> olevan hakemiston <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> käyttöoikeus?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Myönnetäänkö sovellukselle <xliff:g id="APPNAME"><b>^1</b></xliff:g> sijainnissa <xliff:g id="STORAGE"><i>^2</i></xliff:g> olevien tietojesi, mukaan lukien valokuviesi ja videoidesi, käyttöoikeus?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Älä kysy uudestaan"</string>
<string name="allow" msgid="7225948811296386551">"Salli"</string>
<string name="deny" msgid="2081879885755434506">"Kiellä"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 347a3f2..b78b10e 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Impossible de renommer le document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Certains fichiers ont été convertis"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Accorder à <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accès au répertoire <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> sur <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Voulez-vous accorder l\'accès à vos données à <xliff:g id="APPNAME"><b>^1</b></xliff:g>, y compris vos photos et vos vidéos, sur <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne plus me demander"</string>
<string name="allow" msgid="7225948811296386551">"Autoriser"</string>
<string name="deny" msgid="2081879885755434506">"Refuser"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 6030bde..63e28bf 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Échec du changement de nom du document."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Certains fichiers ont été convertis"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Autoriser <xliff:g id="APPNAME"><b>^1</b></xliff:g> à accéder à l\'annuaire \"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>\" sur <xliff:g id="STORAGE"><i>^3</i></xliff:g> ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Autoriser <xliff:g id="APPNAME"><b>^1</b></xliff:g> à accéder à vos données, y compris les photos et les vidéos, sur <xliff:g id="STORAGE"><i>^2</i></xliff:g> ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne plus demander"</string>
<string name="allow" msgid="7225948811296386551">"Autoriser"</string>
<string name="deny" msgid="2081879885755434506">"Refuser"</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index 58181f8..c4a77aa 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Non se puido cambiar o nome do documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Convertéronse algúns ficheiros"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Queres outorgar acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> ao directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no almacenamento de <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Queres outorgar acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> aos teus datos, incluídas as fotos e os vídeos da <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Non preguntar de novo"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Rexeitar"</string>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 2cce5a3..6c2bb3e 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"દસ્તાવેજનું નામ બદલવામાં નિષ્ફળ થયાં"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"કેટલીક ફાઇલો રૂપાંતરિત કરી હતી"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ને <xliff:g id="STORAGE"><i>^3</i></xliff:g> પર <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> નિર્દેશિકાની ઍક્સેસ આપીએ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ને <xliff:g id="STORAGE"><i>^2</i></xliff:g> પર ફોટા અને વિડિઓઝ સહિત તમારા ડેટાની અૅક્સેસ આપીએ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ફરીથી પૂછશો નહીં"</string>
<string name="allow" msgid="7225948811296386551">"મંજૂરી આપો"</string>
<string name="deny" msgid="2081879885755434506">"નકારો"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 9f57f0b..9c27556 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"दस्तावेज़ का नाम बदलना विफल रहा"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"कुछ फ़ाइलें रूपांतरित हो गई थीं"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> को <xliff:g id="STORAGE"><i>^3</i></xliff:g> पर <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिका का एक्सेस दें?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> को <xliff:g id="STORAGE"><i>^2</i></xliff:g> पर मौजूद फ़ोटो और वीडियो सहित, अपने डेटा का एक्सेस प्रदान करें?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"फिर से ना पूछें"</string>
<string name="allow" msgid="7225948811296386551">"अनुमति दें"</string>
<string name="deny" msgid="2081879885755434506">"अस्वीकारें"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 0d42d5d..58e709b 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -118,6 +118,7 @@
<string name="rename_error" msgid="4203041674883412606">"Naziv dokumenta nije promijenjen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke su datoteke konvertirane"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Želite li aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> odobriti pristup direktoriju <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> na pohrani <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Želite li aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dopustiti pristup podacima, uključujući fotografije i videozapise na vanjskoj pohrani (<xliff:g id="STORAGE"><i>^2</i></xliff:g>)?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Više me ne pitaj"</string>
<string name="allow" msgid="7225948811296386551">"Dopusti"</string>
<string name="deny" msgid="2081879885755434506">"Odbij"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 37bfcfa..1936214 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Nem sikerült átnevezni a dokumentumot"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Egyes fájlokat konvertált a rendszer"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Hozzáférést biztosít a(z) <xliff:g id="APPNAME"><b>^1</b></xliff:g> számára a(z) <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> könyvtárhoz itt: <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Hozzáférést biztosít a(z) <xliff:g id="APPNAME"><b>^1</b></xliff:g> számára az Ön adataihoz, beleértve a következő tárhelyen található képekhez és videókhoz: <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne jelenjen meg többé"</string>
<string name="allow" msgid="7225948811296386551">"Engedélyezés"</string>
<string name="deny" msgid="2081879885755434506">"Elutasítás"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 22e44b0f..57af718 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Չհաջողվեց վերանվանել փաստաթուղթը"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Որոշ ֆայլեր փոխարկվել են"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> հավելվածին տրամադրե՞լ <xliff:g id="STORAGE"><i>^3</i></xliff:g>-ի <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> գրացուցակն օգտագործելու թույլտվություն:"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> հավելվածին տրամադրե՞լ <xliff:g id="STORAGE"><i>^2</i></xliff:g>-ում պահվող ձեր տվյալները, այդ թվում նաև լուսանկարները և տեսանյութերը, օգտագործելու թույլտվություն:"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Այլևս չհարցնել"</string>
<string name="allow" msgid="7225948811296386551">"Թույլատրել"</string>
<string name="deny" msgid="2081879885755434506">"Մերժել"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index f9aae9d..6621c9171 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Gagal mengganti nama dokumen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Beberapa file dikonversi"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses ke direktori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> di <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses ke data Anda, termasuk foto dan video, di <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Jangan tanya lagi"</string>
<string name="allow" msgid="7225948811296386551">"Izinkan"</string>
<string name="deny" msgid="2081879885755434506">"Tolak"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index a2a0d9b..4265606 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Ekki tókst að endurnefna skjalið"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sumum skrám var umbreytt"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Veita <xliff:g id="APPNAME"><b>^1</b></xliff:g> aðgang að skráasafninu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> á <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Veita <xliff:g id="APPNAME"><b>^1</b></xliff:g> aðgang að gögnunum þínum, þar á meðal myndum og myndskeiðum, á <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ekki spyrja aftur"</string>
<string name="allow" msgid="7225948811296386551">"Leyfa"</string>
<string name="deny" msgid="2081879885755434506">"Hafna"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index ddede45..7599180 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Ridenominazione documento non riuscita"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alcuni file sono stati convertiti"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Concedere all\'app <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accesso alla directory <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> su <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Concedere all\'app <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accesso ai tuoi dati, inclusi video e foto, sull\'unità <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Non chiedermelo più"</string>
<string name="allow" msgid="7225948811296386551">"Consenti"</string>
<string name="deny" msgid="2081879885755434506">"Nega"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index e2658c3..2e2020c 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"ניסיון שינוי שם המסמך נכשל"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"קבצים מסוימים הומרו"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"האם להעניק לאפליקציה <xliff:g id="APPNAME"><b>^1</b></xliff:g> גישה לספריה <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> באחסון <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"האם להעניק לאפליקציה <xliff:g id="APPNAME"><b>^1</b></xliff:g> גישה לנתונים שלך, כולל תמונות וסרטונים, השמורים ב<xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"אל תשאל שוב"</string>
<string name="allow" msgid="7225948811296386551">"אפשר"</string>
<string name="deny" msgid="2081879885755434506">"דחה"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 3cad318..1ba405f 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ドキュメントの名前を変更できませんでした"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"一部のファイルが変換されました"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"「<xliff:g id="STORAGE"><i>^3</i></xliff:g>」の「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」ディレクトリに「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」へのアクセスを許可しますか?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>の写真や動画などのデータへのアクセスを「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」に許可しますか?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"今後表示しない"</string>
<string name="allow" msgid="7225948811296386551">"許可"</string>
<string name="deny" msgid="2081879885755434506">"拒否"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index afd4198..c1624e3 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"დოკუმენტის გადარქმევა ვერ მოხერხდა"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ზოგიერთი ფაილი გარდაქმნილია"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"გსურთ, <xliff:g id="APPNAME"><b>^1</b></xliff:g> სარგებლობდეს <xliff:g id="STORAGE"><i>^3</i></xliff:g>-ის დირექტორიაზე „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“ წვდომის უფლებით?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"გსურთ, <xliff:g id="APPNAME"><b>^1</b></xliff:g> სარგებლობდეს <xliff:g id="STORAGE"><i>^2</i></xliff:g>-ზე არსებულ მონაცემებზე, მათ შორის, ფოტოებსა და ვიდეოებზე, წვდომის უფლებით?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"აღარ მკითხოთ"</string>
<string name="allow" msgid="7225948811296386551">"უფლების მიცემა"</string>
<string name="deny" msgid="2081879885755434506">"აკრძალვა"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index 21a8260..904ca5d 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Құжат қайта аталмады"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Кейбір файлдар түрлендірілді"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> қолданбасына <xliff:g id="STORAGE"><i>^3</i></xliff:g> қоймасындағы <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> каталогына өтуге рұқсат беру керек пе?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> <xliff:g id="STORAGE"><i>^2</i></xliff:g> қоймасындағы деректерге, соның ішінде, фотосуреттерге және бейнелерге қатынас беру керек пе?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Қайта сұралмасын"</string>
<string name="allow" msgid="7225948811296386551">"Рұқсат беру"</string>
<string name="deny" msgid="2081879885755434506">"Бас тарту"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 5a049add..ca0fa4c 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"បានបរាជ័យក្នុងការប្តូរឈ្មោះឯកសារ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ឯកសារមួយចំនួនត្រូវបានបម្លែង"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ផ្តល់សិទ្ធិឲ្យ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ចូលដំណើរការថត <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> នៅលើ <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ផ្តល់សិទ្ធិអនុញ្ញាតដល់ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ដើម្បីចូលដំណើរការទិន្នន័យរបស់អ្នក រាប់បញ្ចូលទាំងរូបថត និងវីដេអូ នៅលើ <xliff:g id="STORAGE"><i>^2</i></xliff:g> ឬទេ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"កុំសួរទៀត"</string>
<string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
<string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index 924b14a..6287274 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ಡಾಕ್ಯುಮೆಂಟ್ ಮರುಹೆಸರಿಸಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ಕೆಲವು ಫೈಲ್ಗಳನ್ನು ಪರಿವರ್ತಿಸಲಾಗಿದೆ"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> ರಲ್ಲಿ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ಡೈರೆಕ್ಟರಿಗೆ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಪ್ರವೇಶ ನೀಡುವುದೇ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> ಸಂಗ್ರಹಣೆಯಲ್ಲಿನ ಪೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುವುದೇ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ಮತ್ತೆ ಕೇಳಬೇಡಿ"</string>
<string name="allow" msgid="7225948811296386551">"ಅನುಮತಿಸು"</string>
<string name="deny" msgid="2081879885755434506">"ನಿರಾಕರಿಸು"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index df0cbf2..1b553b5 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"문서 이름을 변경하지 못했습니다."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"일부 파일이 변환되었습니다."</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>이(가) <xliff:g id="STORAGE"><i>^3</i></xliff:g>에서 <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> 디렉토리에 액세스하도록 허용하시겠습니까?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>에서 사진, 동영상 등 <xliff:g id="STORAGE"><i>^2</i></xliff:g>의 내 데이터에 액세스하도록 허용하시겠습니까?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"다시 묻지 않음"</string>
<string name="allow" msgid="7225948811296386551">"허용"</string>
<string name="deny" msgid="2081879885755434506">"거부"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index 84bc37f..f4981b9 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Документтин аталышы өзгөртүлбөй калды"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Айрым файлдардын форматы өзгөртүлдү"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> колдонмосуна <xliff:g id="STORAGE"><i>^3</i></xliff:g> түзмөгүндөгү <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> папканы пайдалануу мүмкүнчүлүгү берилсинби?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> колдонмосуна <xliff:g id="STORAGE"><i>^2</i></xliff:g> түзмөгүндөгү дайындарыңыз, сүрөттөрүңүз жана видеолоруңузду пайдалануу мүмкүнчүлүгү берилсинби?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Экинчи суралбасын"</string>
<string name="allow" msgid="7225948811296386551">"Уруксат берүү"</string>
<string name="deny" msgid="2081879885755434506">"Жок"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 4c891aa..4203241 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ປ່ຽນຊື່ເອກະສານບໍ່ສຳເລັດ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ປ່ຽນແປງບາງໄຟລ໌ແລ້ວ"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ອະນຸຍາດສິດເຂົ້າເຖິງໃຫ້ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ເພື່ອເຂົ້າໄດເຣກທໍຣີ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ຢູ່ <xliff:g id="STORAGE"><i>^3</i></xliff:g> ບໍ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ອະນຸມັດໃຫ້ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ເຂົ້າເຖິງຂໍ້ມູນຂອງທ່ານ ເຊິ່ງຮວມເຖິງຮູບພາບ ແລະ ວິດີໂອໃນ <xliff:g id="STORAGE"><i>^2</i></xliff:g> ໄດ້ບໍ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ບໍ່ຕ້ອງຖາມຄືນ"</string>
<string name="allow" msgid="7225948811296386551">"ອະນຸຍາດ"</string>
<string name="deny" msgid="2081879885755434506">"ປະຕິເສດ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 8aec2d4..5114854 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Nepavyko pervardyti dokumento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Kai kurie failai buvo konvertuoti"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Suteikti „<xliff:g id="APPNAME"><b>^1</b></xliff:g>“ prieigą prie katalogo „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“ <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Suteikti programai „<xliff:g id="APPNAME"><b>^1</b></xliff:g>“ prieigą prie duomenų, įskaitant nuotraukas ir vaizdo įrašus, <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Daugiau neklausti"</string>
<string name="allow" msgid="7225948811296386551">"Leisti"</string>
<string name="deny" msgid="2081879885755434506">"Atmesti"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index be2f0b7..8ee161d 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -118,6 +118,7 @@
<string name="rename_error" msgid="4203041674883412606">"Neizdevās pārdēvēt dokumentu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Daži faili tika pārveidoti."</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vai atļaut lietotnei <xliff:g id="APPNAME"><b>^1</b></xliff:g> piekļūt direktorijam <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> šajā krātuvē: <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vai atļaut lietotnei <xliff:g id="APPNAME"><b>^1</b></xliff:g> piekļūt jūsu datiem, tostarp fotoattēliem un videoklipiem, šajā krātuvē: <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Turpmāk vairs nejautāt"</string>
<string name="allow" msgid="7225948811296386551">"Atļaut"</string>
<string name="deny" msgid="2081879885755434506">"Noraidīt"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index e9c7f3f..5fb64f1 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Не успеа да се преименува документот"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Некои датотеки беа конвертирани"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Овозможете пристап на <xliff:g id="APPNAME"><b>^1</b></xliff:g> до директориумот <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> на <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Да се овозможи пристап на <xliff:g id="APPNAME"><b>^1</b></xliff:g> до вашите податоци, вклучувајќи фотографии и видеа, на <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Не прашувај повторно"</string>
<string name="allow" msgid="7225948811296386551">"Дозволи"</string>
<string name="deny" msgid="2081879885755434506">"Одбиј"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index e71e62a..afb26a0 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ഡോക്യുമെന്റിന്റെ പേരുമാറ്റുന്നത് പരാജയപ്പെട്ടു"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ചില ഫയലുകൾ പരിവർത്തനം ചെയ്യപ്പെട്ടു"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> സ്റ്റോറേജിലെ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> എന്ന ഡയറക്റ്ററിയിലേക്ക് <xliff:g id="APPNAME"><b>^1</b></xliff:g> ആപ്പിന് ആക്സസ് അനുവദിക്കണോ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> സ്റ്റോറേജിലെ ഫോട്ടോകളും വീഡിയോകളും ഉൾപ്പെടെ, നിങ്ങളുടെ ഡാറ്റയിലേക്ക് <xliff:g id="APPNAME"><b>^1</b></xliff:g> ആപ്പിന് ആക്സസ്സ് അനുവദിക്കണോ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"വീണ്ടും ആവശ്യപ്പെടരുത്"</string>
<string name="allow" msgid="7225948811296386551">"അനുവദിക്കുക"</string>
<string name="deny" msgid="2081879885755434506">"നിരസിക്കുക"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 64cc2a8..2890908 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Баримт бичгийн нэрийн өөрчилж чадсангүй"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Зарим файлыг хөрвүүлсэн"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g>-д байгаа <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> лавлагаанд хандахыг <xliff:g id="APPNAME"><b>^1</b></xliff:g>-д зөвшөөрөх үү?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>-д байгаа зураг, видео гэх мэт таны өгөгдөлд <xliff:g id="APPNAME"><b>^1</b></xliff:g> хандахыг зөвшөөрөх үү?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Дахин бүү асуу"</string>
<string name="allow" msgid="7225948811296386551">"Зөвшөөрөх"</string>
<string name="deny" msgid="2081879885755434506">"Татгалзах"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index 415eda8..e850b26 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"दस्तऐवज पुनर्नामित करण्यात अयशस्वी झाले"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"काही फायली रूपांतरित केल्या होत्या"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> वर <xliff:g id="APPNAME"><b>^1</b></xliff:g> ला <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिकेवर प्रवेशाची मंजूरी द्यायची?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ला <xliff:g id="STORAGE"><i>^2</i></xliff:g> वर फोटो आणि व्हिडिओंसह, आपल्या डेटामध्ये प्रवेश करण्याची मंजूरी द्यायची?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"पुन्हा विचारू नका"</string>
<string name="allow" msgid="7225948811296386551">"अनुमती द्या"</string>
<string name="deny" msgid="2081879885755434506">"नकार द्या"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index c5f4771..374cbb0 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Gagal menamakan semula dokumen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sesetengah fail telah ditukarkan"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses kepada direktori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> di <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses kepada data anda, termasuk foto dan video pada <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Jangan tanya lagi"</string>
<string name="allow" msgid="7225948811296386551">"Benarkan"</string>
<string name="deny" msgid="2081879885755434506">"Nafi"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index bdfd1a2..80afcfb 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"စာရွက်စာတမ်းကို အမည်ပြောင်းခြင်း မအောင်မြင်ပါ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"အချို့ဖိုင်များကို ပြောင်းလဲထားသည်"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ကို <xliff:g id="STORAGE"><i>^3</i></xliff:g> ရှိ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> လမ်းညွှန်အား အသုံးပြုခွင့်ပေးမလား။"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> ရှိဓာတ်ပုံများနှင့် ဗီဒီယိုများအပါအဝင် သင့်ဒေတာများကို <xliff:g id="APPNAME"><b>^1</b></xliff:g> အားအသုံးပြုခွင့်ပေးမလား။"</string>
<string name="never_ask_again" msgid="4295278542972859268">"နောက်ထပ်မမေးပါနှင့်"</string>
<string name="allow" msgid="7225948811296386551">"ခွင့်ပြုသည်"</string>
<string name="deny" msgid="2081879885755434506">"ငြင်းပယ်သည်"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 5922d1c..8349a13 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Kunne ikke gi dokumentet nytt navn"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Noen filer er konvertert"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vil du gi <xliff:g id="APPNAME"><b>^1</b></xliff:g> tilgang til <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>-katalogen på <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vil du gi <xliff:g id="APPNAME"><b>^1</b></xliff:g> tilgang til dataene dine – inkludert bilder og videoer – på <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ikke spør igjen"</string>
<string name="allow" msgid="7225948811296386551">"Tillat"</string>
<string name="deny" msgid="2081879885755434506">"Avslå"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index 4bc814b..a110742 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"कागजात पुन: नामाकरण गर्न असफल भयो"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"केही फाइलहरू परिवर्तन गरिएका थिए"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> लाई <xliff:g id="STORAGE"><i>^3</i></xliff:g> मा भएको <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिकामा पहुँच गर्न अनुमति दिने हो?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> लाई <xliff:g id="STORAGE"><i>^2</i></xliff:g> मा भएका तस्बिर र भिडियोहरू लगायत तपाईँको डेटामा पहुँच गर्नका लागि अनुमति दिने हो?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"फेरि नसोध्नुहोस्"</string>
<string name="allow" msgid="7225948811296386551">"अनुमति दिनुहोस्"</string>
<string name="deny" msgid="2081879885755434506">"अस्वीकार गर्नुहोस्"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index f1b3ed7..27796cf 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Kan naam van document niet wijzigen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sommige bestanden zijn geconverteerd"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang verlenen tot de map <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> op <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang verlenen tot je gegevens, waaronder foto\'s en video\'s, op <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Niet meer vragen"</string>
<string name="allow" msgid="7225948811296386551">"Toestaan"</string>
<string name="deny" msgid="2081879885755434506">"Weigeren"</string>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index f9f9412..503bc0d 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ਦਸਤਾਵੇਜ਼ ਦਾ ਮੁੜ-ਨਾਮਕਰਨ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ਕੁਝ ਫ਼ਾਈਲਾਂ ਤਬਦੀਲ ਕੀਤੀਆਂ ਗਈਆਂ ਸਨ"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ਕੀ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ਨੂੰ <xliff:g id="STORAGE"><i>^3</i></xliff:g> \'ਤੇ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ਡਾਇਰੈਕਟਰੀ \'ਤੇ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ਕੀ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ਨੂੰ <xliff:g id="STORAGE"><i>^2</i></xliff:g> \'ਤੇ ਫੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਸਮੇਤ, ਤੁਹਾਡੇ ਡੈਟੇ \'ਤੇ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
<string name="allow" msgid="7225948811296386551">"ਆਗਿਆ ਦਿਓ"</string>
<string name="deny" msgid="2081879885755434506">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 7a35bc2..7f0b274 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Nie udało się zmienić nazwy dokumentu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Niektóre pliki zostały przekonwertowane"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Zezwolić aplikacji <xliff:g id="APPNAME"><b>^1</b></xliff:g> na dostęp do katalogu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> w pamięci masowej <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Zezwolić aplikacji <xliff:g id="APPNAME"><b>^1</b></xliff:g> na dostęp do Twoich danych, w tym zdjęć i filmów, zapisanych w pamięci <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nie pytaj ponownie"</string>
<string name="allow" msgid="7225948811296386551">"Zezwól"</string>
<string name="deny" msgid="2081879885755434506">"Odmów"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index b765cd0..d34b7b6 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Falha ao renomear documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns arquivos foram convertidos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Conceder ao <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no/na <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Não perguntar novamente"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Negar"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index 1edd1c0..7c82410 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Falha ao mudar o nome do documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns ficheiros foram convertidos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no(a) <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no(a) <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Não perguntar novamente"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Recusar"</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index b765cd0..d34b7b6 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Falha ao renomear documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns arquivos foram convertidos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Conceder ao <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no/na <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Não perguntar novamente"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Negar"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 10f6aee..54458f1 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -118,6 +118,7 @@
<string name="rename_error" msgid="4203041674883412606">"Documentul nu a putut fi redenumit"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Unele fișiere au fost convertite"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Permiteți aplicației <xliff:g id="APPNAME"><b>^1</b></xliff:g> accesul la directorul <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> de pe <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Permiteți aplicației <xliff:g id="APPNAME"><b>^1</b></xliff:g> să vă acceseze datele, inclusiv fotografiile și videoclipurile, de pe <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nu mai întreba"</string>
<string name="allow" msgid="7225948811296386551">"Permiteți"</string>
<string name="deny" msgid="2081879885755434506">"Refuzați"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index d9f40097..2eda9ec 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Не удалось переименовать документ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Формат некоторых файлов изменен"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Открыть приложению \"<xliff:g id="APPNAME"><b>^1</b></xliff:g>\" доступ к папке \"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>\" на устройстве \"<xliff:g id="STORAGE"><i>^3</i></xliff:g>\"?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Открыть приложению \"<xliff:g id="APPNAME"><b>^1</b></xliff:g>\" доступ к вашим данным, включая фото и видео, на носителе: <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Больше не спрашивать"</string>
<string name="allow" msgid="7225948811296386551">"Разрешить"</string>
<string name="deny" msgid="2081879885755434506">"Отклонить"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index 41ee2260..1dc9a2b 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ලේඛනය යළි නම් කිරීම අසාර්ථක විය"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"සමහර ගොනු පරිවර්තනය කරන ලදී"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> හට <xliff:g id="STORAGE"><i>^3</i></xliff:g> මත <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> නාමාවලිය වෙත ප්රවේශය දෙන්නද?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> හි, ඡායාරූප සහ වීඩියෝ ඇතුළුව, ඔබේ දත්තවලට <xliff:g id="APPNAME"><b>^1</b></xliff:g> හට ප්රවේශය ලබා දෙන්නද?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"නැවත අසන්න එපා"</string>
<string name="allow" msgid="7225948811296386551">"අවසර දෙන්න"</string>
<string name="deny" msgid="2081879885755434506">"ප්රතික්ෂේප කරන්න"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 518093c..da91574 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Premenovanie dokumentu zlyhalo"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Niektoré súbory boli konvertované"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Udeliť aplikácii <xliff:g id="APPNAME"><b>^1</b></xliff:g> prístup k adresáru <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> v úložisku <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Chcete aplikácii <xliff:g id="APPNAME"><b>^1</b></xliff:g> udeliť prístup k dátam (vrátane fotiek a videí) v úložisku <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nabudúce sa nepýtať"</string>
<string name="allow" msgid="7225948811296386551">"Povoliť"</string>
<string name="deny" msgid="2081879885755434506">"Zamietnuť"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index 39cfed7..9a7b477 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumenta ni bilo mogoče preimenovati"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Nekatere datoteke so bile pretvorjene"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Želite aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dovoliti dostop do imenika <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> v shrambi <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Odobrite aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dostop do podatkov, vključno s fotografijami in videoposnetki, v shrambi <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne sprašuj več"</string>
<string name="allow" msgid="7225948811296386551">"Dovoli"</string>
<string name="deny" msgid="2081879885755434506">"Zavrni"</string>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index f5ebbbc..6b41c60 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Riemërtimi i dokumentit dështoi"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Disa skedarë u konvertuan"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Jepi aplikacionit <xliff:g id="APPNAME"><b>^1</b></xliff:g> qasje te direktoria <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> në <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"T\'i jepet aplikacionit <xliff:g id="APPNAME"><b>^1</b></xliff:g> qasje te të dhënat, duke përfshirë fotografitë dhe videot, në <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Mos pyet përsëri"</string>
<string name="allow" msgid="7225948811296386551">"Lejo"</string>
<string name="deny" msgid="2081879885755434506">"Moho"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index 6a6488e..b6ccf45 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -118,6 +118,7 @@
<string name="rename_error" msgid="4203041674883412606">"Преименовање документа није успело"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Неке датотеке су конвертоване"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Желите ли да апликацији <xliff:g id="APPNAME"><b>^1</b></xliff:g> одобрите приступ директоријуму <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> на меморијском простору <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Желите да ли да дозволите да апликација <xliff:g id="APPNAME"><b>^1</b></xliff:g> приступа подацима, укључујући слике и видео снимке, на локацији <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Не питај поново"</string>
<string name="allow" msgid="7225948811296386551">"Дозволи"</string>
<string name="deny" msgid="2081879885755434506">"Одбиј"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 8b55b22..67bbfdf 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Det gick inte att byta namn på dokumentet"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Vissa filer konverterades"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vill du ge <xliff:g id="APPNAME"><b>^1</b></xliff:g> åtkomst till katalogen <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> på <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vill du ge <xliff:g id="APPNAME"><b>^1</b></xliff:g> åtkomst till din data (inklusive foton och videor) på <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Fråga inte igen"</string>
<string name="allow" msgid="7225948811296386551">"Tillåt"</string>
<string name="deny" msgid="2081879885755434506">"Neka"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index 482c7ec..168b63a 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Imeshindwa kubadilisha jina la hati"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Baadhi ya faili zimebadilishwa muundo"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Ungependa kuruhusu <xliff:g id="APPNAME"><b>^1</b></xliff:g> ifikie saraka ya <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> kwenye <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Ungependa kuruhusu <xliff:g id="APPNAME"><b>^1</b></xliff:g> ifikie data yako, ikiwa ni pamoja na picha na video kwenye <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Usiniulize tena"</string>
<string name="allow" msgid="7225948811296386551">"Ruhusu"</string>
<string name="deny" msgid="2081879885755434506">"Kataza"</string>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index b32c324..43543c6 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ஆவணத்திற்கு மறுபெயரிடுவதில் தோல்வி"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"சில கோப்புகள் மாற்றப்பட்டன"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> இல் உள்ள <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> கோப்பகத்தை அணுக <xliff:g id="APPNAME"><b>^1</b></xliff:g>ஐ அனுமதிக்கவா?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> இல் உள்ள படங்கள், வீடியோக்கள் உட்பட எல்லா தரவையும் அணுக, <xliff:g id="APPNAME"><b>^1</b></xliff:g>ஐ அனுமதிக்கவா?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"மீண்டும் கேட்காதே"</string>
<string name="allow" msgid="7225948811296386551">"அனுமதி"</string>
<string name="deny" msgid="2081879885755434506">"நிராகரி"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index e77fd66..47d2c48 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"పత్రం పేరు మార్చడంలో విఫలమైంది"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"కొన్ని పైల్లు మార్చబడ్డాయి"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>కి <xliff:g id="STORAGE"><i>^3</i></xliff:g>లో <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> డైరెక్టరీ ప్రాప్యతను మంజూరు చేయాలా?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>లో ఫోటోలు మరియు వీడియోలతో సహా మీ డేటా ప్రాప్యతను <xliff:g id="APPNAME"><b>^1</b></xliff:g>కి మంజూరు చేయాలా?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"మళ్లీ అడగవద్దు"</string>
<string name="allow" msgid="7225948811296386551">"అనుమతించండి"</string>
<string name="deny" msgid="2081879885755434506">"తిరస్కరించండి"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 4d94795..99009b4 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"ไม่สามารถเปลี่ยนชื่อเอกสาร"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"แปลงบางไฟล์แล้ว"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ให้สิทธิ์ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ในการเข้าถึงไดเรกทอรี <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ใน <xliff:g id="STORAGE"><i>^3</i></xliff:g> ไหม"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ให้สิทธิ์ <xliff:g id="APPNAME"><b>^1</b></xliff:g> เข้าถึงข้อมูลของคุณ รวมถึงรูปภาพและวิดีโอใน <xliff:g id="STORAGE"><i>^2</i></xliff:g> ไหม"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ไม่ต้องถามอีก"</string>
<string name="allow" msgid="7225948811296386551">"อนุญาต"</string>
<string name="deny" msgid="2081879885755434506">"ปฏิเสธ"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index f395a5b..dd08e13 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Hindi napalitan ang pangalan ng dokumento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Na-convert ang ilang file"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Bigyan ang <xliff:g id="APPNAME"><b>^1</b></xliff:g> ng access sa directory ng <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> sa <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Bigyan ang <xliff:g id="APPNAME"><b>^1</b></xliff:g> ng access sa iyong data, kabilang ang mga larawan at video, sa <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Huwag nang tatanunging muli"</string>
<string name="allow" msgid="7225948811296386551">"Payagan"</string>
<string name="deny" msgid="2081879885755434506">"Tanggihan"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index bc4db0d..3cc53e1 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Dokümanın adı değiştirilemedi"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bazı dosyalar dönüştürüldü"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> uygulamasına <xliff:g id="STORAGE"><i>^3</i></xliff:g> depolama alanındaki <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> dizinine erişim izni verilsin mi?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> uygulamasının, fotoğraflar ve videolar dahil olmak üzere <xliff:g id="STORAGE"><i>^2</i></xliff:g> üzerindeki verilerinize erişmesine izin verilsin mi?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Tekrar sorma"</string>
<string name="allow" msgid="7225948811296386551">"İzin Ver"</string>
<string name="deny" msgid="2081879885755434506">"Reddet"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 79b6fbf..f9620a0 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -125,6 +125,7 @@
<string name="rename_error" msgid="4203041674883412606">"Не вдалося перейменувати документ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Деякі файли конвертовано"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Надати додатку <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ до каталогу <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> на пристрої пам’яті <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Надати додатку <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ до ваших даних, зокрема до фотографій і відео, які містить <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Не запитувати знову"</string>
<string name="allow" msgid="7225948811296386551">"Дозвол."</string>
<string name="deny" msgid="2081879885755434506">"Забор."</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 4498f87..76d4cee 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"دستاویز کا نام تبدیل کرنے میں ناکام"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"کچھ فائلوں کو تبدیل کیا گیا تھا"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> کو <xliff:g id="STORAGE"><i>^3</i></xliff:g> پر <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ڈائرکٹری تک رسائی عطا کریں؟"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> کو اپنے ڈیٹا بشمول <xliff:g id="STORAGE"><i>^2</i></xliff:g> پر موجود تصاویر اور ویڈیوز تک رسائی عطا کریں؟"</string>
<string name="never_ask_again" msgid="4295278542972859268">"دوبارہ نہ پوچھیں"</string>
<string name="allow" msgid="7225948811296386551">"اجازت دیں"</string>
<string name="deny" msgid="2081879885755434506">"مسترد کریں"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index b4f435b..47f1ac1 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Hujjatni qayta nomlab bo‘lmadi"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bir nechta fayllar o‘girildi"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ilovasining <xliff:g id="STORAGE"><i>^3</i></xliff:g> xotirasidagi “<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>” jildiga kirishiga ruxsat berilsinmi?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ilovasining <xliff:g id="STORAGE"><i>^2</i></xliff:g> xotirasidagi ma’lumotlardan, jumladan, rasmlar va videolardan foydalanishiga ruxsat berilsinmi?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Boshqa so‘ralmasin"</string>
<string name="allow" msgid="7225948811296386551">"Ruxsat berish"</string>
<string name="deny" msgid="2081879885755434506">"Rad qilish"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index b64026a..613f271 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Không đổi được tên tài liệu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Đã chuyển đổi một số tệp"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Cấp cho <xliff:g id="APPNAME"><b>^1</b></xliff:g> quyền truy cập vào thư mục <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> trong <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Cấp cho <xliff:g id="APPNAME"><b>^1</b></xliff:g> quyền truy cập vào dữ liệu của bạn, kể cả ảnh và video trên <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Không hỏi lại"</string>
<string name="allow" msgid="7225948811296386551">"Cho phép"</string>
<string name="deny" msgid="2081879885755434506">"Từ chối"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index 90d1619..ea5af3d 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"无法重命名文档"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分文件已转换成其他格式"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"要授权<xliff:g id="APPNAME"><b>^1</b></xliff:g>访问 <xliff:g id="STORAGE"><i>^3</i></xliff:g>上的“<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>”目录吗?"</string>
+ <!-- no translation found for open_external_dialog_root_request (8899108702926347720) -->
+ <skip />
<string name="never_ask_again" msgid="4295278542972859268">"不再询问"</string>
<string name="allow" msgid="7225948811296386551">"允许"</string>
<string name="deny" msgid="2081879885755434506">"拒绝"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index aeda260..7587086 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"無法重新命名文件"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分檔案已轉換成其他格式"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"要為「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」開放 <xliff:g id="STORAGE"><i>^3</i></xliff:g>上的「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」目錄存取權嗎?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"要向「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」開放 <xliff:g id="STORAGE"><i>^2</i></xliff:g>上的相片和影片等資料的存取權嗎?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"不要再詢問"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index dc76411..7f13098 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"無法重新命名文件"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分檔案已轉換成其他格式"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"要允許<xliff:g id="APPNAME"><b>^1</b></xliff:g>存取 <xliff:g id="STORAGE"><i>^3</i></xliff:g>上的「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」目錄嗎?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"要允許「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」存取 <xliff:g id="STORAGE"><i>^2</i></xliff:g>上的資料 (包括相片和影片) 嗎?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"不要再詢問"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index c53031b..6453059 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -111,6 +111,7 @@
<string name="rename_error" msgid="4203041674883412606">"Yehlulekile ukuqamba kabusha idokhumenti"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Amanye amafayela aguqulelwe"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Nika i-<xliff:g id="APPNAME"><b>^1</b></xliff:g> ukufinyelela ekuqondiseni kwe-<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ku-<xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Nikeza i-<xliff:g id="APPNAME"><b>^1</b></xliff:g> ukufinyelela kudatha yakho, okufaka izithombe namavidiyo, ku-<xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ungaphindi ubuze"</string>
<string name="allow" msgid="7225948811296386551">"Vumela"</string>
<string name="deny" msgid="2081879885755434506">"Yala"</string>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 04b7fee..51e04b6 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -16,6 +16,7 @@
<resources>
<color name="material_grey_400">#ffbdbdbd</color>
+ <color name="material_teal_700">#ff00796b</color>
<!-- This is the window background, but also the background for anything
else that needs to manually declare a background matching the "default"
@@ -36,6 +37,8 @@
<color name="item_doc_background_disabled">#fff4f4f4</color>
+ <color name="root_activated_color">@color/material_teal_700</color>
+
<!-- TODO: Would be nice to move this to a color-set, but not sure how to support animation -->
<color name="item_doc_background">#fffafafa</color>
<color name="item_doc_background_selected">#ffe0f2f1</color>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index a548d89..b16554c 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -36,6 +36,8 @@
<item name="android:windowActionBar">false</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:windowNoTitle">true</item>
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="android:fitsSystemWindows">false</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
</style>
@@ -43,7 +45,7 @@
<style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
<item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
<item name="android:minHeight">3dp</item>
- <item name="android:maxHeight">3dp</item>
+ <item name="android:maxHeight">3dp</item>
</style>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 2911027..54202d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -58,6 +58,7 @@
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;
@@ -79,6 +80,7 @@
private int mLayoutId;
private boolean mNavDrawerHasFocus;
+ private long mStartTime;
public abstract void onDocumentPicked(DocumentInfo doc, Model model);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -96,16 +98,14 @@
@CallSuper
@Override
public void onCreate(Bundle icicle) {
+ // Record the time when onCreate is invoked for metric.
+ mStartTime = new Date().getTime();
+
super.onCreate(icicle);
final Intent intent = getIntent();
- // If startup benchmark is requested by a whitelisted testing package, then close the
- // activity once idle, and notify the testing activity.
- if (intent.getBooleanExtra(EXTRA_BENCHMARK, false) &&
- BENCHMARK_TESTING_PACKAGE.equals(getCallingPackage())) {
- closeOnIdleForTesting();
- }
+ addListenerForLaunchCompletion();
setContentView(mLayoutId);
@@ -127,6 +127,7 @@
mSearchManager = new SearchViewManager(this, icicle);
DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
+ Display.adjustToolbar(toolbar, this);
setActionBar(toolbar);
mNavigator = new NavigationView(
mDrawer,
@@ -604,12 +605,10 @@
return super.onKeyDown(keyCode, event);
}
- @VisibleForTesting
public void addEventListener(EventListener listener) {
mEventListeners.add(listener);
}
- @VisibleForTesting
public void removeEventListener(EventListener listener) {
mEventListeners.remove(listener);
}
@@ -673,6 +672,44 @@
return false;
}
+ /**
+ * Closes the activity when it's idle.
+ */
+ private void addListenerForLaunchCompletion() {
+ addEventListener(new EventListener() {
+ @Override
+ public void onDirectoryNavigated(Uri uri) {
+ }
+
+ @Override
+ public void onDirectoryLoaded(Uri uri) {
+ removeEventListener(this);
+ getMainLooper().getQueue().addIdleHandler(new IdleHandler() {
+ @Override
+ public boolean queueIdle() {
+ // If startup benchmark is requested by a whitelisted testing package, then
+ // close the activity once idle, and notify the testing activity.
+ if (getIntent().getBooleanExtra(EXTRA_BENCHMARK, false) &&
+ BENCHMARK_TESTING_PACKAGE.equals(getCallingPackage())) {
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ Metrics.logStartupMs(
+ BaseActivity.this, (int) (new Date().getTime() - mStartTime));
+
+ // Remove the idle handler.
+ return false;
+ }
+ });
+ new Handler().post(new Runnable() {
+ @Override public void run() {
+ }
+ });
+ }
+ });
+ }
+
private static final class PickRootTask extends PairedTask<BaseActivity, Void, DocumentInfo> {
private RootInfo mRoot;
@@ -694,33 +731,6 @@
}
}
- /**
- * Closes the activity when it's idle. Used only for tests.
- */
- private void closeOnIdleForTesting() {
- addEventListener(new EventListener() {
- @Override
- public void onDirectoryNavigated(Uri uri) {
- }
-
- @Override
- public void onDirectoryLoaded(Uri uri) {
- getMainLooper().getQueue().addIdleHandler(new IdleHandler() {
- @Override
- public boolean queueIdle() {
- setResult(RESULT_OK);
- finish();
- return false;
- }
- });
- new Handler().post(new Runnable() {
- @Override public void run() {
- }
- });
- }
- });
- }
-
private static final class HandleRootsChangedTask
extends PairedTask<BaseActivity, RootInfo, RootInfo> {
DocumentInfo mDownloadsDocument;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Display.java b/packages/DocumentsUI/src/com/android/documentsui/Display.java
index bae2d58..d46a3ea 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Display.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Display.java
@@ -20,13 +20,15 @@
import android.content.Context;
import android.graphics.Point;
import android.util.TypedValue;
+import android.view.WindowManager;
+import android.widget.Toolbar;
/*
* Convenience class for getting display related attributes
*/
public final class Display {
/*
- * Returns the screen width in pixels.
+ * Returns the screen width in raw pixels.
*/
public static float screenWidth(Activity activity) {
Point size = new Point();
@@ -42,15 +44,44 @@
}
/*
- * Returns action bar height in pixels.
+ * Returns action bar height in raw pixels.
*/
public static float actionBarHeight(Context context) {
- int actionBarHeight = 0;
+ int height = 0;
TypedValue tv = new TypedValue();
if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
- actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
+ height = TypedValue.complexToDimensionPixelSize(tv.data,
context.getResources().getDisplayMetrics());
}
- return actionBarHeight;
+ return height;
+ }
+
+ /*
+ * Returns status bar height in raw pixels.
+ */
+ private static int statusBarHeight(Context context) {
+ int height = 0;
+ int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen",
+ "android");
+ if (resourceId > 0) {
+ height = context.getResources().getDimensionPixelSize(resourceId);
+ }
+ return height;
+ }
+
+ /*
+ * Adjusts toolbar for the layout with translucent status bar. Increases the
+ * height of the toolbar and adds padding at the top to accommodate status bar visible above
+ * toolbar.
+ */
+ public static void adjustToolbar(Toolbar toolbar, Activity activity) {
+ if ((activity.getWindow().getAttributes().flags
+ & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) != 0) {
+ int statusBarHeight = Display.statusBarHeight(activity);
+ toolbar.getLayoutParams().height = (int) (Display.actionBarHeight(activity)
+ + statusBarHeight);
+ toolbar.setPadding(toolbar.getPaddingLeft(), statusBarHeight, toolbar.getPaddingRight(),
+ toolbar.getPaddingBottom());
+ }
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index 020f2c0..2dbb730 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -55,7 +55,7 @@
View drawer = activity.findViewById(R.id.drawer_roots);
Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
-
+ Display.adjustToolbar(toolbar, activity);
drawer.getLayoutParams().width = calculateDrawerWidth(activity);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 2af6c46..bed8b29 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -381,7 +381,7 @@
}
// Open the Close drawer if it is closed and we're at the top of a root.
- if (size == 1) {
+ if (size <= 1) {
mDrawer.setOpen(true);
// Remember so we don't just close it again if back is pressed again.
mDrawerLastFiddled = System.currentTimeMillis();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index 929d1e0..e6b22e6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -64,6 +64,7 @@
private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
+ private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
// Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
// root that is not explicitly recognized by the Metrics code (see {@link
@@ -347,7 +348,7 @@
}
/**
- * Log the cancellation of a file operation. Call this when a Job is canceled.
+ * Logs the cancellation of a file operation. Call this when a Job is canceled.
* @param context
* @param operationType
*/
@@ -355,6 +356,15 @@
logHistogram(context, COUNT_FILEOP_CANCELED, toMetricsOpType(operationType));
}
+ /**
+ * Logs startup time in milliseconds.
+ * @param context
+ * @param startupMs Startup time in milliseconds.
+ */
+ public static void logStartupMs(Context context, int startupMs) {
+ logHistogram(context, COUNT_STARTUP_MS, startupMs);
+ }
+
private static void logInterProviderFileOps(
Context context,
String histogram,
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index b2fd9c4..4acf85e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -527,9 +527,13 @@
// Re-enable TalkBack for the toolbars, as they are no longer covered by action mode.
final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
- final Toolbar rootsToolbar = (Toolbar) getActivity().findViewById(R.id.roots_toolbar);
toolbar.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- rootsToolbar.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+
+ // This toolbar is not present in the fixed_layout
+ final Toolbar rootsToolbar = (Toolbar) getActivity().findViewById(R.id.roots_toolbar);
+ if (rootsToolbar != null) {
+ rootsToolbar.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
}
@Override
@@ -544,13 +548,16 @@
// Hide the toolbars if action mode is enabled, so TalkBack doesn't navigate to
// these controls when using linear navigation.
final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
- final Toolbar rootsToolbar = (Toolbar) getActivity().findViewById(
- R.id.roots_toolbar);
toolbar.setImportantForAccessibility(
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- rootsToolbar.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ // This toolbar is not present in the fixed_layout
+ final Toolbar rootsToolbar = (Toolbar) getActivity().findViewById(
+ R.id.roots_toolbar);
+ if (rootsToolbar != null) {
+ rootsToolbar.setImportantForAccessibility(
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
return true;
}
@@ -1042,7 +1049,7 @@
}
// Can't copy folders to downloads, because we don't show folders there.
- if (!root.isDownloads()) {
+ if (root.isDownloads()) {
for (DocumentInfo docs : files) {
if (docs.isDirectory()) {
return false;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
index b53e165..748da00 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
@@ -158,11 +158,11 @@
}
/**
- * Starts the service for a move operation.
+ * Starts the service for a delete operation.
*
* @param jobId A unique jobid for this job.
* Use {@link #createJobId} if you don't have one handy.
- * @param srcDocs A list of src files to copy.
+ * @param srcDocs A list of src files to delete.
* @param srcParent Parent of all the source documents.
* @return Id of the job.
*/
@@ -184,7 +184,7 @@
*
* @param jobId A unique jobid for this job.
* Use {@link #createJobId} if you don't have one handy.
- * @param srcDocs A list of src files to copy.
+ * @param srcDocs A list of src files for an operation.
* @return Id of the job.
*/
public static Intent createBaseIntent(
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
index 3536593..b816287 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
@@ -21,6 +21,7 @@
import android.content.ContextWrapper;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.MergeCursor;
import android.provider.DocumentsContract.Document;
import android.test.AndroidTestCase;
import android.test.mock.MockContentResolver;
@@ -117,21 +118,25 @@
// Tests multiple authorities with clashing document IDs.
public void testModelIdIsUnique() {
- MatrixCursor cIn = new MatrixCursor(COLUMNS);
+ MatrixCursor cIn1 = new MatrixCursor(COLUMNS);
+ MatrixCursor cIn2 = new MatrixCursor(COLUMNS);
// Make two sets of items with the same IDs, under different authorities.
final String AUTHORITY0 = "auth0";
final String AUTHORITY1 = "auth1";
+
for (int i = 0; i < ITEM_COUNT; ++i) {
- MatrixCursor.RowBuilder row0 = cIn.newRow();
+ MatrixCursor.RowBuilder row0 = cIn1.newRow();
row0.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY0);
row0.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i));
- MatrixCursor.RowBuilder row1 = cIn.newRow();
+ MatrixCursor.RowBuilder row1 = cIn2.newRow();
row1.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY1);
row1.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i));
}
+ Cursor cIn = new MergeCursor(new Cursor[] { cIn1, cIn2 });
+
// Update the model, then make sure it contains all the expected items.
DirectoryResult r = new DirectoryResult();
r.cursor = cIn;
diff --git a/packages/Keyguard/res/values-ky-rKG/strings.xml b/packages/Keyguard/res/values-ky-rKG/strings.xml
index 5089512..098d5a2 100644
--- a/packages/Keyguard/res/values-ky-rKG/strings.xml
+++ b/packages/Keyguard/res/values-ky-rKG/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_password_puk_failed" msgid="2838824369502455984">"SIM-картанын PUK-кодун ачуу кыйрады!"</string>
<string name="kg_pin_accepted" msgid="1448241673570020097">"Код кабыл алынды!"</string>
<string name="keyguard_carrier_default" msgid="8700650403054042153">"Байланыш жок."</string>
- <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"Киргизүү ыкмасын которуу"</string>
+ <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"Киргизүү ыкмасын өзгөртүү"</string>
<string name="airplane_mode" msgid="3122107900897202805">"Учак режими"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="5519822969283306009">"Түзмөк кайра күйгүзүлгөндөн кийин графикалык ачкыч талап кылынат"</string>
<string name="kg_prompt_reason_restart_pin" msgid="4411398237158448198">"Түзмөк кайра күйгүзүлгөндөн кийин PIN код талап кылынат"</string>
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index 81666fe..a73dcb6 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_password_puk_failed" msgid="2838824369502455984">"SIM卡PUK码操作失败!"</string>
<string name="kg_pin_accepted" msgid="1448241673570020097">"代码正确!"</string>
<string name="keyguard_carrier_default" msgid="8700650403054042153">"无服务。"</string>
- <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"输入法切换按钮。"</string>
+ <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"切换输入法"</string>
<string name="airplane_mode" msgid="3122107900897202805">"飞行模式"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="5519822969283306009">"重启设备后需要绘制解锁图案"</string>
<string name="kg_prompt_reason_restart_pin" msgid="4411398237158448198">"重启设备后需要输入 PIN 码"</string>
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index 2b44d51..1d4ed1d 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -51,6 +51,7 @@
static jclass app_fuse_class;
static jmethodID app_fuse_get_file_size;
static jmethodID app_fuse_read_object_bytes;
+static jmethodID app_fuse_write_object_bytes;
static jfieldID app_fuse_buffer;
// NOTE:
@@ -140,6 +141,9 @@
case FUSE_READ:
invoke_handler(fd, req, &AppFuse::handle_fuse_read);
return true;
+ case FUSE_WRITE:
+ invoke_handler(fd, req, &AppFuse::handle_fuse_write);
+ return true;
case FUSE_RELEASE:
invoke_handler(fd, req, &AppFuse::handle_fuse_release);
return true;
@@ -289,6 +293,29 @@
return 0;
}
+ int handle_fuse_write(const fuse_in_header& /* header */,
+ const fuse_write_in* in,
+ FuseResponse<fuse_write_out>* out) {
+ if (in->size > MAX_WRITE) {
+ return -EINVAL;
+ }
+ const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh);
+ if (it == handles_.end()) {
+ return -EBADF;
+ }
+ const uint64_t offset = in->offset;
+ const uint32_t size = in->size;
+ const void* const buffer = reinterpret_cast<const uint8_t*>(in) + sizeof(fuse_write_in);
+ uint32_t written_size;
+ const int result = write_object_bytes(it->second, offset, size, buffer, &written_size);
+ if (result < 0) {
+ return result;
+ }
+ out->prepare_buffer();
+ out->data()->size = written_size;
+ return 0;
+ }
+
int handle_fuse_release(const fuse_in_header& /* header */,
const fuse_release_in* in,
FuseResponse<void>* /* out */) {
@@ -355,6 +382,27 @@
return read_size;
}
+ int write_object_bytes(int inode, uint64_t offset, uint32_t size, const void* buffer,
+ uint32_t* written_size) {
+ ScopedLocalRef<jbyteArray> array(
+ env_,
+ static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer)));
+ {
+ ScopedByteArrayRW bytes(env_, array.get());
+ if (bytes.get() == nullptr) {
+ return -EIO;
+ }
+ memcpy(bytes.get(), buffer, size);
+ }
+ *written_size = env_->CallIntMethod(
+ self_, app_fuse_write_object_bytes, inode, offset, size, array.get());
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionClear();
+ return -EIO;
+ }
+ return 0;
+ }
+
static void fuse_reply(int fd, int unique, int reply_code, void* reply_data,
size_t reply_size) {
// Don't send any data for error case.
@@ -469,6 +517,28 @@
return -1;
}
+ app_fuse_write_object_bytes = env->GetMethodID(app_fuse_class, "writeObjectBytes", "(IJI[B)I");
+ if (app_fuse_write_object_bytes == nullptr) {
+ ALOGE("Can't find getWriteObjectBytes");
+ return -1;
+ }
+
+ app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B");
+ if (app_fuse_buffer == nullptr) {
+ ALOGE("Can't find mBuffer");
+ return -1;
+ }
+
+ const jfieldID read_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_READ", "I");
+ if (static_cast<int>(env->GetStaticIntField(app_fuse_class, read_max_fied)) != MAX_READ) {
+ return -1;
+ }
+
+ const jfieldID write_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_WRITE", "I");
+ if (static_cast<int>(env->GetStaticIntField(app_fuse_class, write_max_fied)) != MAX_WRITE) {
+ return -1;
+ }
+
const int result = android::AndroidRuntime::registerNativeMethods(
env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods));
if (result < 0) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
index 38435f4..1fd471c 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
@@ -38,8 +38,12 @@
* Max read amount specified at the FUSE kernel implementation.
* The value is copied from sdcard.c.
*/
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
static final int MAX_READ = 128 * 1024;
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
+ static final int MAX_WRITE = 256 * 1024;
+
private final String mName;
private final Callback mCallback;
@@ -47,7 +51,7 @@
* Buffer for read bytes request.
* Don't use the buffer from the out of AppFuseMessageThread.
*/
- private byte[] mBuffer = new byte[MAX_READ];
+ private byte[] mBuffer = new byte[Math.max(MAX_READ, MAX_WRITE)];
private Thread mMessageThread;
private ParcelFileDescriptor mDeviceFd;
@@ -79,11 +83,22 @@
}
}
- public ParcelFileDescriptor openFile(int i) throws FileNotFoundException {
+ /**
+ * Opens a file on app fuse and returns ParcelFileDescriptor.
+ *
+ * @param i ID for opened file.
+ * @param mode Mode for opening file.
+ * @see ParcelFileDescriptor#MODE_READ_ONLY
+ * @see ParcelFileDescriptor#MODE_WRITE_ONLY
+ */
+ public ParcelFileDescriptor openFile(int i, int mode) throws FileNotFoundException {
+ Preconditions.checkArgument(
+ mode == ParcelFileDescriptor.MODE_READ_ONLY ||
+ mode == ParcelFileDescriptor.MODE_WRITE_ONLY);
return ParcelFileDescriptor.open(new File(
getMountPoint(),
Integer.toString(i)),
- ParcelFileDescriptor.MODE_READ_ONLY);
+ mode);
}
File getMountPoint() {
@@ -100,7 +115,7 @@
long getFileSize(int inode) throws FileNotFoundException;
/**
- * Returns flie bytes for the give inode.
+ * Returns file bytes for the give inode.
* @param inode
* @param offset Offset for file bytes.
* @param size Size for file bytes.
@@ -109,6 +124,17 @@
* @throws IOException
*/
long readObjectBytes(int inode, long offset, long size, byte[] bytes) throws IOException;
+
+ /**
+ * Handles writing bytes for the give inode.
+ * @param inode
+ * @param offset Offset for file bytes.
+ * @param size Size for file bytes.
+ * @param bytes Buffer to store file bytes.
+ * @return Number of read bytes. Must not be negative.
+ * @throws IOException
+ */
+ int writeObjectBytes(int inode, long offset, int size, byte[] bytes) throws IOException;
}
@UsedByNative("com_android_mtp_AppFuse.cpp")
@@ -138,6 +164,15 @@
}
}
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
+ @WorkerThread
+ private /* unsgined */ int writeObjectBytes(int inode,
+ /* unsigned */ long offset,
+ /* unsigned */ int size,
+ byte[] bytes) throws IOException {
+ return mCallback.writeObjectBytes(inode, offset, size, bytes);
+ }
+
private native boolean native_start_app_fuse_loop(int fd);
private class AppFuseMessageThread extends Thread {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index cf5bee5..4152d1e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -242,7 +242,8 @@
// extension.
if (MtpDeviceRecord.isPartialReadSupported(
device.operationsSupported, fileSize)) {
- return mAppFuse.openFile(Integer.parseInt(documentId));
+ return mAppFuse.openFile(
+ Integer.parseInt(documentId), ParcelFileDescriptor.MODE_READ_ONLY);
} else {
return getPipeManager(identifier).readDocument(mMtpManager, identifier);
}
@@ -606,5 +607,12 @@
public long getFileSize(int inode) throws FileNotFoundException {
return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode));
}
+
+ @Override
+ public int writeObjectBytes(int inode, long offset, int size, byte[] bytes)
+ throws IOException {
+ // TODO: Implement it.
+ throw new IOException();
+ }
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java b/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
index a7f295f..2ded925 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
@@ -22,7 +22,7 @@
/**
* Annotation that shows the method is used by JNI.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.FIELD})
public @interface UsedByNative {
/**
* JNI file name that uses the method.
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
index c0973bd..3b92506 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
@@ -56,7 +56,8 @@
}
});
appFuse.mount(storageManager);
- final ParcelFileDescriptor fd = appFuse.openFile(INODE);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ INODE, ParcelFileDescriptor.MODE_READ_ONLY);
fd.close();
appFuse.close();
}
@@ -67,11 +68,21 @@
final AppFuse appFuse = new AppFuse("test", new TestCallback());
appFuse.mount(storageManager);
try {
- appFuse.openFile(INODE);
+ appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_ONLY);
fail();
- } catch (Throwable t) {
- assertTrue(t instanceof FileNotFoundException);
- }
+ } catch (FileNotFoundException exp) {}
+ appFuse.close();
+ }
+
+ public void testOpenFile_illegalMode() throws IOException {
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ final int INODE = 10;
+ final AppFuse appFuse = new AppFuse("test", new TestCallback());
+ appFuse.mount(storageManager);
+ try {
+ appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_WRITE);
+ fail();
+ } catch (IllegalArgumentException exp) {}
appFuse.close();
}
@@ -105,7 +116,8 @@
}
});
appFuse.mount(storageManager);
- final ParcelFileDescriptor fd = appFuse.openFile(fileInode);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ fileInode, ParcelFileDescriptor.MODE_READ_ONLY);
try (final ParcelFileDescriptor.AutoCloseInputStream stream =
new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
final byte[] buffer = new byte[1024];
@@ -115,6 +127,71 @@
appFuse.close();
}
+ public void testWriteFile() throws IOException {
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ final int INODE = 10;
+ final byte[] resultBytes = new byte[5];
+ final AppFuse appFuse = new AppFuse(
+ "test",
+ new TestCallback() {
+ @Override
+ public long getFileSize(int inode) throws FileNotFoundException {
+ if (inode != INODE) {
+ throw new FileNotFoundException();
+ }
+ return resultBytes.length;
+ }
+
+ @Override
+ public int writeObjectBytes(int inode, long offset, int size, byte[] bytes) {
+ for (int i = 0; i < size; i++) {
+ resultBytes[(int)(offset + i)] = bytes[i];
+ }
+ return size;
+ }
+ });
+ appFuse.mount(storageManager);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ INODE, ParcelFileDescriptor.MODE_WRITE_ONLY);
+ try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+ stream.write('a');
+ stream.write('b');
+ stream.write('c');
+ stream.write('d');
+ stream.write('e');
+ }
+ final byte[] BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e' };
+ assertTrue(Arrays.equals(BYTES, resultBytes));
+ appFuse.close();
+ }
+
+ public void testWriteFile_writeError() throws IOException {
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ final int INODE = 10;
+ final AppFuse appFuse = new AppFuse(
+ "test",
+ new TestCallback() {
+ @Override
+ public long getFileSize(int inode) throws FileNotFoundException {
+ if (inode != INODE) {
+ throw new FileNotFoundException();
+ }
+ return 5;
+ }
+ });
+ appFuse.mount(storageManager);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ INODE, ParcelFileDescriptor.MODE_WRITE_ONLY);
+ try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+ stream.write('a');
+ fail();
+ } catch (IOException e) {
+ }
+ appFuse.close();
+ }
+
private static class TestCallback implements AppFuse.Callback {
@Override
public long getFileSize(int inode) throws FileNotFoundException {
@@ -126,5 +203,11 @@
throws IOException {
throw new IOException();
}
+
+ @Override
+ public int writeObjectBytes(int inode, long offset, int size, byte[] bytes)
+ throws IOException {
+ throw new IOException();
+ }
}
}
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index c8478f2..0224c6c 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Herbegin"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met drukker nie"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie beskikbaar nie"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gebruik <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Jou dokument kan dalk deur een of meer bedieners op pad na die drukker gaan."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Jammer, dit het nie gewerk nie. Probeer weer."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Herprobeer"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Hierdie drukker is nie op die oomblik beskikbaar nie."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Kan nie voorskou wys nie"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Berei tans voorskou voor …"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index d4426cc..98255d4 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"እንደገና ጀምር"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ከአታሚ ጋር ምንም ግንኙነት የለም"</string>
<string name="reason_unknown" msgid="5507940196503246139">"አይታወቅም"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – አይገኝም"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ይጠቀሙ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ሰነድዎ ወደ አታሚው በሚሄድበት ወቅት በአንድ ወይም ከዚያ በላይ አገልጋዮች ውስጥ ሊያልፍ ይችላል።"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ይቅርታ፣ ያ አልሰራም። እንደገና ይሞክሩ።"</string>
<string name="print_error_retry" msgid="1426421728784259538">"እንደገና ይሞክሩ"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"አታሚው አሁን አይገኝም።"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ቅድመ ዕይታን ማሳየት አይቻልም"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ቅድመ እይታን በማዘጋጀት ላይ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index 2e1b6d0..5f0255c 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -84,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"إعادة تشغيل"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"لا يوجد اتصال بالطابعة"</string>
<string name="reason_unknown" msgid="5507940196503246139">"غير معروف"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – غير متاحة"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"هل تريد استخدام <xliff:g id="SERVICE">%1$s</xliff:g>؟"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"من الممكن أن يمر المستند عبر خادم أو أكثر أثناء إرساله إلى الطابعة."</string>
<string-array name="color_mode_labels">
@@ -104,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"عذرًا، هذا لا يعمل. أعد المحاولة."</string>
<string name="print_error_retry" msgid="1426421728784259538">"إعادة المحاولة"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"الطابعة ليست متوفرة في الوقت الحالي."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"يتعذر عرض المعاينة."</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"جارٍ تحضير المعاينة…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-az-rAZ/strings.xml b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
index 5490b84..b5dfaf8 100644
--- a/packages/PrintSpooler/res/values-az-rAZ/strings.xml
+++ b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Yenidən başlat"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printerə heç bir bağlantı yoxdur"</string>
<string name="reason_unknown" msgid="5507940196503246139">"naməlum"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>– əlçatmaz"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> xidmətindən istifadə edilsin?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Sənədiniz printerə qədər bir və ya daha çox server vasitəsilə keçə bilər."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Üzr istəyirik, alınmadı. Yenidən cəhd edin."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Yenidən yoxla"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Bu printer hazırda əlçatan deyil."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Önizləmə göstərilə bilmir"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Önizləməyə hazırlıq gedir..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
index 0574dae..2ac8002 100644
--- a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
+++ b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
@@ -81,7 +81,6 @@
<string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze sa štampačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznato"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nedostupan"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite li da koristite <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument može da prođe kroz jedan ili više servera na putu do štampača."</string>
<string-array name="color_mode_labels">
@@ -101,5 +100,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Žao nam je, ovo nije uspelo. Pokušajte ponovo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Pokušajte ponovo"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ovaj štampač trenutno nije dostupan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nije uspeo prikaz pregleda"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-be-rBY/strings.xml b/packages/PrintSpooler/res/values-be-rBY/strings.xml
index b581045..13d573e 100644
--- a/packages/PrintSpooler/res/values-be-rBY/strings.xml
+++ b/packages/PrintSpooler/res/values-be-rBY/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Перазапусціць"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Няма падлучэння да прынтара"</string>
<string name="reason_unknown" msgid="5507940196503246139">"невядома"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недаступна"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Выкарыстоўваць службу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Ваш дакумент можа прайсці праз адзін ці больш сервераў перад тым, як будзе надрукаваны."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"На жаль, не атрымалася. Паспрабуйце яшчэ раз."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Паўтарыць спробу"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Гэты прынтар зараз недаступны."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Папярэдні прагляд немагчымы"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Падрыхтоўка папярэдняга прагляду..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index 88af8e4..73c51e9 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Рестартиране"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Няма връзка с принтера"</string>
<string name="reason_unknown" msgid="5507940196503246139">"няма данни"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – не е налице"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Да се използва ли „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"По пътя към принтера документът ви може да премине през един или повече сървъри."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"За съжаление това не проработи. Опитайте отново."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Нов опит"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"В момента този принтер не е налице."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Визуализацията не може да се покаже"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Визуализацията се подготвя…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index c61ef74..25b4660 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"পুনর্সূচনা"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"মুদ্রকে কোনো সংযোগ নেই"</string>
<string name="reason_unknown" msgid="5507940196503246139">"অজানা"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – অনুপলব্ধ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ব্যবহার করবেন?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"আপনার দস্তাবেজ মুদ্রকে যাওয়ার সময় এক বা একাধিক সার্ভারের মাধ্যমে পাস হতে পারে।"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, এটি কাজ করেনি৷ আবার চেষ্টা করুন৷"</string>
<string name="print_error_retry" msgid="1426421728784259538">"পুনরায় চেষ্টা করুন"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"এই মূহুর্তে প্রিন্টার উপলব্ধ নয়।"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"পূর্বরূপ প্রদর্শন করা যাবে না"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"পূর্বরূপ প্রস্তুত করছে..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-bs-rBA/strings.xml b/packages/PrintSpooler/res/values-bs-rBA/strings.xml
index 7465c3c..9a17707 100644
--- a/packages/PrintSpooler/res/values-bs-rBA/strings.xml
+++ b/packages/PrintSpooler/res/values-bs-rBA/strings.xml
@@ -81,7 +81,6 @@
<string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema konekcije sa štampačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznat"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nedostupan"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Zaista želite koristiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Moguće je da dokument prije štampanja prođe kroz jedan ili više servera."</string>
<string-array name="color_mode_labels">
@@ -101,5 +100,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Nažalost, nije uspjelo. Pokušajte ponovo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Ponovi"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Štampač trenutno nije dostupan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Pregled se ne može prikazati"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index 482100a..a1df406 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Reinicia"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hi ha connexió amb la impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconegut"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vols fer servir <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"És possible que el document passi com a mínim per un servidor abans d\'imprimir-se."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"No ha funcionat. Torna-ho a provar."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Torna-ho a provar"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ara mateix, aquesta impressora no està disponible."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"La previsualització no es pot mostrar"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"S\'està preparant la previsualització..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index a4c412c..55fb21b 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Restartovat"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nelze se připojit k tiskárně"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznámé"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – není k dispozici"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Použít službu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument může cestou do tiskárny projít jedním i více servery."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Litujeme, nepodařilo se. Zkuste to znovu."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Opakovat"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Tiskárna aktuálně není k dispozici."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Náhled nelze zobrazit"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Příprava náhledu…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index 9ee252167..49417bd 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Genstart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse til printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukendt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ikke tilgængelig"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vil du bruge <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dit dokument passerer muligvis gennem én eller flere servere på vej til printeren."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Det virkede desværre ikke. Prøv igen."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Prøv igen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Denne printer er i øjeblikket ikke tilgængelig."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Eksempelvisning kan ikke vises"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Eksempelvisning forberedes..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index ef451b7..cb7aeee 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Neu starten"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Keine Verbindung zum Drucker"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unbekannt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nicht verfügbar"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> verwenden?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dein Dokument passiert bei der Übermittlung an den Drucker möglicherweise einen oder mehrere Server."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Fehler. Bitte versuche es erneut."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Erneut versuchen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Dieser Drucker ist momentan nicht verfügbar."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Vorschau kann nicht angezeigt werden"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Vorschau wird vorbereitet…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index 9be81c1..4441ea2 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Επανεκκίνηση"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Δεν υπάρχει σύνδεση με εκτυπωτή"</string>
<string name="reason_unknown" msgid="5507940196503246139">"άγνωστο"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – μη διαθέσιμο"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Να χρησιμοποιηθεί η υπηρεσία <xliff:g id="SERVICE">%1$s</xliff:g>;"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Το έγγραφό σας μπορεί να περάσει από έναν ή περισσότερους διακομιστές κατά τη μετάβαση στον εκτυπωτή."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Δυστυχώς, αυτό δεν λειτούργησε. Δοκιμάστε ξανά."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Επανάληψη"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Αυτός ο εκτυπωτής δεν είναι διαθέσιμος αυτήν τη στιγμή."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Αδυναμία προβολής προεπισκόπησης"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Προετοιμασία προεπισκόπησης…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rAU/strings.xml b/packages/PrintSpooler/res/values-en-rAU/strings.xml
index 8b58011..f8b6265 100644
--- a/packages/PrintSpooler/res/values-en-rAU/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rAU/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sorry, that didn\'t work. Try again."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"This printer isn\'t available right now."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Can\'t display preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparing preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index 8b58011..f8b6265 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sorry, that didn\'t work. Try again."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"This printer isn\'t available right now."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Can\'t display preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparing preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index 8b58011..f8b6265 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sorry, that didn\'t work. Try again."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"This printer isn\'t available right now."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Can\'t display preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparing preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 8fa6094..8d55597 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora."</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"¿Deseas usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Es posible que el documento pase por uno o varios servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"No funcionó. Vuelve a intentarlo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Volver a intentar"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impresora no está disponible en este momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"No se puede mostrar la vista previa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando vista previa…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index dba0491..7d08a0e 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Volver a empezar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – no disponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"¿Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Es posible que el documento pase por uno o varios servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"No ha funcionado. Prueba de nuevo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Reintentar"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impresora no está disponible en este momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"No se puede mostrar la vista previa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando vista previa…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 6dde083..09da3e0 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Taaskäivita"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printeriühendus puudub"</string>
<string name="reason_unknown" msgid="5507940196503246139">"teadmata"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – pole saadaval"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Kas soovite kasutada teenust <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Printerini jõudmiseks võib dokument läbida ühe või mitu serverit."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Kahjuks see ei toiminud. Proovige uuesti."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Proovi uuesti"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"See printer ei ole praegu saadaval."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Eelvaadet ei õnnestu kuvada"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Eelvaate ettevalmistamine ..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index 858444b..6b760b4 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Berrabiarazi"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Inprimagailua ez dago konektatuta"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ezezaguna"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: ez dago erabilgarri"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> erabili nahi duzu?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Baliteke dokumentuak zerbitzari batean edo gehiagotan zehar igarotzea inprimagailurako bidean."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Horrek ez du funtzionatu. Saiatu berriro."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Saiatu berriro"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Une honetan inprimagailua ez dago erabilgarri."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ezin da bistaratu aurrebista"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Aurrebista prestatzen…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 7c69c27..fa105d5 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"راهاندازی مجدد"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"اتصال با چاپگر برقرار نیست"</string>
<string name="reason_unknown" msgid="5507940196503246139">"نامعلوم"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - در دسترس نیست"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"از <xliff:g id="SERVICE">%1$s</xliff:g> استفاده شود؟"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ممکن است سندتان برای رسیدن به چاپگر از یک یا چند سرور عبور کند."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"متأسفیم، تلاش ناموفق بود. دوباره امتحان کنید."</string>
<string name="print_error_retry" msgid="1426421728784259538">"امتحان مجدد"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"این چاپگر اکنون در دسترس نیست."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"نمایش پیشنمایش امکانپذیر نیست"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"در حال آمادهسازی پیشنمایش…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index dfd98f8..cf051f8 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Käynnistä uudelleen"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ei yhteyttä tulostimeen"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tuntematon"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ei käytettävissä"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Käytetäänkö palvelua <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Asiakirja saattaa kulkea yhden tai useamman palvelimen kautta matkalla tulostimeen."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Ei valitettavasti onnistunut. Yritä uudelleen."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Yritä uudelleen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Tämä tulostin ei ole käyttävissä juuri nyt."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Esikatselua ei voi näyttää."</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Esikatselua valmistellaan…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index a95d565..11d2875 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Recommencer"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnu"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — indisponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Utiliser <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Votre document peut passer par un ou plusieurs serveurs avant d\'arriver à l\'imprimante."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Échec de l\'action. Réessayez."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Réessayer"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Cette imprimante n\'est pas accessible pour le moment."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Impossible d\'afficher l\'aperçu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Préparation de l\'aperçu en cours…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index dd1f490..6b89281 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Redémarrer"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante."</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnue"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Utiliser <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Votre document peut passer par un ou plusieurs serveurs avant d\'être envoyé sur l\'imprimante."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Échec de l\'opération. Veuillez réessayer."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Réessayer"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Cette imprimante n\'est pas disponible actuellement."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Impossible d\'afficher l\'aperçu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Préparation de l\'aperçu en cours…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index 81e080e..7ddc9f8 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Non hai conexión coa impresora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"descoñecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: non dispoñible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Queres usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"É posible que o teu documento pase por un ou máis servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Non funcionou. Téntao de novo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar de novo"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impresora non está dispoñible nestes momentos."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Non se pode mostrar a vista previa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando a vista previa…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-gu-rIN/strings.xml b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
index 44ede86..6a7e0df 100644
--- a/packages/PrintSpooler/res/values-gu-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"પુનઃપ્રારંભ કરો"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"પ્રિન્ટર માટે કોઈ કનેક્શન નથી"</string>
<string name="reason_unknown" msgid="5507940196503246139">"અજાણ્યું"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – અનુપલબ્ધ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> નો ઉપયોગ કરીએ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"તમારો દસ્તાવેજ પ્રિન્ટર સુધીના તેના માર્ગમાં એક અથવા વધુ સર્વર્સથી પસાર થઈ શકે છે."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"માફ કરશો, તે કામ કરતું નહોતું. ફરીથી પ્રયાસ કરો."</string>
<string name="print_error_retry" msgid="1426421728784259538">"ફરી પ્રયાસ કરો"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"આ પ્રિન્ટર અત્યારે ઉપલબ્ધ નથી."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"પૂર્વાવલોકન પ્રદર્શિત કરી શકતાં નથી"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"પૂર્વાવલોકનની તૈયારી કરી રહ્યું છે..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index f75630e..377dc62 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"पुन: आरंभ करें"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटर के लिए कोई कनेक्शन नहीं"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – अनुपलब्ध"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> का उपयोग करें?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"प्रिंटर पर जाते समय आपका दस्तावेज़ एक या अधिक सर्वर से गुज़र सकता है."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"क्षमा करें, उससे बात नहीं बनी. पुन: प्रयास करें."</string>
<string name="print_error_retry" msgid="1426421728784259538">"फिर से प्रयास करें"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"यह प्रिंटर इस समय उपलब्ध नहीं है."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"पूर्वावलोकन प्रदर्शित नहीं किया जा सकता"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"पूर्वावलोकन तैयार हो रहा है..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index bd29d02..8550be4 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -81,7 +81,6 @@
<string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze s pisačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznato"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – zadatak nije dostupan"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite li upotrijebiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Na putu do pisača vaš dokument može proći kroz jedan ili više poslužitelja."</string>
<string-array name="color_mode_labels">
@@ -101,5 +100,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Nažalost, to nije uspjelo. Pokušajte ponovo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Pokušajte ponovno"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Pisač trenutačno nije dostupan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Pregled nije dostupan"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 356cb76..20789a3 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Újraindítás"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nincs kapcsolat a nyomtatóval"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ismeretlen"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nem érhető el"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Használni szeretné a következő szolgáltatást: <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"A dokumentum áthaladhat egy vagy több szerveren, mielőtt a nyomtatóhoz érne."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sajnáljuk, de nem sikerült. Próbálja újra."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Újra"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ez a nyomtató jelenleg nem érhető el."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nem lehet megjeleníteni az előnézetet"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Előnézet előkészítése…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 2d10166..8950338 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Վերագործարկել"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Տպիչի հետ կապ չկա"</string>
<string name="reason_unknown" msgid="5507940196503246139">"անհայտ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> տպիչն անհասանելի է"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Օգտագործե՞լ <xliff:g id="SERVICE">%1$s</xliff:g>-ը:"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Հնարավոր է՝ փաստաթուղթը մի քանի սերվերներով անցնի մինչ տպվելը:"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Չհաջողվեց: Նորից փորձեք:"</string>
<string name="print_error_retry" msgid="1426421728784259538">"Կրկնել"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Տպիչն այս պահին հասանելի չէ:"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Նախադիտումը հնարավոր չէ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Նախադիտումը պատրաստվում է…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index 8e20d27..9b72250 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Mulai Ulang"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tidak ada sambungan ke printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tak diketahui"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gunakan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumen Anda dapat melewati satu atau beberapa server saat menuju printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Maaf, tidak berhasil. Coba lagi."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Coba lagi"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Saat ini printer ini tidak tersedia."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Tidak dapat menampilkan pratinjau"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Menyiapkan pratinjau..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index 73660fb..37abb5a 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Endurræsa"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Engin tenging við prentara"</string>
<string name="reason_unknown" msgid="5507940196503246139">"óþekkt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ekki í boði"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Nota <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Skjalið gæti þurft að fara í gegnum einn eða fleiri þjóna á leið sinni til prentarans."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Þetta virkaði því miður ekki. Reyndu aftur."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Reyna aftur"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Þessi prentari er ekki í boði núna."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ekki hægt að birta forskoðun"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Undirbýr forskoðun…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index 46a570d..dd4a8cb 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Riavvia"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nessun collegamento alla stampante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"sconosciuto"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - non disponibile"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Utilizzare <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Il tuo documento potrebbe passare da uno o più server per raggiungere la stampante."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Non ha funzionato. Riprova."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Riprova"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Al momento la stampante non è disponibile."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Impossibile visualizzare l\'anteprima"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparazione anteprima…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index c26c3d1..1d813bb 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"הפעל מחדש"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"אין חיבור למדפסת"</string>
<string name="reason_unknown" msgid="5507940196503246139">"לא ידוע"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – לא זמינה"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"האם להשתמש ב-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ייתכן שהמסמך שלך יעבור בשרת אחד או יותר בדרכו למדפסת."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"מצטערים, אך זה לא עבד. נסה שוב."</string>
<string name="print_error_retry" msgid="1426421728784259538">"נסה שוב"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"המדפסת הזו אינה זמינה כעת."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"לא ניתן להציג תצוגה מקדימה"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"מכין תצוגה מקדימה…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index a6e243f..946052a 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"再試行"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"プリンタに接続されていません"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>–使用不可"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>を利用しますか?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ドキュメントは1つ以上のサーバーを経由してプリンタに送信されることがあります。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"エラーです。もう一度お試しください。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"再試行"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"現在このプリンターは使用できません。"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"プレビューを表示できません"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"プレビューを準備しています…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index 2608ed4..94152b1 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"გადატვირთვა"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"პრინტერთან კავშირი არ არის"</string>
<string name="reason_unknown" msgid="5507940196503246139">"უცნობი"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – მიუწვდომელია"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"გსურთ, გამოიყენოთ <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"პრინტერამდე გზად დოკუმენტმა შეიძლება ერთი ან მეტი სერვერი გაიაროს."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"უკაცრავად, ვერ მოხერხდა. სცადეთ ისევ."</string>
<string name="print_error_retry" msgid="1426421728784259538">"გამეორება"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"პრინტერი ამჟამად მიუწვდომელია."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"გადახედვის ჩვენება ვერ ხერხდება"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"მზადდება გადახედვა…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index def0c3c..2bc5ab6 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Қайта бастау"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтермен байланыс жоқ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"белгісіз"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – қол жетімсіз"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> қолданылсын ба?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Құжат принтерге жеткенше бір немесе бірнеше серверден өтуі мүмкін."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Кешіріңіз, бұл нәтиже бермеді. Әрекетті қайталаңыз."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Қайталау"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Бұл принтер дәл қазір қол жетімді емес."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Алдын ала қарауды көрсету мүмкін емес"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Алдын ала қарау дайындалуда…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index 24048cf..330edf5 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើមឡើងវិញ"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
<string name="reason_unknown" msgid="5507940196503246139">"មិនស្គាល់"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិនអាចប្រើបាន"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ប្រើ <xliff:g id="SERVICE">%1$s</xliff:g> ឬ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ឯកសាររបស់អ្នកអាចនឹងឆ្លងកាត់ម៉ាស៊ីនមេមួយ ឬច្រើននៅពេលដែលវាធ្វើដំណើរទៅកាន់ម៉ាស៊ីនបោះពុម្ព។"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"សូមទោស វាមិនដំណើរការទេ។ ព្យាយាមម្ដងទៀត។"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ព្យាយាមម្ដងទៀត"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ឥឡូវនេះ ម៉ាស៊ីនបោះពុម្ពនេះមិនអាចប្រើបាន។"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"មិនអាចបង្ហាញការមើលជាមុនបានទេ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"កំពុងរៀបចំមើលជាមុន…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index af20965..f1cef86 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"ಮರುಪ್ರಾರಂಭಿಸು"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ಮುದ್ರಕಕ್ಕೆ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ಅಜ್ಞಾತ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ಬಳಸುವುದೇ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ನಿಮ್ಮ ಡಾಕ್ಯುಮೆಂಟ್ ಪ್ರಿಂಟರ್ಗೆ ಹೋಗುವ ಸಂದರ್ಭದಲ್ಲಿ ಒಂದು ಅಥವಾ ಅದಕ್ಕಿಂತ ಹೆಚ್ಚು ಸರ್ವರ್ಗಳ ಮೂಲಕ ಹಾದು ಹೋಗಬಹುದು."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ಕ್ಷಮಿಸಿ, ಅದು ಕೆಲಸ ಮಾಡುತ್ತಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="print_error_retry" msgid="1426421728784259538">"ಮರುಪ್ರಯತ್ನಿಸು"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ಈ ಪ್ರಿಂಟರ್ ಸದ್ಯಕ್ಕೆ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ಪೂರ್ವವೀಕ್ಷಣೆ ಪ್ರದರ್ಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ಪೂರ್ವವೀಕ್ಷಣೆ ತಯಾರಾಗುತ್ತಿದೆ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 0b297a2..d3cc967 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"다시 시작"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"프린터와 연결되지 않음"</string>
<string name="reason_unknown" msgid="5507940196503246139">"알 수 없음"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 사용할 수 없음"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용할까요?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"문서가 프린터로 전송되는 중에 하나 이상의 서버를 통과할 수 있습니다."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"죄송합니다. 오류가 발생했습니다. 다시 시도해 보세요."</string>
<string name="print_error_retry" msgid="1426421728784259538">"다시 시도"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"현재 이 프린터를 사용할 수 없습니다."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"미리보기를 표시할 수 없음"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"미리보기 준비 중…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index 85b2526..d84e5d8 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Кайра баштоо"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер менен байланыш жок"</string>
<string name="reason_unknown" msgid="5507940196503246139">"белгисиз"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – жеткиликтүү эмес"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> колдонулсунбу?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Принтерге жеткиче документиңиз бир же андан көп серверлерден өтүшү мүмкүн."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Кечиресиз, иштеген жок. Дагы бир жолу аракет кылып көрүңүз."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Дагы бир жолу аракет кылуу"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Учурда бул принтерди колдонуу мүмкүн эмес."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Алдын ала көрүнүшү көрсөтүлбөй жатат"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Алдын-ала көрүүгө даярданууда…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index 81ace83..6a69053 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"ປິດເປີດໃໝ່"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ບໍ່ມີການເຊື່ອມຕໍ່ຫາເຄື່ອງພິມ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ບໍ່ຮູ້ຈັກ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - ບໍ່ມີຢູ່"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g> ບໍ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ເອກະສານຂອງທ່ານອາດເດີນທາງຜ່ານໜຶ່ງ ຫຼື ຫຼາຍເຊີບເວີ ເພື່ອໄປຮອດເຄື່ອງພິມ."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ຂໍອະໄພ, ໃຊ້ບໍ່ໄດ້. ໃຫ້ລອງໃໝ່ອີກເທື່ອນຶ່ງ."</string>
<string name="print_error_retry" msgid="1426421728784259538">"ລອງໃໝ່"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ບໍ່ສາມາດໃຊ້ເຄື່ອງພິມນີ້ໃນເວລານີ້ໄດ້."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ບໍ່ສາມາດສະແດງຕົວຢ່າງໄດ້"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ກຳລັງກະກຽມຕົວຢ່າງ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index 40bc7f1..ddcaba7 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Paleisti iš naujo"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nėra ryšio su spausdintuvu"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nežinoma"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ – nepasiekiama"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Naudoti „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Kai dokumentas siunčiamas į spausdintuvą, jis gali būti perduodamas per vieną ar daugiau serverių."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Deja, tai neveikia. Bandykite dar kartą."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Bandykite dar kartą"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Šis spausdintuvas šiuo metu nepasiekiamas."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nepavyksta pateikti peržiūros"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Ruošiama peržiūra…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 11e689b..50ba32d 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -81,7 +81,6 @@
<string name="restart" msgid="2472034227037808749">"Restartēt"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nav savienojuma ar printeri"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nezināms"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — nav pieejams"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vai izmantot pakalpojumu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokuments, iespējams, tiek pārsūtīts caur vienu vai vairākiem serveriem, līdz tas nonāk līdz printerim."</string>
<string-array name="color_mode_labels">
@@ -101,5 +100,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Diemžēl tas neizdevās. Mēģiniet vēlreiz."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Mēģināt vēlreiz"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Šis printeris šobrīd nav pieejams."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nevar attēlot priekšskatījumu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Notiek priekšskatījuma sagatavošana..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index bc2b498..a189042 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Рестартирај"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нема поврзување со печатач"</string>
<string name="reason_unknown" msgid="5507940196503246139">"непознато"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - недостапен"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Користи <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"На пат до печатачот, документот може да помине преку еден или повеќе сервери."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"За жал, тоа не успеа. Обидете се повторно."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Обиди се повторно"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Овој печатач не е достапен во моментов."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Прегледот не може да се прикаже"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Се подготвува преглед…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index ade7fb39..5625632 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"പുനരാരംഭിക്കുക"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"പ്രിന്ററിൽ കണക്ഷനൊന്നുമില്ല"</string>
<string name="reason_unknown" msgid="5507940196503246139">"അജ്ഞാതം"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ലഭ്യമല്ല"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ഉപയോഗിക്കണോ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"നിങ്ങളുടെ പ്രമാണം പ്രിന്ററിലേക്ക് പോകുന്നതിനിടെ അത് ഒന്നോ അതിലധികമോ സെർവറുകളിലൂടെ കടന്നുപോകാനിടയുണ്ട്."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ക്ഷമിക്കണം, അത് പ്രവർത്തിച്ചില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="print_error_retry" msgid="1426421728784259538">"വീണ്ടും ശ്രമിക്കുക"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ഈ പ്രിന്ററർ ഇപ്പോൾ ലഭ്യമല്ല."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"പ്രിവ്യൂ കാണിക്കാൻ കഴിയില്ല"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"പ്രിവ്യൂ തയ്യാറാക്കുന്നു…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index 133d88c..7797944 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Дахин эхлүүлэх"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер холбогдоогүй байна"</string>
<string name="reason_unknown" msgid="5507940196503246139">"тодорхойгүй"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ашиглах боломжгүй"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>-г ашиглах уу?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Таны документ хэвлэгчид иртэл нэг эсвэл хэд хэдэн серверээр дамжина."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Уучлаарай, ажилласангүй. Дахин оролдоно уу."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Дахин оролдох"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Одоо хэвлэгч ашиглах боломжгүй."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Урьдчилан үзүүлэх боломжгүй"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Урьдчилан харахыг бэлтгэж байна…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index 2b3b29f..ee09db2 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"रीस्टार्ट करा"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटरवर कोणतेही कनेक्शन नाही"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – अनुपलब्ध"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> वापरायची?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"आपला दस्तऐवज प्रिंटरपर्यंत पोहचण्यापूर्वी एक किंवा अधिक सर्व्हरद्वारे जाऊ शकतो."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"क्षमस्व, त्याने कार्य केले नाही. पुन्हा प्रयत्न करा."</string>
<string name="print_error_retry" msgid="1426421728784259538">"पुन्हा प्रयत्न करा"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"हा प्रिंटर आत्ता उपलब्ध नाही."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"पूर्वावलोकन प्रदर्शित करू शकत नाही"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"पूर्वावलोकनाची तयारी करत आहे..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 73104e1..4042c71 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Mulakan semula"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tiada sambungan ke pencetak"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tidak diketahui"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gunakan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumen anda mungkin melalui satu atau beberapa pelayan dalam perjalanan ke pencetak."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Maaf, itu tidak berjaya. Cuba lagi."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Cuba semula"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Pencetak ini tidak tersedia sekarang."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Tidak dapat memaparkan pratonton"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Menyediakan pratonton..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index 8cec068..f34ca67 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"အစက ပြန်စရန်"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"စာထုတ်စက်နဲ့ ဆက်သွယ်ထားမှု မရှိပါ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"အကြောင်းအရာ မသိရှိ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – မတွေ့ရှိပါ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ကိုသုံးမလား။"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"သင်၏ စာရွက်စာတမ်းများသည် ပရင်တာထံသို့ သွားစဉ် ဆာဗာ တစ်ခု သို့မဟုတ် ပိုများပြီး ဖြတ်ကျော်နိုင်ရသည်။"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ဆော်ရီး၊ အဲဒါ အလုပ်မဖြစ်ခဲ့ပါ။ ထပ် စမ်းပါ။"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ထပ်စမ်း"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ဒီပရင်တာမှာ ယခုအချိန်မှာ မရနိုင်ပါ။"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"အစမ်းကြည့်ခြင်းကို ပြသ၍မရပါ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"အစမ်းကြည့်ရန် ပြင်ဆင်နေ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index 0a6f6c3..6b74765 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Start på nytt"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse med skriveren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukjent"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – utilgjengelig"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vil du bruke <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumentet ditt kan gå via flere tjenere før det når skriveren."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Beklager, det fungerte ikke. Prøv på nytt."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Prøv på nytt"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Denne skriveren er ikke tilgjengelig akkurat nå."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Kan ikke vise forhåndsvisningen"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Forbereder forhåndsvisningen …"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index 5af3a04..8e8bf15 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"पुनःस्टार्ट गर्नुहोस्"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिन्टरमा कुनै जडान छैन"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - अनुपलब्ध"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> प्रयोग गर्ने हो?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"तपाईँको कागजात प्रिन्टरमा जाँदा यसको मार्गमा एक वा धेरै सर्भरहरू पार हुनसक्छन्।"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"माफ गर्नुहोस्, त्यसले काम गरेन। पुनः प्रयास गर्नुहोस्।"</string>
<string name="print_error_retry" msgid="1426421728784259538">"पुनःप्रयास गर्नुहोस्"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"यो प्रिन्टर अहिले उपलब्ध छैन।"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"पूर्वावलोकनलाई प्रदर्शन गर्न सक्दैन"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"पूर्वावलोकन तयारी..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 4afdb86..3c65d8a 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Opnieuw starten"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niet beschikbaar"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> gebruiken?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Je document kan via een of meer servers naar de printer worden verzonden."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Dat werkte niet. Probeer het opnieuw."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Opnieuw proberen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Deze printer is momenteel niet beschikbaar."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Kan voorbeeld niet weergeven"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Voorbeeld voorbereiden…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pa-rIN/strings.xml b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
index 1886ef5..934123b 100644
--- a/packages/PrintSpooler/res/values-pa-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"ਰੀਸਟਾਰਟ ਕਰੋ"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ਪ੍ਰਿੰਟਰ ਲਈ ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ਅਗਿਆਤ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ਅਣਉਪਲਬਧ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਵਰਤਣੀ ਹੈ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ਤੁਹਾਡਾ ਦਸਤਾਵੇਜ਼ ਪ੍ਰਿੰਟਰ ਵਿੱਚ ਜਾਣ ਲਈ ਇੱਕ ਜਾਂ ਦੋ ਸਰਵਰਾਂ ਵਿੱਚੋਂ ਲੰਘਦਾ ਹੈ।"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ਮਾਫ਼ ਕਰਨਾ, ਉਸਨੇ ਲਾਭਕਾਰੀ ਨਹੀਂ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ਇਹ ਪ੍ਰਿੰਟਰ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ਝਲਕ ਨਹੀਂ ਵਿਖਾਈ ਜਾ ਸਕਦੀ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ਪ੍ਰੀਵਿਊ ਦੀ ਤਿਆਰੀ ਕਰ ਰਿਹਾ ਹੈ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 45649bb..80b6070 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Od nowa"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Brak połączenia z drukarką"</string>
<string name="reason_unknown" msgid="5507940196503246139">"brak informacji"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niedostępne"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Użyć usługi <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Zanim dokument dotrze do drukarki, może przejść przez jeden lub kilka serwerów."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"To nie zadziałało. Spróbuj jeszcze raz."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Ponów próbę"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Drukarka nie jest teraz dostępna."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nie można wyświetlić podglądu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Przygotowuję podgląd…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index 58eb24f..4bd1161 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – não disponível"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Seu documento pode passar por um ou mais servidores até chegar à impressora."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Falhou. Tente novamente."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar novamente"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impressora não está disponível no momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Não é possível exibir a visualização"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando visualização…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 370bbb9..7660c5c 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem ligação à impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponível"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Pretende utilizar o <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"O seu documento pode passar por um ou mais servidores no respetivo caminho para a impressora."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Lamentamos, mas isso não funcionou. Tente novam."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar novamente"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impressora não está atualmente disponível."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Não é possível apresentar a pré-visualização"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"A preparar a pré-visualização..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 58eb24f..4bd1161 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – não disponível"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Seu documento pode passar por um ou mais servidores até chegar à impressora."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Falhou. Tente novamente."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar novamente"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impressora não está disponível no momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Não é possível exibir a visualização"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando visualização…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 1097d56..dd38c31 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -81,7 +81,6 @@
<string name="restart" msgid="2472034227037808749">"Reporniți"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nu există conexiune la o imprimantă"</string>
<string name="reason_unknown" msgid="5507940196503246139">"necunoscut"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - indisponibil"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Folosiți <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Documentul poate trece prin unul sau mai multe servere pe calea spre imprimantă."</string>
<string-array name="color_mode_labels">
@@ -101,5 +100,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Ne pare rău, operațiunea nu a reușit. Încercați din nou."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Reîncercați"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Această imprimantă nu este disponibilă momentan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Previzualizarea nu se poate afișa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Se pregătește previzualizarea..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index 24b1e0d..6c074ed 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Повторить"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нет связи с принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"неизвестно"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступен"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Использовать <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Документ может пересылаться на принтер через несколько серверов."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Ошибка. Повторите попытку."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Повторить"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Принтер не готов."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Сбой предварительного просмотра"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Подготовка изображения…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index 707c151..8278aee 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"යළි අරඹන්න"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"මුද්රණ යන්ත්රය වෙත සම්බන්ධය නැත"</string>
<string name="reason_unknown" msgid="5507940196503246139">"නොදනී"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ලද නොහැක"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> භාවිත කරන්නද?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ඔබගේ ලේඛනය මුද්රණ යන්ත්රයට යන අතරතුර සේවාදායක එකක් හෝ කිහිපයක් හරහා යා හැක."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"කණගාටුයි, එය වැඩ නොකරයි. නැවත උත්සහ කරන්න."</string>
<string name="print_error_retry" msgid="1426421728784259538">"නැවත උත්සාහ කරන්න"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"දැන් මෙම මුද්රණ යන්ත්රය නොපවතී."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"පෙරදසුන සංදර්ශනය කළ නොහැකිය"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"පෙරදසුන සූදානම් කරමින්…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 1f13b54..610fe99 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Spustiť znova"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Žiadne pripojenie k tlačiarni"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznáme"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie je k dispozícii"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Použiť službu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Skôr ako sa váš dokument dostane do tlačiarne, môže prejsť jedným alebo viacerými servermi."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Je nám to ľúto, nefungovalo to. Skúste to znova."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Opakovať"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Táto tlačiareň nie je momentálne k dispozícii."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ukážka sa nedá zobraziť"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Pripravuje sa ukážka..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index 3f8a5e6..b5124b4 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Začni znova"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ni povezave s tiskalnikom"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznano"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ni na voljo"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite uporabiti storitev <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument gre lahko na poti do tiskalnika skozi enega ali več strežnikov."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"To žal ni delovalo. Poskusite znova."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Poskusi znova"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ta tiskalnik trenutno ni na voljo."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Predogleda ni mogoče prikazati"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprava predogleda …"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sq-rAL/strings.xml b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
index 0b843d7..27bbbf9 100644
--- a/packages/PrintSpooler/res/values-sq-rAL/strings.xml
+++ b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Rifillo"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printeri nuk është i lidhur"</string>
<string name="reason_unknown" msgid="5507940196503246139">"e panjohur"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nuk mundësohet"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Përdor <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumenti mund të kalojë përmes një ose shumë serverëve deri te printeri."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Na vjen keq, nuk funksionoi! Provo përsëri."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Provo sërish"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ky printer nuk mund të përdoret tani."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nuk mund të shfaqet paraafishimi"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Po përgatit shikimin paraprak…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 8baa23c..ea6fcb7 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -81,7 +81,6 @@
<string name="restart" msgid="2472034227037808749">"Поново покрени"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нема везе са штампачем"</string>
<string name="reason_unknown" msgid="5507940196503246139">"непознато"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступан"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Желите ли да користите <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Документ може да прође кроз један или више сервера на путу до штампача."</string>
<string-array name="color_mode_labels">
@@ -101,5 +100,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Жао нам је, ово није успело. Покушајте поново."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Покушајте поново"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Овај штампач тренутно није доступан."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Није успео приказ прегледа"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Припрема прегледа..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 64b6b20..c909e19 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Starta om"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen anslutning till skrivaren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"okänt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – inte tillgänglig"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vill du använda <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"På vägen till skrivaren kan dokumentet passera en eller flera servrar."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Det fungerade tyvärr inte. Försök igen."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Försök igen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Den här skrivaren är inte tillgänglig just nu."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Det gick inte att visa förhandsgranskningen"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Förbereder förhandsvisning ..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index b3ffa71..bd14117 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Anzisha upya"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hakuna muunganisho kwa printa"</string>
<string name="reason_unknown" msgid="5507940196503246139">"haijulikani"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - haipatikani"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Ungependa kutumia <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Huenda hati yako ikapitia seva moja au zaidi kabla ya kufika kwenye printa."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Samahani, hiyo haikufanya kazi. Jaribu tena."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Jaribu tena"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Printa hii haipatikani kwa sasa."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Haiwezi kupakia onyesho la kuchungulia"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Inaandaa onyesho la kuchungulia..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index 7ae3cbc..782ebf2 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"மீண்டும் தொடங்கு"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"அச்சுப்பொறியுடன் இணைக்கப்படவில்லை"</string>
<string name="reason_unknown" msgid="5507940196503246139">"அறியப்படாதது"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – இல்லை"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ஐப் பயன்படுத்தவா?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"உங்கள் ஆவணம் பிரிண்டருக்குச் செல்லும் வழியில் ஒன்று அல்லது அதற்கு மேற்பட்ட சேவையகங்களைக் கடந்து செல்லக்கூடும்."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"செயல்படவில்லை. மீண்டும் முயலவும்."</string>
<string name="print_error_retry" msgid="1426421728784259538">"மீண்டும் முயலவும்"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"இப்போது பிரிண்டர் இல்லை."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"மாதிரிக்காட்சியைக் காட்ட முடியவில்லை"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"மாதிரிக்காட்சியைத் தயார்படுத்துகிறது…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index 9e8dea2..ca393c839 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"పునఃప్రారంభించు"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ప్రింటర్కు కనెక్షన్ లేదు"</string>
<string name="reason_unknown" msgid="5507940196503246139">"తెలియదు"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – అందుబాటులో లేదు"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ని ఉపయోగించాలా?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"మీ పత్రం ప్రింటర్కు వెళ్లే మార్గంలో ఒకటి లేదా అంతకంటే ఎక్కువ సర్వర్ల గుండా వెళ్లవచ్చు."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"క్షమించండి, అది పని చేయలేదు. మళ్లీ ప్రయత్నించండి."</string>
<string name="print_error_retry" msgid="1426421728784259538">"మళ్లీ ప్రయత్నించు"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ఈ ప్రింటర్ ప్రస్తుతం అందుబాటులో లేదు."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"పరిదృశ్యాన్ని ప్రదర్శించడం సాధ్యపడలేదు"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"పరిదృశ్యం సిద్ధమవుతోంది…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index c623dd7..92b960e 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"เริ่มต้นใหม่"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ไม่มีการเชื่อมต่อไปยังเครื่องพิมพ์"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ไม่ทราบ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ไม่พร้อมใช้งาน"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ใช้ <xliff:g id="SERVICE">%1$s</xliff:g> ไหม"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"เอกสารของคุณอาจต้องผ่านมากกว่าหนึ่งเซิร์ฟเวอร์ระหว่างส่งไปยังเครื่องพิมพ์"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ขออภัย ไม่สามารถใช้งานได้ ลองอีกครั้ง"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ลองอีกครั้ง"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"เครื่องพิมพ์นี้ไม่พร้อมใช้งานในขณะนี้"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ไม่สามารถแสดงตัวอย่าง"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"กำลังเตรียมการแสดงตัวอย่าง…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index 0494cf6..5a73659 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"I-restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hindi nakakonekta sa printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"hindi alam"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – hindi available"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gusto mo bang gamitin ang <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Bago ma-print ang iyong dokumento, maaari itong dumaan sa isa o higit pang mga server."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Paumanhin, hindi iyon gumana. Subukang muli."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Subukang muli"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Hindi available ang printer na ito sa ngayon."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Hindi maipakita ang preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Inihahanda ang preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 2818f36..f17bc9d 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Yeniden başlat"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Yazıcı bağlantısı yok"</string>
<string name="reason_unknown" msgid="5507940196503246139">"bilinmiyor"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – kullanılamıyor"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> kullanılsın mı?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokümanınız yazıcıya giderken bir veya daha fazla sunucudan geçebilir."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Maalesef bu işe yaramadı. Tekrar deneyin."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Yeniden dene"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Bu yazı şu anda kullanılamıyor."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Önizleme gösterilemiyor"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Önizleme hazırlanıyor…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index 41051af..9629ad3 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -82,7 +82,6 @@
<string name="restart" msgid="2472034227037808749">"Перезапустити"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Немає з’єднання з принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"невідомо"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" не доступне"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Увімкнути службу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Коли ви надсилаєте документ на принтер, він може проходити через декілька серверів."</string>
<string-array name="color_mode_labels">
@@ -102,5 +101,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"На жаль, сталася помилка. Повторіть спробу."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Повторити"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Цей принтер зараз недоступний."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Не вдалося відкрити попередній перегляд"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Підготовка до попереднього перегляду…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index a94b16f..0d01ee2 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"دوبارہ شروع کریں"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"پرنٹر کے ساتھ کوئی کنکشن نہیں ہے"</string>
<string name="reason_unknown" msgid="5507940196503246139">"نامعلوم"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – دستیاب نہیں ہے"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> استعمال کریں؟"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"آپ کی دستاویز پرنٹر تک جاتے ہوئے ممکن ہے ایک یا زیادہ سرورز سے گزرے۔"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"معذرت، اس نے کام نہیں کیا۔ دوبارہ کوشش کریں۔"</string>
<string name="print_error_retry" msgid="1426421728784259538">"دوبارہ کوشش کریں"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"یہ پرنٹر ابھی دستیاب نہیں ہے۔"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"پیش منظر ڈسپلے نہیں ہو سکتا"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"پیش منظر کو تیار کیا جا رہا ہے…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index a6af5bd..1f379d9 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Qayta boshlash"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printer ulanmagan"</string>
<string name="reason_unknown" msgid="5507940196503246139">"noma’lum"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – mavjud emas"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> xizmatidan foydalanilsinmi?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Hujjatingiz chop etilishidan oldin bir yoki bir necha serverlardan o‘tishi mumkin."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Kechirasiz, ishlamadi. Qayta urinib ko‘ring."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Qayta urinish"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ushbu printer hozirda mavjud emas."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Oldindan ko‘rsatib bo‘lmaydi"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Dastlabki ko\'rishga tayyorlanmoqda…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index df9e1a4..b931557 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Bắt đầu lại"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Không có kết nối nào với máy in"</string>
<string name="reason_unknown" msgid="5507940196503246139">"không xác định"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – không khả dụng"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Sử dụng <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Tài liệu của bạn có thể đi qua một hoặc nhiều máy chủ trên đường đến máy in."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Rất tiếc, tính năng đó không hoạt động. Hãy thử lại."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Thử lại"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Máy in này hiện không khả dụng."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Không thể hiển thị bản xem trước"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Đang chuẩn bị xem trước…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index fb30e44..e523d48 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"重新开始"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"未与打印机建立连接"</string>
<string name="reason_unknown" msgid="5507940196503246139">"未知"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - 无法使用"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用<xliff:g id="SERVICE">%1$s</xliff:g>吗?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文档可能会通过一个或多个服务器发送至打印机。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,7 @@
<string name="print_error_default_message" msgid="8602678405502922346">"抱歉,操作失败。请重试。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"重试"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"该打印机目前无法使用。"</string>
+ <!-- no translation found for print_cannot_load_page (6179560924492912009) -->
+ <skip />
<string name="print_preparing_preview" msgid="3939930735671364712">"即将显示预览…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index f7c8fc9..4411a23 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"重新開始"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與打印機連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用 <xliff:g id="SERVICE">%1$s</xliff:g> 嗎?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文件可能會通過一部或多部伺服器才傳送至打印機。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"很抱歉,行不通。請再試一次。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"重試"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"這部打印機目前無法使用。"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"無法顯示預覽"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"正在準備預覽…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index aa18f3c..98126a7 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"重新開始"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與印表機建立連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用「<xliff:g id="SERVICE">%1$s</xliff:g>」嗎?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文件可能會透過一或多個伺服器輾轉傳送至印表機。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"很抱歉,無法執行這項操作。請再試一次。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"重試"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"這台印表機目前無法使用。"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"無法顯示預覽畫面"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"正在準備預覽…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 9cfcb33..f2b4990 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -80,7 +80,6 @@
<string name="restart" msgid="2472034227037808749">"Qala kabusha"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Akukho ukuxhumana kuphrinta"</string>
<string name="reason_unknown" msgid="5507940196503246139">"akwaziwa"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"I-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ayitholakali"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Sebenzisa i-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Idokhumenti yakho ingase idlule iseva eyodwa noma amaningi lapho iya kuphrinta."</string>
<string-array name="color_mode_labels">
@@ -100,5 +99,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Uxolo, lokho akusebenzanga. Zama futhi."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Zama futhi"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Le phrinta ayitholakali khona manje."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ayikwazi ukubonisa ukubuka kuqala"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Ilungiselela ukubuka kuqala…"</string>
</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 0210693..cd1d540 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -208,7 +208,7 @@
}
}
- CharSequence status = printJob.getStatus();
+ CharSequence status = printJob.getStatus(mContext.getPackageManager());
if (status != null) {
builder.setContentText(status);
} else {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 18160ff..9b1ab0e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
@@ -575,19 +576,35 @@
}
}
- /**
- * Set the status for a print job.
- *
- * @param printJobId ID of the print job to update
- * @param status the new status
- */
- public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
- synchronized (mLock) {
- getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status);
+ /**
+ * Set the status for a print job.
+ *
+ * @param printJobId ID of the print job to update
+ * @param status the new status
+ */
+ public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
+ synchronized (mLock) {
+ getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status);
- mNotificationController.onUpdateNotifications(mPrintJobs);
- }
- }
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+ }
+ }
+
+ /**
+ * Set the status for a print job.
+ *
+ * @param printJobId ID of the print job to update
+ * @param status the new status as a string resource
+ * @param appPackageName app package the resource belongs to
+ */
+ public void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
+ @Nullable CharSequence appPackageName) {
+ synchronized (mLock) {
+ getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status, appPackageName);
+
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+ }
+ }
public boolean hasActivePrintJobsLocked() {
final int printJobCount = mPrintJobs.size();
@@ -884,7 +901,7 @@
serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress));
}
- CharSequence status = printJob.getStatus();
+ CharSequence status = printJob.getStatus(getPackageManager());
if (!TextUtils.isEmpty(status)) {
serializer.attribute(null, ATTR_STATUS, status.toString());
}
@@ -1041,7 +1058,9 @@
try {
in = mStatePersistFile.openRead();
} catch (FileNotFoundException e) {
- Log.i(LOG_TAG, "No existing print spooler state.");
+ if (DEBUG_PERSISTENCE) {
+ Log.d(LOG_TAG, "No existing print spooler state.");
+ }
return;
}
try {
@@ -1433,6 +1452,13 @@
PrintSpoolerService.this.setStatus(printJobId, status);
}
+ @Override
+ public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
+ @NonNull CharSequence appPackageName) throws RemoteException {
+ PrintSpoolerService.this.setStatus(printJobId, status, appPackageName);
+ }
+
+
public PrintSpoolerService getService() {
return PrintSpoolerService.this;
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a2a3fd3..fe3ef1a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -812,4 +812,18 @@
<!-- HTTP proxy settings. The hint text field for the hostname. -->
<string name="proxy_hostname_hint" translatable="false">proxy.example.com</string>
+ <!-- Description for the screen zoom level that makes interface elements small. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_small">Small</string>
+ <!-- Description for the device's default screen zoom level. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_default">Default</string>
+ <!-- Description for the screen zoom level that makes interface elements large. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_large">Large</string>
+ <!-- Description for the screen zoom level that makes interface elements larger. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_very_large">Larger</string>
+ <!-- Description for the screen zoom level that makes interface elements largest. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_extremely_large">Largest</string>
+ <!-- Description for a custom screen zoom level. This shows the requested display
+ density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 3b818c8..0e3e0d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -60,7 +60,7 @@
public class StorageMeasurement {
private static final String TAG = "StorageMeasurement";
- private static final boolean LOCAL_LOGV = true;
+ private static final boolean LOCAL_LOGV = false;
static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
new file mode 100644
index 0000000..78d7c56
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 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.settingslib.display;
+
+import com.android.settingslib.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import java.util.Arrays;
+
+/**
+ * Utility methods for working with display density.
+ */
+public class DisplayDensityUtils {
+ private static final String LOG_TAG = "DisplayDensityUtils";
+
+ /** Minimum increment between density scales. */
+ private static final float MIN_SCALE_INTERVAL = 0.09f;
+
+ /** Minimum density scale. This is available on all devices. */
+ private static final float MIN_SCALE = 0.85f;
+
+ /** Maximum density scale. The actual scale used depends on the device. */
+ private static final float MAX_SCALE = 1.50f;
+
+ /** Summary used for "default" scale. */
+ public static final int SUMMARY_DEFAULT = R.string.screen_zoom_summary_default;
+
+ /** Summary used for "custom" scale. */
+ private static final int SUMMARY_CUSTOM = R.string.screen_zoom_summary_custom;
+
+ /**
+ * Summaries for scales smaller than "default" in order of smallest to
+ * largest.
+ */
+ private static final int[] SUMMARIES_SMALLER = new int[] {
+ R.string.screen_zoom_summary_small
+ };
+
+ /**
+ * Summaries for scales larger than "default" in order of smallest to
+ * largest.
+ */
+ private static final int[] SUMMARIES_LARGER = new int[] {
+ R.string.screen_zoom_summary_large,
+ R.string.screen_zoom_summary_very_large,
+ R.string.screen_zoom_summary_extremely_large,
+ };
+
+ /**
+ * Minimum allowed screen dimension, corresponds to resource qualifiers
+ * "small" or "sw320dp". This value must be at least the minimum screen
+ * size required by the CDD so that we meet developer expectations.
+ */
+ private static final int MIN_DIMENSION_DP = 320;
+
+ private final String[] mEntries;
+ private final int[] mValues;
+
+ private final int mDefaultDensity;
+ private final int mCurrentIndex;
+
+ public DisplayDensityUtils(Context context) {
+ final int defaultDensity = DisplayDensityUtils.getDefaultDisplayDensity(
+ Display.DEFAULT_DISPLAY);
+ if (defaultDensity <= 0) {
+ mEntries = null;
+ mValues = null;
+ mDefaultDensity = 0;
+ mCurrentIndex = -1;
+ return;
+ }
+
+ final Resources res = context.getResources();
+ final DisplayMetrics metrics = res.getDisplayMetrics();
+ final int currentDensity = metrics.densityDpi;
+ int currentDensityIndex = -1;
+
+ // Compute number of "larger" and "smaller" scales for this display.
+ final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels);
+ final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
+ final float maxScale = Math.min(MAX_SCALE, maxDensity / (float) defaultDensity);
+ final float minScale = MIN_SCALE;
+ final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL,
+ 0, SUMMARIES_LARGER.length);
+ final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
+ 0, SUMMARIES_SMALLER.length);
+
+ String[] entries = new String[1 + numSmaller + numLarger];
+ int[] values = new int[entries.length];
+ int curIndex = 0;
+
+ if (numSmaller > 0) {
+ final float interval = (1 - minScale) / numSmaller;
+ for (int i = numSmaller - 1; i >= 0; i--) {
+ // Round down to a multiple of 2 by truncating the low bit.
+ final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
+ if (currentDensity == density) {
+ currentDensityIndex = curIndex;
+ }
+ entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
+ values[curIndex] = density;
+ curIndex++;
+ }
+ }
+
+ if (currentDensity == defaultDensity) {
+ currentDensityIndex = curIndex;
+ }
+ values[curIndex] = defaultDensity;
+ entries[curIndex] = res.getString(SUMMARY_DEFAULT);
+ curIndex++;
+
+ if (numLarger > 0) {
+ final float interval = (maxScale - 1) / numLarger;
+ for (int i = 0; i < numLarger; i++) {
+ // Round down to a multiple of 2 by truncating the low bit.
+ final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
+ if (currentDensity == density) {
+ currentDensityIndex = curIndex;
+ }
+ values[curIndex] = density;
+ entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
+ curIndex++;
+ }
+ }
+
+ final int displayIndex;
+ if (currentDensityIndex >= 0) {
+ displayIndex = currentDensityIndex;
+ } else {
+ // We don't understand the current density. Must have been set by
+ // someone else. Make room for another entry...
+ int newLength = values.length + 1;
+ values = Arrays.copyOf(values, newLength);
+ values[curIndex] = currentDensity;
+
+ entries = Arrays.copyOf(entries, newLength);
+ entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
+
+ displayIndex = curIndex;
+ }
+
+ mDefaultDensity = defaultDensity;
+ mCurrentIndex = displayIndex;
+ mEntries = entries;
+ mValues = values;
+ }
+
+ public String[] getEntries() {
+ return mEntries;
+ }
+
+ public int[] getValues() {
+ return mValues;
+ }
+
+ public int getCurrentIndex() {
+ return mCurrentIndex;
+ }
+
+ public int getDefaultDensity() {
+ return mDefaultDensity;
+ }
+
+ /**
+ * Returns the default density for the specified display.
+ *
+ * @param displayId the identifier of the display
+ * @return the default density of the specified display, or {@code -1} if
+ * the display does not exist or the density could not be obtained
+ */
+ private static int getDefaultDisplayDensity(int displayId) {
+ try {
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ return wm.getInitialDisplayDensity(displayId);
+ } catch (RemoteException exc) {
+ return -1;
+ }
+ }
+
+ /**
+ * Asynchronously applies display density changes to the specified display.
+ *
+ * @param displayId the identifier of the display to modify
+ */
+ public static void clearForcedDisplayDensity(final int displayId) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ wm.clearForcedDisplayDensity(displayId);
+ } catch (RemoteException exc) {
+ Log.w(LOG_TAG, "Unable to clear forced display density setting");
+ }
+ }
+ });
+ }
+
+ /**
+ * Asynchronously applies display density changes to the specified display.
+ *
+ * @param displayId the identifier of the display to modify
+ * @param density the density to force for the specified display
+ */
+ public static void setForcedDisplayDensity(final int displayId, final int density) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ wm.setForcedDisplayDensity(displayId, density);
+ } catch (RemoteException exc) {
+ Log.w(LOG_TAG, "Unable to save forced display density setting");
+ }
+ }
+ });
+ }
+}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index c9582ea..c131fa5 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -409,10 +409,11 @@
final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
if (mProcesses.indexOfKey(id) >= 0) {
+ // BUGREPORT_STARTED intent was already received; ignore it.
Log.w(TAG, "ID " + id + " already watched");
- } else {
- mProcesses.put(info.id, info);
+ return true;
}
+ mProcesses.put(info.id, info);
// Take initial screenshot.
takeScreenshot(id, false);
updateProgress(info);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 589eac6..9839446 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -263,6 +263,14 @@
</intent-filter>
</activity>
+ <activity
+ android:name=".stackdivider.ForcedResizableInfoActivity"
+ android:theme="@style/ForcedResizableTheme"
+ android:excludeFromRecents="true"
+ android:stateNotNeeded="true"
+ android:exported="false">
+ </activity>
+
<!-- Callback for dismissing screenshot notification after a share target is picked -->
<receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
android:process=":screenshot"
diff --git a/packages/SystemUI/res/anim/forced_resizable_enter.xml b/packages/SystemUI/res/anim/forced_resizable_enter.xml
new file mode 100644
index 0000000..01b8fdb
--- /dev/null
+++ b/packages/SystemUI/res/anim/forced_resizable_enter.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="280" />
diff --git a/packages/SystemUI/res/anim/forced_resizable_exit.xml b/packages/SystemUI/res/anim/forced_resizable_exit.xml
new file mode 100644
index 0000000..6f316a7
--- /dev/null
+++ b/packages/SystemUI/res/anim/forced_resizable_exit.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="160"
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:zAdjustment="top"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/recents_from_search_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_search_launcher_enter.xml
deleted file mode 100644
index 7de4460..0000000
--- a/packages/SystemUI/res/anim/recents_from_search_launcher_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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.
-*/
--->
-<!-- Recents Activity -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="normal">
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/linear"
- android:duration="1"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml
deleted file mode 100644
index 23cedf8..0000000
--- a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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.
-*/
--->
-<!-- Launcher Activity -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/recents_from_launcher_exit_interpolator"
- android:duration="133"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml
deleted file mode 100644
index 657b216..0000000
--- a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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.
-*/
--->
-<!-- Launcher Activity -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="normal">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/recents_to_launcher_enter_interpolator"
- android:duration="133"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml
deleted file mode 100644
index 5182cab2..0000000
--- a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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.
-*/
--->
-<!-- Recents Activity -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/linear"
- android:duration="1"/>
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml b/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
new file mode 100644
index 0000000..89e4aac
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <objectAnimator
+ android:propertyName="translationY"
+ android:valueTo="10dp"
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+ <objectAnimator
+ android:propertyName="alpha"
+ android:valueTo="1.0"
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml b/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
new file mode 100644
index 0000000..c73fed6
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <objectAnimator
+ android:propertyName="translationY"
+ android:valueTo="0dp"
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+ <objectAnimator
+ android:propertyName="alpha"
+ android:valueTo="0.0"
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/layout/forced_resizable_activity.xml b/packages/SystemUI/res/layout/forced_resizable_activity.xml
new file mode 100644
index 0000000..df245bc
--- /dev/null
+++ b/packages/SystemUI/res/layout/forced_resizable_activity.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/forced_resizable_info_text"
+ android:textColor="#ffffff"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_history.xml b/packages/SystemUI/res/layout/recents_history.xml
deleted file mode 100644
index dc2da72..0000000
--- a/packages/SystemUI/res/layout/recents_history.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<com.android.systemui.recents.history.RecentsHistoryView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <android.support.v7.widget.RecyclerView
- android:id="@+id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-</com.android.systemui.recents.history.RecentsHistoryView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_history_clear_all_button.xml b/packages/SystemUI/res/layout/recents_history_clear_all_button.xml
deleted file mode 100644
index 05f0979..0000000
--- a/packages/SystemUI/res/layout/recents_history_clear_all_button.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:paddingTop="14dp"
- android:paddingBottom="14dp"
- android:drawableStart="@drawable/recents_dismiss_all_history"
- android:contentDescription="@string/recents_history_clear_all_button_label"
- android:textSize="14sp"
- android:textColor="#FFFFFF"
- android:textAllCaps="true"
- android:shadowColor="#99000000"
- android:shadowDx="0"
- android:shadowDy="2"
- android:shadowRadius="5"
- android:fontFamily="sans-serif-medium"
- android:background="?android:selectableItemBackground"
- android:visibility="invisible" />
diff --git a/packages/SystemUI/res/layout/recents_history_date.xml b/packages/SystemUI/res/layout/recents_history_date.xml
deleted file mode 100644
index 13c7dbe..0000000
--- a/packages/SystemUI/res/layout/recents_history_date.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
- android:gravity="start"
- android:textSize="14sp"
- android:textColor="#009688"
- android:textAllCaps="true"
- android:fontFamily="sans-serif-medium"
- android:background="?android:selectableItemBackground"
- android:alpha="1" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_history_task.xml b/packages/SystemUI/res/layout/recents_history_task.xml
deleted file mode 100644
index e92c24a..0000000
--- a/packages/SystemUI/res/layout/recents_history_task.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:orientation="horizontal"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:selectableItemBackground">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_gravity="center"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="12dp" />
- <TextView
- android:id="@+id/description"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:layout_gravity="end"
- android:paddingStart="16dp"
- android:gravity="start|center_vertical"
- android:textSize="14sp"
- android:textColor="#FFFFFF"
- android:fontFamily="sans-serif-medium" />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index f0bfebe..0f8c77c 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -19,8 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false"
- android:layoutDirection="rtl">
+ android:clipToPadding="false">
<com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
android:id="@+id/task_list"
android:layout_width="wrap_content"
@@ -29,20 +28,21 @@
android:clipToPadding="false"
android:descendantFocusability="beforeDescendants"
android:layout_marginTop="@dimen/recents_tv_gird_row_top_margin"
- android:focusable="true" />
+ android:focusable="true"
+ android:layoutDirection="rtl" />
+
<View
android:id="@+id/pip_shade"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
- android:background="#76000000"/>
+ android:background="#76000000" />
- <!-- Placeholder view to handle key events for PIP when it's focused.
- Size and positions will be adjusted to comply with the PIP bounds -->
- <View
- android:id="@+id/pip"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:visibility="gone"
- android:focusable="true" />
+ <include
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_marginTop="132dp"
+ layout="@layout/tv_pip_controls" />
+
</com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout/recents_history_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml
similarity index 95%
rename from packages/SystemUI/res/layout/recents_history_button.xml
rename to packages/SystemUI/res/layout/recents_stack_action_button.xml
index 538bad1..8783261 100644
--- a/packages/SystemUI/res/layout/recents_history_button.xml
+++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml
@@ -23,7 +23,7 @@
android:paddingEnd="14dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
- android:text="@string/recents_history_button_label"
+ android:text="@string/recents_stack_action_button_label"
android:textSize="14sp"
android:textColor="#FFFFFF"
android:textAllCaps="true"
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index fa65758..2b3c5df 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -14,28 +14,28 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- The layouts params are calculated in TaskViewHeader.java -->
<com.android.systemui.recents.views.TaskViewHeader
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_view_bar"
android:layout_width="match_parent"
- android:layout_height="@dimen/recents_task_bar_height"
+ android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal">
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/icon"
android:contentDescription="@string/recents_app_info_button_label"
- android:layout_width="@dimen/recents_task_view_header_icon_width"
- android:layout_height="@dimen/recents_task_view_header_icon_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:paddingTop="8dp"
android:paddingBottom="8dp"
- android:paddingStart="12dp"
- android:paddingEnd="16dp" />
+ android:paddingStart="16dp"
+ android:paddingEnd="12dp" />
<LinearLayout
+ android:id="@+id/title_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:layout_marginStart="56dp"
- android:layout_marginEnd="56dp"
android:orientation="vertical">
<TextView
android:id="@+id/title"
@@ -67,21 +67,20 @@
</LinearLayout>
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/move_task"
- android:layout_width="@dimen/recents_task_view_header_button_width"
- android:layout_height="@dimen/recents_task_view_header_button_height"
- android:layout_marginEnd="@dimen/recents_task_view_header_button_width"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
- android:padding="13dp"
+ android:padding="@dimen/recents_task_view_header_button_padding"
android:src="@drawable/star"
android:background="?android:selectableItemBackground"
android:alpha="0"
android:visibility="gone" />
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/dismiss_task"
- android:layout_width="@dimen/recents_task_view_header_button_width"
- android:layout_height="@dimen/recents_task_view_header_button_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
- android:padding="13dp"
+ android:padding="@dimen/recents_task_view_header_button_padding"
android:src="@drawable/recents_dismiss_light"
android:background="?android:selectableItemBackground"
android:alpha="0"
diff --git a/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml b/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
index 1becdab..cf09b1d 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
@@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- The layouts params are calculated in TaskViewHeader.java -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
@@ -20,20 +21,18 @@
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/app_icon"
android:contentDescription="@string/recents_app_info_button_label"
- android:layout_width="@dimen/recents_task_view_header_icon_width"
- android:layout_height="@dimen/recents_task_view_header_icon_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:paddingTop="8dp"
android:paddingBottom="8dp"
- android:paddingStart="12dp"
- android:paddingEnd="16dp" />
+ android:paddingStart="16dp"
+ android:paddingEnd="12dp" />
<TextView
android:id="@+id/app_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:layout_marginStart="56dp"
- android:layout_marginEnd="112dp"
android:textSize="16sp"
android:textColor="#ffffffff"
android:text="@string/recents_empty_message"
@@ -44,10 +43,10 @@
android:fadingEdge="horizontal" />
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/app_info"
- android:layout_width="@dimen/recents_task_view_header_button_width"
- android:layout_height="@dimen/recents_task_bar_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
- android:padding="13dp"
+ android:padding="@dimen/recents_task_view_header_button_padding"
android:background="?android:selectableItemBackground"
android:src="@drawable/recents_info_light" />
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
new file mode 100644
index 0000000..2e0c9e7
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.systemui.tv.pip.PipControlsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pip_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageView android:id="@+id/full_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_full_button" />
+
+ <TextView android:id="@+id/full_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_fullscreen"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-50dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageView android:id="@+id/close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_close_button" />
+
+ <TextView android:id="@+id/close_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_close"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE" />
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/play_pause"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-50dp"
+ android:orientation="vertical"
+ android:gravity="center" >
+
+ <ImageView android:id="@+id/play_pause_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_pause_button" />
+
+ <TextView android:id="@+id/play_pause_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_pause"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE" />
+ </LinearLayout>
+</com.android.systemui.tv.pip.PipControlsView>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 1fec49e..c2c83ff 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -26,85 +26,7 @@
android:gravity="top|center_horizontal"
android:clipChildren="false">
- <LinearLayout
- android:layout_width="34dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="3dp"
- android:orientation="vertical"
- android:gravity="center"
- android:clipChildren="false">
-
- <ImageView android:id="@+id/full_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:src="@drawable/tv_pip_full_button" />
-
- <TextView android:id="@+id/full_desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:visibility="invisible"
- android:text="@string/pip_fullscreen"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:clipChildren="false" />
- </LinearLayout>
-
- <LinearLayout android:id="@+id/play_pause"
- android:layout_width="34dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="3dp"
- android:layout_marginEnd="3dp"
- android:orientation="vertical"
- android:gravity="center"
- android:clipChildren="false">
-
- <ImageView android:id="@+id/play_pause_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:src="@drawable/tv_pip_pause_button" />
-
- <TextView android:id="@+id/play_pause_desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:visibility="invisible"
- android:text="@string/pip_pause"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:clipChildren="false" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="34dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="3dp"
- android:orientation="vertical"
- android:gravity="center"
- android:clipChildren="false">
-
- <ImageView android:id="@+id/close_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:src="@drawable/tv_pip_close_button" />
-
- <TextView android:id="@+id/close_desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:visibility="invisible"
- android:text="@string/pip_close"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:clipChildren="false" />
- </LinearLayout>
+ <include
+ layout="@layout/tv_pip_controls"
+ android:clipChildren="false" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index 1ba423b..c5c7e84 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -46,16 +46,17 @@
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="19dp"
+ android:layout_height="19dp"
android:src="@drawable/ic_fullscreen_white_24dp" />
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_pause_white_24dp" />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="19dp"
+ android:layout_height="19dp"
android:src="@drawable/ic_close_white" />
+ <ImageView
+ android:id="@+id/guide_button_play_pause"
+ android:layout_width="19dp"
+ android:layout_height="19dp"
+ android:src="@drawable/ic_pause_white_24dp" />
</LinearLayout>
</RelativeLayout>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 43e7bac..f7e2344 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -28,14 +28,4 @@
<!-- We have only space for one notification on phone landscape layouts. -->
<integer name="keyguard_max_notification_count">1</integer>
-
- <!-- Recents: The relative range of visible tasks from the current scroll position
- while the stack is focused. -->
- <item name="recents_layout_focused_range_min" format="float" type="integer">-3</item>
- <item name="recents_layout_focused_range_max" format="float" type="integer">2</item>
-
- <!-- Recents: The relative range of visible tasks from the current scroll position
- while the stack is not focused. -->
- <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
- <item name="recents_layout_unfocused_range_max" format="float" type="integer">1.5</item>
</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 585984c..26a81c8 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -22,9 +22,6 @@
<!-- Standard notification gravity -->
<integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
- <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
- <dimen name="recents_initial_bottom_peek_size">@dimen/recents_task_bar_height</dimen>
-
<dimen name="docked_divider_handle_width">2dp</dimen>
<dimen name="docked_divider_handle_height">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index db4da10..1f6bbd3 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -33,16 +33,6 @@
<!-- Set to true to enable the user switcher on the keyguard. -->
<bool name="config_keyguardUserSwitcher">true</bool>
- <!-- Recents: The relative range of visible tasks from the current scroll position
- while the stack is focused. -->
- <item name="recents_layout_focused_range_min" format="float" type="integer">-3</item>
- <item name="recents_layout_focused_range_max" format="float" type="integer">3</item>
-
- <!-- Recents: The relative range of visible tasks from the current scroll position
- while the stack is not focused. -->
- <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
- <item name="recents_layout_unfocused_range_max" format="float" type="integer">2.5</item>
-
<!-- Nav bar button default ordering/layout -->
<string name="config_navBarLayout" translatable="false">space;back,home,recent;menu_ime</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 66963c4..a2010be 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -96,15 +96,6 @@
<dimen name="qs_expand_margin">0dp</dimen>
- <!-- The top padding for the task stack. -->
- <dimen name="recents_stack_top_padding">40dp</dimen>
-
- <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
- <dimen name="recents_initial_bottom_peek_size">100dp</dimen>
-
- <!-- The side padding for the task stack. -->
- <dimen name="recents_stack_left_right_padding">64dp</dimen>
-
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_layout_width">488dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 8fe6be9..25e96c8 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -29,11 +29,6 @@
<!-- Bottom margin (from display edge) for status bar panels -->
<dimen name="panel_float">56dp</dimen>
- <!-- The radius of the rounded corners on a task view. -->
- <dimen name="recents_task_view_rounded_corners_radius">3dp</dimen>
- <!-- The radius of the rounded corners on a task view's shadow. -->
- <dimen name="recents_task_view_shadow_rounded_corners_radius">12dp</dimen>
-
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 076ba00..b9aa26b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -63,7 +63,7 @@
<!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
<color name="recents_task_bar_dark_text_color">#cc000000</color>
<!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
- <color name="recents_task_bar_light_icon_color">#ffeeeeee</color>
+ <color name="recents_task_bar_light_icon_color">#ccffffff</color>
<!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
<color name="recents_task_bar_dark_icon_color">#99000000</color>
<!-- The lock to task button background color. -->
@@ -151,6 +151,7 @@
<color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
+ <drawable name="forced_resizable_background">#80000000</drawable>
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
<color name="remote_input_hint">#99ffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4e1680d..622ae71 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -86,9 +86,6 @@
<bool name="config_dead_zone_flash">false</bool>
- <!-- Min alpha % that recent items will fade to while being dismissed -->
- <integer name="config_recent_item_min_alpha">3</integer>
-
<!-- Whether QuickSettings is in a phone landscape -->
<bool name="quick_settings_wide">false</bool>
@@ -165,9 +162,6 @@
<!-- The animation duration for subsequent scrolling the stack to a particular item. -->
<integer name="recents_subsequent_auto_advance_duration">1000</integer>
- <!-- The animation duration for entering and exiting the history. -->
- <integer name="recents_history_transition_duration">250</integer>
-
<!-- The delay to enforce between each alt-tab key press. -->
<integer name="recents_alt_tab_key_delay">200</integer>
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
new file mode 100644
index 0000000..22b7578
--- /dev/null
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+ when the PIP menu is shown in center. -->
+ <string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
+
+ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+ when the PIP is shown in Recents without focus. -->
+ <string translatable="false" name="pip_recents_bounds">"800 54 1120 234"</string>
+
+ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+ when the PIP is shown in Recents with focus. -->
+ <string translatable="false" name="pip_recents_focused_bounds">"775 54 1145 262"</string>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8cd2167..88230bc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -234,85 +234,6 @@
<!-- Default distance from each snap target that GlowPadView considers a "hit" -->
<dimen name="glowpadview_inner_radius">15dip</dimen>
- <!-- The size of the icon in the recents task view header. -->
- <dimen name="recents_task_view_header_icon_width">56dp</dimen>
- <dimen name="recents_task_view_header_icon_height">@dimen/recents_task_bar_height</dimen>
-
- <!-- The size of a button in the recents task view header. -->
- <dimen name="recents_task_view_header_button_width">@dimen/recents_task_bar_height</dimen>
- <dimen name="recents_task_view_header_button_height">@dimen/recents_task_bar_height</dimen>
-
- <!-- The radius of the rounded corners on a task view. -->
- <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
- <!-- The radius of the rounded corners on a task view's shadow. -->
- <dimen name="recents_task_view_shadow_rounded_corners_radius">12dp</dimen>
-
- <!-- The min translation in the Z index for the last task. -->
- <dimen name="recents_task_view_z_min">3dp</dimen>
-
- <!-- The max translation in the Z index for the last task. -->
- <dimen name="recents_task_view_z_max">24dp</dimen>
-
- <!-- The amount to translate when animating the removal of a task. -->
- <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
-
- <!-- The amount of highlight to make on each task view. -->
- <dimen name="recents_task_view_highlight">1dp</dimen>
-
- <!-- The amount to offset when animating into an affiliate group. -->
- <dimen name="recents_task_view_affiliate_group_enter_offset">32dp</dimen>
-
- <!-- The height of a task view bar. -->
- <dimen name="recents_task_bar_height">50dp</dimen>
-
- <!-- The height of the search bar space. -->
- <dimen name="recents_search_bar_space_height">64dp</dimen>
-
- <!-- The overscroll percentage allowed on the stack. -->
- <item name="recents_stack_overscroll_percentage" format="float" type="dimen">0.0875</item>
-
- <!-- The top padding for the task stack. -->
- <dimen name="recents_stack_top_padding">16dp</dimen>
-
- <!-- The side padding for the task stack. -->
- <dimen name="recents_stack_left_right_padding">16dp</dimen>
-
- <!-- The dimesnsions of the dismiss all recents button. -->
- <dimen name="recents_dismiss_all_button_size">48dp</dimen>
-
- <!-- The min alpha to apply to a task affiliation group color. -->
- <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
-
- <!-- The size of the lock-to-app button. -->
- <dimen name="recents_lock_to_app_size">56dp</dimen>
-
- <!-- The size of the lock-to-app button icon. -->
- <dimen name="recents_lock_to_app_icon_size">28dp</dimen>
-
- <!-- The amount to allow the stack to overscroll. -->
- <dimen name="recents_stack_overscroll">24dp</dimen>
-
- <!-- The size of the initial peek area at the top of the stack (below the status bar). -->
- <dimen name="recents_initial_top_peek_size">8dp</dimen>
-
- <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
- <dimen name="recents_initial_bottom_peek_size">100dp</dimen>
-
- <!-- The size of the peek area at the top of the stack (below the status bar). -->
- <dimen name="recents_layout_focused_top_peek_size">@dimen/recents_history_button_height</dimen>
-
- <!-- The size of each task peek area at the bottom of the stack (above the nav bar). -->
- <dimen name="recents_layout_focused_bottom_task_peek_size">16dp</dimen>
-
- <!-- The height of the history button. -->
- <dimen name="recents_history_button_height">48dp</dimen>
-
- <!-- The padding between freeform workspace tasks -->
- <dimen name="recents_freeform_workspace_task_padding">8dp</dimen>
-
- <!-- The offsets the tasks animate from when recents is launched while docking -->
- <dimen name="recents_task_view_launched_while_docking_offset">144dp</dimen>
-
<!-- Space reserved for the cards behind the top card in the bottom stack -->
<dimen name="bottom_stack_peek_amount">12dp</dimen>
@@ -639,4 +560,83 @@
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_layout_width">@dimen/match_parent</dimen>
+
+<!-- Recents Layout -->
+
+ <!-- The amount to inset the stack, specifically at the top and the other sides. We also
+ don't want this to change across configurations that Recents can be opened in, so we
+ define them statically for all display sizes. -->
+ <dimen name="recents_layout_min_margin">16dp</dimen>
+ <dimen name="recents_layout_top_margin_phone">16dp</dimen>
+ <dimen name="recents_layout_top_margin_tablet">32dp</dimen>
+ <dimen name="recents_layout_top_margin_tablet_xlarge">40dp</dimen>
+ <dimen name="recents_layout_bottom_margin">16dp</dimen>
+ <dimen name="recents_layout_side_margin_phone">16dp</dimen>
+ <dimen name="recents_layout_side_margin_tablet">48dp</dimen>
+ <dimen name="recents_layout_side_margin_tablet_xlarge">64dp</dimen>
+
+ <!-- The height between the top margin and the top of the focused task. -->
+ <dimen name="recents_layout_top_peek_size">56dp</dimen>
+ <!-- The height between the bottom margin and the top of task in front of the focused task. -->
+ <dimen name="recents_layout_bottom_peek_size">56dp</dimen>
+
+ <!-- The offset from the top and bottom of the stack of the focused task. The bottom offset
+ will be additionally offset by the bottom system insets since it goes under the nav bar
+ in certain orientations. -->
+ <dimen name="recents_layout_initial_top_offset_phone_port">128dp</dimen>
+ <dimen name="recents_layout_initial_bottom_offset_phone_port">80dp</dimen>
+ <dimen name="recents_layout_initial_top_offset_phone_land">72dp</dimen>
+ <dimen name="recents_layout_initial_bottom_offset_phone_land">72dp</dimen>
+ <dimen name="recents_layout_initial_top_offset_tablet">160dp</dimen>
+ <dimen name="recents_layout_initial_bottom_offset_tablet">112dp</dimen>
+
+ <!-- The min/max translationZ for the tasks in the stack. -->
+ <dimen name="recents_layout_z_min">3dp</dimen>
+ <dimen name="recents_layout_z_max">24dp</dimen>
+
+ <!-- The margin between the freeform and stack. We also don't want this to change across
+ configurations that Recents can be opened in, so we define them statically for all
+ display sizes. -->
+ <dimen name="recents_freeform_layout_bottom_margin">16dp</dimen>
+
+ <!-- The padding between each freeform task. -->
+ <dimen name="recents_freeform_layout_task_padding">8dp</dimen>
+
+<!-- Recents Views -->
+
+ <!-- The height of a task view bar. This has to be large enough to cover the action bar
+ height in either orientation at this smallest width. -->
+ <dimen name="recents_task_view_header_height">56dp</dimen>
+ <dimen name="recents_task_view_header_height_tablet_land">64dp</dimen>
+
+ <!-- The padding of a button in the recents task view header. -->
+ <dimen name="recents_task_view_header_button_padding">16dp</dimen>
+ <dimen name="recents_task_view_header_button_padding_tablet_land">20dp</dimen>
+
+ <!-- The radius of the rounded corners on a task view and its shadow (which can be larger
+ to create a softer corner effect. -->
+ <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
+ <dimen name="recents_task_view_shadow_rounded_corners_radius">6dp</dimen>
+
+ <!-- The amount of highlight to make on each task view. -->
+ <dimen name="recents_task_view_highlight">1dp</dimen>
+
+ <!-- The size of the lock-to-app button and its icon. -->
+ <dimen name="recents_lock_to_app_size">56dp</dimen>
+ <dimen name="recents_lock_to_app_icon_size">28dp</dimen>
+
+ <!-- The amount of overscroll allowed when flinging to the end of the stack. -->
+ <dimen name="recents_fling_overscroll_distance">24dp</dimen>
+
+ <!-- The min alpha to apply to a task affiliation group color. -->
+ <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
+
+ <!-- The amount to offset when animating into an affiliate group. -->
+ <dimen name="recents_task_stack_animation_affiliate_enter_offset">32dp</dimen>
+
+ <!-- The offsets the tasks animate from when recents is launched while docking -->
+ <dimen name="recents_task_stack_animation_launched_while_docking_offset">144dp</dimen>
+
+ <!-- The amount to translate when animating the removal of a task. -->
+ <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index c60c245..20cd330 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -17,4 +17,5 @@
<integer name="item_scale_anim_duration">150</integer>
<integer name="dismiss_short_duration">200</integer>
<integer name="dismiss_long_duration">400</integer>
-</resources>
\ No newline at end of file
+ <integer name="recents_tv_pip_focus_anim_duration">200</integer>
+</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 091ba51..25ecd888 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -731,10 +731,8 @@
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
<!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
<string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
- <!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
- <string name="recents_history_button_label">History</string>
- <!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
- <string name="recents_history_clear_all_button_label">Clear</string>
+ <!-- Recents: Stack action button string. [CHAR LIMIT=NONE] -->
+ <string name="recents_stack_action_button_label">Clear all</string>
<!-- Recents: Non-dockable task drag message. [CHAR LIMIT=NONE] -->
<string name="recents_drag_non_dockable_task_message">This app does not support multi-window</string>
<!-- Recents: Non-dockable task launch sub header. [CHAR LIMIT=NONE] -->
@@ -1329,6 +1327,59 @@
<!-- Summary of switch for battery saver [CHAR LIMIT=NONE] -->
<string name="battery_detail_switch_summary">Reduces performance and background data</string>
+ <!-- Name used for certain Keyboard keys on gamepads, e.g. "Button L1". -->
+ <string name="keyboard_key_button_template">Button <xliff:g id="name">%1$s</xliff:g></string>
+ <!-- Name used to refer to the "Home" key on the keyboard. -->
+ <string name="keyboard_key_home">Home</string>
+ <!-- Name used to refer to the "Back" key on the keyboard. -->
+ <string name="keyboard_key_back">Back</string>
+ <!-- Name used to refer to the "Up" arrow key on the keyboard. -->
+ <string name="keyboard_key_dpad_up">Up</string>
+ <!-- Name used to refer to the "Down" arrow key on the keyboard. -->
+ <string name="keyboard_key_dpad_down">Down</string>
+ <!-- Name used to refer to the "Left" arrow key on the keyboard. -->
+ <string name="keyboard_key_dpad_left">Left</string>
+ <!-- Name used to refer to the "Right" arrow key on the keyboard. -->
+ <string name="keyboard_key_dpad_right">Right</string>
+ <!-- Name used to refer to the "Center" arrow key on the keyboard. -->
+ <string name="keyboard_key_dpad_center">Center</string>
+ <!-- Name used to refer to the "Tab" key on the keyboard. -->
+ <string name="keyboard_key_tab">Tab</string>
+ <!-- Name used to refer to the "Space" key on the keyboard. -->
+ <string name="keyboard_key_space">Space</string>
+ <!-- Name used to refer to the "Enter" key on the keyboard. -->
+ <string name="keyboard_key_enter">Enter</string>
+ <!-- Name used to refer to the "Backspace" key on the keyboard. -->
+ <string name="keyboard_key_backspace">Backspace</string>
+ <!-- Name used to refer to the "Play/Pause" media key on the keyboard. -->
+ <string name="keyboard_key_media_play_pause">Play/Pause</string>
+ <!-- Name used to refer to the "Stop" media key on the keyboard. -->
+ <string name="keyboard_key_media_stop">Stop</string>
+ <!-- Name used to refer to the "Next" media key on the keyboard. -->
+ <string name="keyboard_key_media_next">Next</string>
+ <!-- Name used to refer to the "Previous" media key on the keyboard. -->
+ <string name="keyboard_key_media_previous">Previous</string>
+ <!-- Name used to refer to the "Rewind" media key on the keyboard. -->
+ <string name="keyboard_key_media_rewind">Rewind</string>
+ <!-- Name used to refer to the "Fast Forward" media key on the keyboard. -->
+ <string name="keyboard_key_media_fast_forward">Fast Forward</string>
+ <!-- Name used to refer to the "Page Up" key on the keyboard. -->
+ <string name="keyboard_key_page_up">Page Up</string>
+ <!-- Name used to refer to the "Page Down" key on the keyboard. -->
+ <string name="keyboard_key_page_down">Page Down</string>
+ <!-- Name used to refer to the "Delete" key on the keyboard. -->
+ <string name="keyboard_key_forward_del">Delete</string>
+ <!-- Name used to refer to the "Home" move key on the keyboard. -->
+ <string name="keyboard_key_move_home">Home</string>
+ <!-- Name used to refer to the "End" move key on the keyboard. -->
+ <string name="keyboard_key_move_end">End</string>
+ <!-- Name used to refer to the "Insert" key on the keyboard. -->
+ <string name="keyboard_key_insert">Insert</string>
+ <!-- Name used to refer to the "Num Lock" key on the keyboard. -->
+ <string name="keyboard_key_num_lock">Num Lock</string>
+ <!-- Name used to refer to keys on the numeric pad of the keyboard, e.g. "Numpad 9". -->
+ <string name="keyboard_key_numpad_template">Numpad <xliff:g id="name">%1$s</xliff:g></string>
+
<!-- User visible title for the system-wide keyboard shortcuts list. -->
<string name="keyboard_shortcut_group_system">System</string>
<!-- User visible title for the keyboard shortcut that takes the user to the home screen. -->
@@ -1479,4 +1530,6 @@
<!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
<string name="accessibility_action_divider_move_right">Move right</string>
+ <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
+ <string name="forced_resizable_info_text">App may not work with multi-window</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 21ad216..2b134af 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -40,6 +40,18 @@
<item name="android:windowBackground">@android:color/black</item>
</style>
+ <!-- Theme used for the activity that shows when the system forced an app to be resizable -->
+ <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
+ <item name="android:windowBackground">@drawable/forced_resizable_background</item>
+ <item name="android:statusBarColor">@color/transparent</item>
+ <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
+ </style>
+
+ <style name="Animation.ForcedResizable" parent="@android:style/Animation">
+ <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
+ <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
+ </style>
+
<style name="TextAppearance.StatusBar.HeadsUp"
parent="@*android:style/TextAppearance.StatusBar">
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 9f2745b..28ed84f 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -30,8 +30,6 @@
@Retention(RetentionPolicy.SOURCE)
@StringDef({
- Key.OVERVIEW_SEARCH_APP_WIDGET_ID,
- Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
Key.DEBUG_MODE_ENABLED,
Key.HOTSPOT_TILE_LAST_USED,
@@ -51,8 +49,6 @@
Key.QS_NIGHT_ADDED,
})
public @interface Key {
- String OVERVIEW_SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
- String OVERVIEW_SEARCH_APP_WIDGET_PACKAGE = "searchAppWidgetPackage";
String OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME = "OverviewLastStackTaskActiveTime";
String DEBUG_MODE_ENABLED = "debugModeEnabled";
String HOTSPOT_TILE_LAST_USED = "HotspotTileLastUsed";
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index a08b2c1..8f79bda 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -274,10 +274,7 @@
mWatchLongPress = new Runnable() {
@Override
public void run() {
- float pos = getPos(ev);
- float delta = pos - mInitialTouchPos;
- if (mCurrView != null && !mLongPressSent
- && Math.abs(delta) < mPagingTouchSlop) {
+ if (mCurrView != null && !mLongPressSent) {
mLongPressSent = true;
mCurrView.sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 6d8b476..51efbf0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -36,12 +36,12 @@
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.SecurityController;
+import static android.provider.Settings.ACTION_VPN_SETTINGS;
+
public class QSFooter implements OnClickListener, DialogInterface.OnClickListener {
protected static final String TAG = "QSFooter";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
-
private final View mRootView;
private final TextView mFooterText;
private final ImageView mFooterIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 070b395..003379f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -27,6 +27,7 @@
public static final int DismissSourceKeyboard = 0;
public static final int DismissSourceSwipeGesture = 1;
public static final int DismissSourceHeaderButton = 2;
+ @Deprecated
public static final int DismissSourceHistorySwipeGesture = 3;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index da07aec..287bb22 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -180,7 +180,7 @@
@Override
public void start() {
sDebugFlags = new RecentsDebugFlags(mContext);
- sSystemServicesProxy = new SystemServicesProxy(mContext);
+ sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
sTaskLoader = new RecentsTaskLoader(mContext);
sConfiguration = new RecentsConfiguration(mContext);
mHandler = new Handler();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index aab45b5d..fe7ac71 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -18,10 +18,7 @@
import android.app.Activity;
import android.app.ActivityOptions;
-import android.app.SearchManager;
import android.app.TaskStackBuilder;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -37,25 +34,24 @@
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.HideHistoryEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
-import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
@@ -77,6 +73,7 @@
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.AnimationProps;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -89,9 +86,6 @@
private final static String TAG = "RecentsActivity";
private final static boolean DEBUG = false;
- private final static String KEY_SAVED_STATE_HISTORY_VISIBLE =
- "saved_instance_state_history_visible";
-
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
private RecentsPackageMonitor mPackageMonitor;
@@ -104,11 +98,6 @@
private RecentsView mRecentsView;
private SystemBarScrimViews mScrimViews;
- // Search AppWidget
- private AppWidgetProviderInfo mSearchWidgetInfo;
- private RecentsAppWidgetHost mAppWidgetHost;
- private RecentsAppWidgetHostView mSearchWidgetHostView;
-
// Runnables to finish the Recents activity
private Intent mHomeIntent;
@@ -138,17 +127,10 @@
@Override
public void run() {
try {
- RecentsActivityLaunchState launchState =
- Recents.getConfiguration().getLaunchState();
ActivityOptions opts = mOpts;
if (opts == null) {
opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
- launchState.launchedFromSearchHome ?
- R.anim.recents_to_search_launcher_enter :
- R.anim.recents_to_launcher_enter,
- launchState.launchedFromSearchHome ?
- R.anim.recents_to_search_launcher_exit :
- R.anim.recents_to_launcher_exit);
+ R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
}
startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
@@ -167,27 +149,11 @@
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
// When the screen turns off, dismiss Recents to Home
dismissRecentsToHomeIfVisible(false);
- } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
- // When the search activity changes, update the search widget view
- SystemServicesProxy ssp = Recents.getSystemServices();
- mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(context, mAppWidgetHost);
- refreshSearchWidgetView();
}
}
};
/**
- * Dismisses the history view back into the stack view.
- */
- boolean dismissHistory() {
- if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
- EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
- return true;
- }
- return false;
- }
-
- /**
* Dismisses recents if we are already visible and the intent is to toggle the recents view.
*/
boolean dismissRecentsToFocusedTask(int logCategory) {
@@ -285,10 +251,7 @@
// Register this activity with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
- // Initialize the widget host (the host id is static and does not change)
- if (RecentsDebugFlags.Static.EnableSearchBar) {
- mAppWidgetHost = new RecentsAppWidgetHost(this, RecentsAppWidgetHost.HOST_ID);
- }
+ // Initialize the package monitor
mPackageMonitor = new RecentsPackageMonitor();
mPackageMonitor.register(this);
@@ -317,18 +280,15 @@
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- // Bind the search app widget when we first start up
- if (RecentsDebugFlags.Static.EnableSearchBar) {
- mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
- }
-
// Register the broadcast receiver to handle messages when the screen is turned off
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
- if (RecentsDebugFlags.Static.EnableSearchBar) {
- filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
- }
registerReceiver(mSystemBroadcastReceiver, filter);
+
+ getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
+
+ // Reload the stack view
+ reloadStackView();
}
@Override
@@ -341,15 +301,17 @@
}
@Override
- public void onEnterAnimationComplete() {
- super.onEnterAnimationComplete();
- EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ // Reload the stack view
+ reloadStackView();
}
- @Override
- protected void onResume() {
- super.onResume();
-
+ /**
+ * Reloads the stack views upon launching Recents.
+ */
+ private void reloadStackView() {
// If the Recents component has preloaded a load plan, then use that to prevent
// reconstructing the task stack
RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -372,38 +334,21 @@
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
- mRecentsView.onResume(mIsVisible, stack);
+ mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
+ mRecentsView.updateStack(stack);
- // Animate the SystemUI scrims into view
- Task launchTarget = stack.getLaunchTarget();
- int taskCount = stack.getTaskCount();
- int launchTaskIndexInStack = launchTarget != null
- ? stack.indexOfStackTask(launchTarget)
- : 0;
- boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
+ // Update the nav bar scrim, but defer the animation until the enter-window event
boolean animateNavBarScrim = !launchState.launchedWhileDocking;
- mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
+ updateNavBarScrim(animateNavBarScrim, null);
- // If this is a new instance from a configuration change, then we have to manually trigger
- // the enter animation state, or if recents was relaunched by AM, without going through
- // the normal mechanisms
+ // If this is a new instance relaunched by AM, without going through the normal mechanisms,
+ // then we have to manually trigger the enter animation state
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromApp;
- if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
+ if (wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
-
- @Override
- public boolean onPreDraw() {
- mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
- EventBus.getDefault().post(new RecentsDrawnEvent());
- return true;
- }
- });
-
// Keep track of whether we launched from the nav bar button or via alt-tab
if (launchState.launchedWithAltTab) {
MetricsLogger.count(this, "overview_trigger_alttab", 1);
@@ -413,6 +358,10 @@
// Keep track of whether we launched from an app or from home
if (launchState.launchedFromApp) {
+ Task launchTarget = stack.getLaunchTarget();
+ int launchTaskIndexInStack = launchTarget != null
+ ? stack.indexOfStackTask(launchTarget)
+ : 0;
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
@@ -421,6 +370,7 @@
}
// Keep track of the total stack task count
+ int taskCount = mRecentsView.getStack().getTaskCount();
MetricsLogger.histogram(this, "overview_task_count", taskCount);
// After we have resumed, set the visible state until the next onStop() call
@@ -428,6 +378,29 @@
}
@Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Notify of the next draw
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+ EventBus.getDefault().post(new RecentsDrawnEvent());
+ return true;
+ }
+ });
+ }
+
+ @Override
protected void onPause() {
super.onPause();
@@ -439,18 +412,40 @@
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- EventBus.getDefault().send(new ConfigurationChangedEvent());
+ // Update the nav bar for the current orientation
+ updateNavBarScrim(false /* animateNavBarScrim */, AnimationProps.IMMEDIATE);
+
+ EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */));
+ }
+
+ @Override
+ public void onMultiWindowChanged(boolean inMultiWindow) {
+ super.onMultiWindowChanged(inMultiWindow);
+ EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */));
+
+ if (mRecentsView != null) {
+ // Reload the task stack completely
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+ loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
+
+ RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+ loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+ loader.loadTasks(this, loadPlan, loadOpts);
+
+ mRecentsView.updateStack(loadPlan.getTaskStack());
+ }
+
+ EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
}
@Override
protected void onStop() {
super.onStop();
- // Only hide the history if Recents is completely hidden
- if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
- EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
- }
-
// Notify that recents is now hidden
mIsVisible = false;
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
@@ -479,11 +474,6 @@
// Unregister any broadcast receivers for the task loader
mPackageMonitor.unregister();
- // Stop listening for widget package changes if there was one bound
- if (RecentsDebugFlags.Static.EnableSearchBar) {
- mAppWidgetHost.stopListening();
- }
-
EventBus.getDefault().unregister(this);
}
@@ -500,23 +490,6 @@
}
@Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if (RecentsDebugFlags.Static.EnableHistory) {
- outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, mRecentsView.isHistoryVisible());
- }
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- if (RecentsDebugFlags.Static.EnableHistory &&
- savedInstanceState.getBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, false)) {
- EventBus.getDefault().send(new ShowHistoryEvent());
- }
- }
-
- @Override
public void onTrimMemory(int level) {
RecentsTaskLoader loader = Recents.getTaskLoader();
if (loader != null) {
@@ -525,28 +498,6 @@
}
@Override
- public void onMultiWindowChanged(boolean inMultiWindow) {
- super.onMultiWindowChanged(inMultiWindow);
- EventBus.getDefault().send(new ConfigurationChangedEvent());
-
- // Reload the task stack completely
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
- loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
-
- RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
- loader.loadTasks(this, loadPlan, loadOpts);
-
- mRecentsView.onResume(mIsVisible, loadPlan.getTaskStack());
-
- EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
- }
-
- @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_TAB: {
@@ -611,39 +562,35 @@
/**** EventBus events ****/
public final void onBusEvent(ToggleRecentsEvent event) {
- if (!RecentsDebugFlags.Static.EnableHistory || !dismissHistory()) {
- RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (launchState.launchedFromHome) {
- dismissRecentsToHome(true /* animateTaskViews */);
- } else {
- dismissRecentsToLaunchTargetTaskOrHome();
- }
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ if (launchState.launchedFromHome) {
+ dismissRecentsToHome(true /* animateTaskViews */);
+ } else {
+ dismissRecentsToLaunchTargetTaskOrHome();
}
}
public final void onBusEvent(IterateRecentsEvent event) {
- if (!RecentsDebugFlags.Static.EnableHistory || !dismissHistory()) {
- final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- // Start dozing after the recents button is clicked
- int timerIndicatorDuration = 0;
- if (debugFlags.isFastToggleRecentsEnabled()) {
- timerIndicatorDuration = getResources().getInteger(
- R.integer.recents_subsequent_auto_advance_duration);
+ // Start dozing after the recents button is clicked
+ int timerIndicatorDuration = 0;
+ if (debugFlags.isFastToggleRecentsEnabled()) {
+ timerIndicatorDuration = getResources().getInteger(
+ R.integer.recents_subsequent_auto_advance_duration);
- mIterateTrigger.setDozeDuration(timerIndicatorDuration);
- if (!mIterateTrigger.isDozing()) {
- mIterateTrigger.startDozing();
- } else {
- mIterateTrigger.poke();
- }
+ mIterateTrigger.setDozeDuration(timerIndicatorDuration);
+ if (!mIterateTrigger.isDozing()) {
+ mIterateTrigger.startDozing();
+ } else {
+ mIterateTrigger.poke();
}
-
- // Focus the next task
- EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration));
-
- MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE);
}
+
+ // Focus the next task
+ EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration));
+
+ MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE);
}
public final void onBusEvent(UserInteractionEvent event) {
@@ -658,17 +605,7 @@
dismissRecentsToFocusedTaskOrHome();
}
} else if (event.triggeredFromHomeKey) {
- // Otherwise, dismiss Recents to Home
- if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
- // If the history view is visible, then just cross-fade home
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
- R.anim.recents_to_launcher_enter,
- R.anim.recents_to_launcher_exit);
- dismissRecentsToHome(false /* animate */, opts);
-
- } else {
- dismissRecentsToHome(true /* animateTaskViews */);
- }
+ dismissRecentsToHome(true /* animateTaskViews */);
// Cancel any pending dozes
EventBus.getDefault().send(mUserInteractionEvent);
@@ -677,23 +614,6 @@
}
}
- public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
- // Try and start the enter animation (or restart it on configuration changed)
- if (RecentsDebugFlags.Static.EnableSearchBar) {
- if (mSearchWidgetInfo != null) {
- event.addPostAnimationCallback(new Runnable() {
- @Override
- public void run() {
- // Start listening for widget package changes if there is one bound
- if (mAppWidgetHost != null) {
- mAppWidgetHost.startListening();
- }
- }
- });
- }
- }
- }
-
public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
@@ -708,6 +628,11 @@
mRecentsView.invalidate();
}
+ public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+ mRecentsView.invalidate();
+ }
+
public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
int launchToTaskId = launchState.launchedToTaskId;
@@ -719,10 +644,6 @@
}
}
- public final void onBusEvent(AppWidgetProviderChangedEvent event) {
- refreshSearchWidgetView();
- }
-
public final void onBusEvent(ShowApplicationInfoEvent event) {
// Create a new task stack with the application info details activity
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
@@ -785,24 +706,6 @@
mIgnoreAltTabRelease = true;
}
- private void refreshSearchWidgetView() {
- if (mSearchWidgetInfo != null) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- int searchWidgetId = ssp.getSearchAppWidgetId(this);
- mSearchWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
- this, searchWidgetId, mSearchWidgetInfo);
- Bundle opts = new Bundle();
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
- mSearchWidgetHostView.updateAppWidgetOptions(opts);
- // Set the padding to 0 for this search widget
- mSearchWidgetHostView.setPadding(0, 0, 0, 0);
- mRecentsView.setSearchBar(mSearchWidgetHostView);
- } else {
- mRecentsView.setSearchBar(null);
- }
- }
-
@Override
public boolean onPreDraw() {
mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
@@ -816,4 +719,18 @@
});
return true;
}
+
+ /**
+ * Updates the nav bar scrim.
+ */
+ private void updateNavBarScrim(boolean animateNavBarScrim, AnimationProps animation) {
+ // Animate the SystemUI scrims into view
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ int taskCount = mRecentsView.getStack().getTaskCount();
+ boolean hasNavBarScrim = (taskCount > 0) && !ssp.hasTransposedNavBar();
+ mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
+ if (animateNavBarScrim && animation != null) {
+ mScrimViews.animateNavBarScrimVisibility(true, animation);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index ec4820a..77f7739 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -31,9 +31,6 @@
public boolean launchedFromApp;
public boolean launchedFromAppDocked;
public boolean launchedFromHome;
- public boolean launchedFromSearchHome;
- public boolean launchedReuseTaskStackViews;
- public boolean launchedHasConfigurationChanged;
public boolean launchedViaDragGesture;
public boolean launchedWhileDocking;
public int launchedToTaskId;
@@ -42,12 +39,10 @@
public void reset() {
launchedFromHome = false;
- launchedFromSearchHome = false;
launchedFromApp = false;
launchedFromAppDocked = false;
launchedToTaskId = -1;
launchedWithAltTab = false;
- launchedHasConfigurationChanged = false;
launchedViaDragGesture = false;
launchedWhileDocking = false;
}
@@ -55,10 +50,6 @@
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
- // Reset this flag on configuration change to ensure that we recreate new task views
- launchedReuseTaskStackViews = false;
- // Set this flag to indicate that the configuration has changed since Recents last launched
- launchedHasConfigurationChanged = true;
launchedViaDragGesture = false;
launchedWhileDocking = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
deleted file mode 100644
index 318c69f..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.recents;
-
-import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
-
-/** Our special app widget host for the Search widget */
-public class RecentsAppWidgetHost extends AppWidgetHost {
-
- public static final int HOST_ID = 1024;
-
- boolean mIsListening;
-
- public RecentsAppWidgetHost(Context context, int hostId) {
- super(context, hostId);
- }
-
- public void startListening() {
- if (!mIsListening) {
- mIsListening = true;
- super.startListening();
- }
- }
-
- @Override
- public void stopListening() {
- if (mIsListening) {
- mIsListening = false;
- super.stopListening();
- }
- }
-
- @Override
- protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
- AppWidgetProviderInfo appWidget) {
- return new RecentsAppWidgetHostView(context);
- }
-
- /**
- * Note: this is only called for packages that have updated, not removed.
- */
- @Override
- protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
- super.onProviderChanged(appWidgetId, appWidgetInfo);
- if (mIsListening) {
- EventBus.getDefault().send(new AppWidgetProviderChangedEvent());
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHostView.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHostView.java
deleted file mode 100644
index 672d602..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHostView.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2015 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.recents;
-
-import android.appwidget.AppWidgetHostView;
-import android.content.Context;
-import android.view.View;
-import android.widget.RemoteViews;
-
-public class RecentsAppWidgetHostView extends AppWidgetHostView {
-
- private Context mContext;
- private int mPreviousOrientation;
-
- public RecentsAppWidgetHostView(Context context) {
- super(context);
- mContext = context;
- }
-
- @Override
- public void updateAppWidget(RemoteViews remoteViews) {
- // Store the orientation in which the widget was inflated
- updateLastInflationOrientation();
- super.updateAppWidget(remoteViews);
- }
-
- @Override
- protected View getErrorView() {
- // Just return an empty view as the error view when failing to inflate the Recents search
- // bar widget (this is mainly to catch the case where we try and inflate the widget view
- // while the search provider is updating)
- return new View(mContext);
- }
-
- /**
- * Updates the last orientation that this widget was inflated.
- */
- private void updateLastInflationOrientation() {
- mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
- }
-
- /**
- * @return whether the search widget was updated while Recents was in a different orientation
- * in the background.
- */
- public boolean isReinflateRequired() {
- // Re-inflate is required if the orientation has changed since last inflated.
- int orientation = mContext.getResources().getConfiguration().orientation;
- if (mPreviousOrientation != orientation) {
- return true;
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index eec0411..40613f0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -32,13 +32,6 @@
private static final int LARGE_SCREEN_MIN_DP = 600;
private static final int XLARGE_SCREEN_MIN_DP = 720;
- // Variables that are used for global calculations
- private static final float STACK_SIDE_PADDING_PHONES_PCT = 0.03333f;
- private static final float STACK_SIZE_PADDING_TABLETS_PCT = 0.075f;
- private static final float STACK_SIZE_PADDING_LARGE_TABLETS_PCT = 0.15f;
- private static final int SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS = 64;
- private static final int SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS = 72;
-
/** Levels of svelte in increasing severity/austerity. */
// No svelting.
public static final int SVELTE_NONE = 0;
@@ -54,13 +47,6 @@
// Launch states
public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState();
- // TODO: Values determined by the current context, needs to be refactored into something that is
- // agnostic of the activity context, but still calculable from the Recents component for
- // the transition into recents
- boolean hasTransposedSearchBar;
- boolean hasTransposedNavBar;
- public float taskStackWidthPaddingPct;
-
// Since the positions in Recents has to be calculated globally (before the RecentsActivity
// starts), we need to calculate some resource values ourselves, instead of relying on framework
// resources.
@@ -71,7 +57,6 @@
/** Misc **/
public boolean fakeShadows;
public int svelteLevel;
- public int searchBarSpaceHeightPx;
public RecentsConfiguration(Context context) {
// Load only resources that can not change after the first load either through developer
@@ -82,28 +67,10 @@
fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
svelteLevel = res.getInteger(R.integer.recents_svelte_level);
- float density = context.getResources().getDisplayMetrics().density;
+ float screenDensity = context.getResources().getDisplayMetrics().density;
smallestWidth = ssp.getDeviceSmallestWidth();
- isLargeScreen = smallestWidth >= (int) (density * LARGE_SCREEN_MIN_DP);
- isXLargeScreen = smallestWidth >= (int) (density * XLARGE_SCREEN_MIN_DP);
- searchBarSpaceHeightPx = isLargeScreen ?
- (int) (density * SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS) :
- (int) (density * SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS);
- if (isLargeScreen) {
- taskStackWidthPaddingPct = STACK_SIZE_PADDING_TABLETS_PCT;
- } else if (isXLargeScreen) {
- taskStackWidthPaddingPct = STACK_SIZE_PADDING_LARGE_TABLETS_PCT;
- } else {
- taskStackWidthPaddingPct = STACK_SIDE_PADDING_PHONES_PCT;
- }
- }
-
- /**
- * Updates the configuration based on the current state of the system
- */
- void update(Rect systemInsets) {
- hasTransposedNavBar = systemInsets.right > 0;
- hasTransposedSearchBar = systemInsets.right > 0;
+ isLargeScreen = smallestWidth >= (int) (screenDensity * LARGE_SCREEN_MIN_DP);
+ isXLargeScreen = smallestWidth >= (int) (screenDensity * XLARGE_SCREEN_MIN_DP);
}
/**
@@ -121,40 +88,4 @@
public void updateOnConfigurationChange() {
mLaunchState.updateOnConfigurationChange();
}
-
- /**
- * Returns the task stack bounds in the current orientation. These bounds do not account for
- * the system insets.
- */
- public void getTaskStackBounds(Rect windowBounds, int topInset,
- int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
- if (hasTransposedNavBar) {
- // In landscape phones, the search bar appears on the left, but we overlay it on top
- taskStackBounds.set(windowBounds.left, windowBounds.top + topInset,
- windowBounds.right - rightInset, windowBounds.bottom);
- } else {
- // In portrait, the search bar appears on the top (which already has the inset)
- int top = searchBarBounds.isEmpty() ? topInset : 0;
- taskStackBounds.set(windowBounds.left, windowBounds.top + searchBarBounds.bottom + top,
- windowBounds.right - rightInset, windowBounds.bottom);
- }
- }
-
- /**
- * Returns the search bar bounds in the current orientation. These bounds do not account for
- * the system insets.
- */
- public void getSearchBarBounds(Rect windowBounds, int topInset, Rect searchBarSpaceBounds) {
- // Return empty rects if search is not enabled
- int searchBarSize = searchBarSpaceHeightPx;
- if (hasTransposedSearchBar) {
- // In landscape phones, the search bar appears on the left
- searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
- windowBounds.left + searchBarSize, windowBounds.bottom);
- } else {
- // In portrait, the search bar appears on the top
- searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
- windowBounds.right, windowBounds.top + topInset + searchBarSize);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index 40bf6d3..043510e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -31,14 +31,12 @@
public static class Static {
// Enables debug drawing for the transition thumbnail
public static final boolean EnableTransitionThumbnailDebugMode = false;
- // This enables the search bar integration
- public static final boolean EnableSearchBar = false;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// Enables the task affiliations
- public static final boolean EnableAffiliatedTaskGroups = true;
- // Enables the history
- public static final boolean EnableHistory = false;
+ public static final boolean EnableAffiliatedTaskGroups = false;
+ // TODO: To be repurposed
+ public static final boolean EnableStackActionButton = false;
// Overrides the Tuner flags and enables the timeout
private static final boolean EnableFastToggleTimeout = false;
// Overrides the Tuner flags and enables the paging via the Recents button
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 880fe10..7daef64 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -20,8 +20,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.ITaskStackListener;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +28,7 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -41,12 +40,13 @@
import android.view.ViewConfiguration;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.EventBus.Event;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.ForcedResizableEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
@@ -59,6 +59,7 @@
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -95,37 +96,13 @@
public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
/**
- * An implementation of ITaskStackListener, that allows us to listen for changes to the system
+ * An implementation of TaskStackListener, that allows us to listen for changes to the system
* task stacks and update recents accordingly.
*/
- class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
- Handler mHandler;
-
- public TaskStackListenerImpl(Handler handler) {
- mHandler = handler;
- }
-
+ class TaskStackListenerImpl extends TaskStackListener {
@Override
public void onTaskStackChanged() {
- // Debounce any task stack changes
- mHandler.removeCallbacks(this);
- mHandler.post(this);
- }
-
- @Override
- public void onActivityPinned() {
- }
-
- @Override
- public void onPinnedActivityRestartAttempt() {
- }
-
- @Override
- public void onPinnedStackAnimationEnded() {
- }
-
- /** Preloads the next task */
- public void run() {
+ // Preloads the next task
RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -149,6 +126,13 @@
loader.loadTasks(mContext, plan, launchOpts);
}
}
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId) {
+ EventBus.getDefault().sendOntoMainThread(
+ new ForcedResizableEvent(packageName, taskId));
+
+ }
}
protected static RecentsTaskLoadPlan sInstanceLoadPlan;
@@ -156,13 +140,10 @@
protected Context mContext;
protected Handler mHandler;
TaskStackListenerImpl mTaskStackListener;
- RecentsAppWidgetHost mAppWidgetHost;
- protected boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
boolean mLaunchedWhileDocking;
// Task launching
- Rect mSearchBarBounds = new Rect();
Rect mTaskStackBounds = new Rect();
Rect mLastTaskViewBounds = new Rect();
TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -195,19 +176,18 @@
public RecentsImpl(Context context) {
mContext = context;
mHandler = new Handler();
- mAppWidgetHost = new RecentsAppWidgetHost(mContext, RecentsAppWidgetHost.HOST_ID);
// Initialize the static foreground thread
ForegroundThread.get();
// Register the task stack listener
- mTaskStackListener = new TaskStackListenerImpl(mHandler);
+ mTaskStackListener = new TaskStackListenerImpl();
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.registerTaskStackListener(mTaskStackListener);
// Initialize the static configuration resources
reloadHeaderBarLayout();
- updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
+ updateHeaderBarLayout(null /* stack */);
// When we start, preload the data associated with the previous recent tasks.
// We can use a new plan since the caches will be the same.
@@ -222,14 +202,12 @@
}
public void onBootCompleted() {
- updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
+ updateHeaderBarLayout(null /* stack */);
}
public void onConfigurationChanged() {
reloadHeaderBarLayout();
- updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
- // Don't reuse task stack views if the configuration changes
- mCanReuseTaskStackViews = false;
+ updateHeaderBarLayout(null /* stack */);
Recents.getConfiguration().updateOnConfigurationChange();
}
@@ -585,8 +563,13 @@
com.android.internal.R.dimen.navigation_bar_height);
mNavBarWidth = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_width);
- mTaskBarHeight = res.getDimensionPixelSize(
- R.dimen.recents_task_bar_height);
+ mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land);
mDummyStackView = new TaskStackView(mContext);
mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
null, false);
@@ -596,12 +579,9 @@
* Prepares the header bar layout for the next transition, if the task view bounds has changed
* since the last call, it will attempt to re-measure and layout the header bar to the new size.
*
- * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
- * is not already bound (can be expensive)
* @param stack the stack to initialize the stack layout with
*/
- private void updateHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
- RecentsConfiguration config = Recents.getConfiguration();
+ private void updateHeaderBarLayout(TaskStack stack) {
SystemServicesProxy ssp = Recents.getSystemServices();
Rect systemInsets = new Rect();
ssp.getStableInsets(systemInsets);
@@ -609,29 +589,17 @@
calculateWindowStableInsets(systemInsets, windowRect);
windowRect.offsetTo(0, 0);
- // Update the configuration for the current state
- config.update(systemInsets);
-
- if (RecentsDebugFlags.Static.EnableSearchBar && tryAndBindSearchWidget) {
- // Try and pre-emptively bind the search widget on startup to ensure that we
- // have the right thumbnail bounds to animate to.
- // Note: We have to reload the widget id before we get the task stack bounds below
- if (ssp.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
- config.getSearchBarBounds(windowRect, mStatusBarHeight, mSearchBarBounds);
- }
- }
- config.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
- mSearchBarBounds, mTaskStackBounds);
+ TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
+ stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
+ mTaskStackBounds);
// Rebind the header bar and draw it for the transition
- TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
stackLayout.setSystemInsets(systemInsets);
if (stack != null) {
- stackLayout.initialize(taskStackBounds,
+ stackLayout.initialize(windowRect, taskStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
- mDummyStackView.setTasks(stack, false /* notifyStackChanges */,
- false /* relayoutTaskStack */);
+ mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
}
Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
if (!taskViewBounds.equals(mLastTaskViewBounds)) {
@@ -688,7 +656,7 @@
preloadIcon(topTask);
// Update the header bar if necessary
- updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
+ updateHeaderBarLayout(stack);
// Update the destination rect
final Task toTask = new Task();
@@ -721,13 +689,7 @@
/**
* Creates the activity options for a home->recents transition.
*/
- protected ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
- if (fromSearchHome) {
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_search_launcher_enter,
- R.anim.recents_from_search_launcher_exit,
- mHandler, null);
- }
+ protected ActivityOptions getHomeTransitionActivityOptions() {
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_launcher_enter,
R.anim.recents_from_launcher_exit,
@@ -746,7 +708,7 @@
TaskStackViewScroller stackScroller = stackView.getScroller();
stackView.updateLayoutAlgorithm(true /* boundScroll */);
- stackView.updateToInitialState();
+ stackView.updateToInitialState(true /* scrollToInitialState */);
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
@@ -813,7 +775,7 @@
// Get the transform for the running task
stackView.updateLayoutAlgorithm(true /* boundScroll */);
- stackView.updateToInitialState();
+ stackView.updateToInitialState(true /* scrollToInitialState */);
mTmpTransform = stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
stackView.getScroller().getStackScroll(), mTmpTransform, null);
return mTmpTransform;
@@ -839,6 +801,12 @@
} else {
Canvas c = new Canvas(thumbnail);
c.scale(toTransform.scale, toTransform.scale);
+ // Workaround for b/27815919, reset the callback so that we do not trigger an
+ // invalidate on the header bar as a result of updating the icon
+ Drawable icon = mHeaderBar.getIconView().getDrawable();
+ if (icon != null) {
+ icon.setCallback(null);
+ }
mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
disabledInSafeMode);
mHeaderBar.setDimAlpha(toTransform.dimAlpha);
@@ -857,6 +825,7 @@
protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
boolean isTopTaskHome, boolean animate) {
RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
@@ -868,31 +837,41 @@
if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
}
+
TaskStack stack = sInstanceLoadPlan.getTaskStack();
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
+ boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
// Update the header bar if necessary
- updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
+ updateHeaderBarLayout(stack);
// Prepare the dummy stack for the transition
TaskStackLayoutAlgorithm.VisibilityReport stackVr =
mDummyStackView.computeStackVisibilityReport();
+ // Update the launch state
+ launchState.launchedFromHome = false;
+ launchState.launchedFromApp = mLaunchedWhileDocking;
+ launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+ launchState.launchedFromAppDocked = mLaunchedWhileDocking;
+ launchState.launchedWithAltTab = mTriggeredFromAltTab;
+ launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
+ launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
+ launchState.launchedViaDragGesture = mDraggingInRecents;
+ launchState.launchedWhileDocking = mLaunchedWhileDocking;
+
if (!animate) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
- startRecentsActivity(topTask, opts, false /* fromHome */,
- false /* fromSearchHome */, false /* fromThumbnail*/, stackVr);
+ startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1));
return;
}
- boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
-
if (useThumbnailTransition) {
+ launchState.launchedFromApp = true;
+
// Try starting with a thumbnail transition
ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
if (opts != null) {
- startRecentsActivity(topTask, opts, false /* fromHome */,
- false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
+ startRecentsActivity(opts);
} else {
// Fall through below to the non-thumbnail transition
useThumbnailTransition = false;
@@ -900,34 +879,14 @@
}
if (!useThumbnailTransition) {
- // If there is no thumbnail transition, but is launching from home into recents, then
- // use a quick home transition and do the animation from home
- if (hasRecentTasks) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- String homeActivityPackage = ssp.getHomeActivityPackageName();
- String searchWidgetPackage = null;
- if (RecentsDebugFlags.Static.EnableSearchBar) {
- searchWidgetPackage = Prefs.getString(mContext,
- Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
- } else {
- AppWidgetProviderInfo searchWidgetInfo = ssp.resolveSearchAppWidget();
- if (searchWidgetInfo != null) {
- searchWidgetPackage = searchWidgetInfo.provider.getPackageName();
- }
- }
+ launchState.launchedFromHome = true;
- // Determine whether we are coming from a search owned home activity
- boolean fromSearchHome = (homeActivityPackage != null) &&
- homeActivityPackage.equals(searchWidgetPackage);
- ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
- startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
- false /* fromThumbnail */, stackVr);
- } else {
- // Otherwise we do the normal fade from an unknown source
- ActivityOptions opts = getUnknownTransitionActivityOptions();
- startRecentsActivity(topTask, opts, true /* fromHome */,
- false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
- }
+ // If there is no thumbnail transition, but is launching from home into recents, then
+ // use a quick home transition
+ ActivityOptions opts = hasRecentTasks
+ ? getHomeTransitionActivityOptions()
+ : getUnknownTransitionActivityOptions();
+ startRecentsActivity(opts);
}
mLastToggleTime = SystemClock.elapsedRealtime();
}
@@ -935,25 +894,7 @@
/**
* Starts the recents activity.
*/
- private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, boolean fromHome, boolean fromSearchHome,
- boolean fromThumbnail, TaskStackLayoutAlgorithm.VisibilityReport vr) {
- // Update the configuration based on the launch options
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.launchedFromHome = fromSearchHome || fromHome;
- launchState.launchedFromSearchHome = fromSearchHome;
- launchState.launchedFromApp = fromThumbnail || mLaunchedWhileDocking;
- launchState.launchedFromAppDocked = mLaunchedWhileDocking;
- launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
- launchState.launchedWithAltTab = mTriggeredFromAltTab;
- launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
- launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
- launchState.launchedHasConfigurationChanged = false;
- launchState.launchedViaDragGesture = mDraggingInRecents;
- launchState.launchedWhileDocking = mLaunchedWhileDocking;
-
+ private void startRecentsActivity(ActivityOptions opts) {
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -965,7 +906,6 @@
} else {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
- mCanReuseTaskStackViews = true;
EventBus.getDefault().send(new RecentsActivityStartingEvent());
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
index 98c0a69..4738eed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
@@ -11,7 +11,7 @@
* 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
*/
package com.android.systemui.recents.events.activity;
@@ -19,8 +19,7 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the history is to be cleared
+ * Sent when an app transition has finished playing.
*/
-public class ClearHistoryEvent extends EventBus.AnimatedEvent {
- // Simple event
+public class AppTransitionFinishedEvent extends EventBus.Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
deleted file mode 100644
index 52cfe18..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 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.recents.events.activity;
-
-import com.android.systemui.recents.RecentsAppWidgetHost;
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent by the {@link RecentsAppWidgetHost} whenever the search provider widget changes, and
- * subscribers can update accordingly.
- */
-public class AppWidgetProviderChangedEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
index 0ad4681..c234c34 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
@@ -22,5 +22,10 @@
* This is sent when the Recents activity configuration has changed.
*/
public class ConfigurationChangedEvent extends EventBus.AnimatedEvent {
- // Simple event
+
+ public final boolean fromMultiWindow;
+
+ public ConfigurationChangedEvent(boolean fromMultiWindow) {
+ this.fromMultiWindow = fromMultiWindow;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
similarity index 74%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
index 98c0a69..32d9a70 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
@@ -11,16 +11,15 @@
* 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
*/
package com.android.systemui.recents.events.activity;
-import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.EventBus.Event;
/**
- * This is sent when the history is to be cleared
+ * Sent when the window animation has started when docking a task
*/
-public class ClearHistoryEvent extends EventBus.AnimatedEvent {
- // Simple event
+public class DockedFirstAnimationFrameEvent extends Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java
index aaf77af..cdcabf0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java
@@ -11,7 +11,7 @@
* 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
*/
package com.android.systemui.recents.events.activity;
@@ -19,10 +19,16 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the history view button is clicked.
+ * Sent when recents received the information that an activity got forced resizable, and we need
+ * to inform the user about that.
*/
-public class ToggleHistoryEvent extends EventBus.AnimatedEvent {
+public class ForcedResizableEvent extends EventBus.Event {
- // Simple event
+ public final String packageName;
+ public final int taskId;
+ public ForcedResizableEvent(String packageName, int taskId) {
+ this.packageName = packageName;
+ this.taskId = taskId;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryButtonEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryButtonEvent.java
deleted file mode 100644
index 6c767e4..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryButtonEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the history view button should be hidden.
- */
-public class HideHistoryButtonEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
deleted file mode 100644
index bacf3bd..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the history view will be closed.
- */
-public class HideHistoryEvent extends EventBus.Event {
-
- public final boolean animate;
-
- public HideHistoryEvent(boolean animate) {
- this.animate = animate;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
index 469f336..e02fb14 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
@@ -19,10 +19,8 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the history view button is clicked.
+ * This is sent when the stack action button should be hidden.
*/
-public class ShowHistoryEvent extends EventBus.Event {
-
+public class HideStackActionButtonEvent extends EventBus.Event {
// Simple event
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryButtonEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryButtonEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
index ae803ea..d81f89c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryButtonEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
@@ -19,14 +19,14 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the history view button should be shown.
+ * This is sent when the stack action view button should be shown.
*/
-public class ShowHistoryButtonEvent extends EventBus.Event {
+public class ShowStackActionButtonEvent extends EventBus.Event {
- // Whether or not to translate the history button when showing it
+ // Whether or not to translate the stack action button when showing it
public final boolean translate;
- public ShowHistoryButtonEvent(boolean translate) {
+ public ShowStackActionButtonEvent(boolean translate) {
this.translate = translate;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
deleted file mode 100644
index 16385c9..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2015 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.recents.history;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.support.v7.widget.RecyclerView;
-import android.text.format.DateFormat;
-import android.util.SparseIntArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
-import com.android.systemui.recents.events.activity.HideHistoryEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.AnimationProps;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
-
-/**
- * An adapter for the list of recent tasks in the history view.
- */
-public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAdapter.ViewHolder> {
-
- private static final String TAG = "RecentsHistoryView";
- private static final boolean DEBUG = false;
-
- static final int DATE_ROW_VIEW_TYPE = 0;
- static final int TASK_ROW_VIEW_TYPE = 1;
-
- /**
- * View holder implementation. The {@param TaskCallbacks} are only called for TaskRow view
- * holders.
- */
- public static class ViewHolder extends RecyclerView.ViewHolder implements Task.TaskCallbacks {
- public final View content;
-
- private Task mTask;
-
- public ViewHolder(View content) {
- super(content);
- this.content = content;
- }
-
- /**
- * Binds this view holder to the given task.
- */
- public void bindToTask(Task newTask) {
- unbindFromTask();
- mTask = newTask;
- mTask.addCallback(this);
- }
-
- /**
- * Unbinds this view holder from the
- */
- public void unbindFromTask() {
- if (mTask != null) {
- mTask.removeCallback(this);
- mTask = null;
- }
- }
-
- @Override
- public void onTaskDataLoaded(Task task) {
- // This callback is only made for TaskRow view holders
- ImageView iv = (ImageView) content.findViewById(R.id.icon);
- iv.setImageDrawable(task.icon);
- iv.animate()
- .alpha(1f)
- .setDuration(100)
- .start();
- }
-
- @Override
- public void onTaskDataUnloaded() {
- // This callback is only made for TaskRow view holders
- ImageView iv = (ImageView) content.findViewById(R.id.icon);
- iv.setImageBitmap(null);
- iv.animate().cancel();
- }
-
- @Override
- public void onTaskStackIdChanged() {
- // Do nothing, this callback is only made for TaskRow view holders
- }
- }
-
- /**
- * A single row of content.
- */
- interface Row {
- int getViewType();
- }
-
- /**
- * A date row.
- */
- static class DateRow implements Row {
-
- public final String date;
-
- public DateRow(String date) {
- this.date = date;
- }
-
- @Override
- public int getViewType() {
- return RecentsHistoryAdapter.DATE_ROW_VIEW_TYPE;
- }
- }
-
- /**
- * A task row.
- */
- static class TaskRow implements Row, View.OnClickListener {
-
- public final Task task;
- public final int dateKey;
-
- public TaskRow(Task task, int dateKey) {
- this.task = task;
- this.dateKey = dateKey;
- }
-
- @Override
- public void onClick(View v) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(v.getContext(), task.key.id, task.title,
- ActivityOptions.makeBasic());
-
- MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
- task.key.getComponent().toString());
- }
-
- @Override
- public int getViewType() {
- return RecentsHistoryAdapter.TASK_ROW_VIEW_TYPE;
- }
- }
-
- private Context mContext;
- private LayoutInflater mLayoutInflater;
- private final List<Row> mRows = new ArrayList<>();
- private final SparseIntArray mTaskRowCount = new SparseIntArray();
- private TaskStack mStack;
-
- public RecentsHistoryAdapter(Context context) {
- mLayoutInflater = LayoutInflater.from(context);
- }
-
- /**
- * Updates this adapter with the given tasks.
- */
- public void updateTasks(Context context, TaskStack stack) {
- mContext = context;
- mStack = stack;
-
- final Locale l = context.getResources().getConfiguration().locale;
- final String dateFormatStr = DateFormat.getBestDateTimePattern(l, "EEEEMMMMd");
- final List<Task> tasksMostRecent = new ArrayList<>(stack.getHistoricalTasks());
- Collections.reverse(tasksMostRecent);
- int prevDateKey = -1;
- int taskCount = tasksMostRecent.size();
- mRows.clear();
- mTaskRowCount.clear();
- Calendar cal = Calendar.getInstance(l);
- for (int i = 0; i < taskCount; i++) {
- Task task = tasksMostRecent.get(i);
- if (task.isFreeformTask()) {
- continue;
- }
-
- cal.setTimeInMillis(task.key.lastActiveTime);
- int dateKey = Objects.hash(cal.get(Calendar.YEAR), cal.get(Calendar.DAY_OF_YEAR));
- if (dateKey != prevDateKey) {
- prevDateKey = dateKey;
- mRows.add(new DateRow(DateFormat.format(dateFormatStr, cal).toString()));
- }
- mRows.add(new TaskRow(task, dateKey));
- mTaskRowCount.put(dateKey, mTaskRowCount.get(dateKey, 0) + 1);
- }
- notifyDataSetChanged();
- }
-
- /**
- * Removes historical tasks belonging to the specified package and user. We do not need to
- * remove the task from the TaskStack since the TaskStackView will also receive this event.
- */
- public void removeTasks(String packageName, int userId) {
- for (int i = mRows.size() - 1; i >= 0; i--) {
- Row row = mRows.get(i);
- if (row.getViewType() == TASK_ROW_VIEW_TYPE) {
- TaskRow taskRow = (TaskRow) row;
- Task task = taskRow.task;
- String taskPackage = task.key.getComponent().getPackageName();
- if (task.key.userId == userId && taskPackage.equals(packageName)) {
- i = removeTaskRow(i);
- }
- }
- }
- if (mRows.isEmpty()) {
- dismissHistory();
- }
- }
-
- /**
- * Removes all historical tasks.
- */
- public void removeAllTasks() {
- for (int i = mRows.size() - 1; i >= 0; i--) {
- Row row = mRows.get(i);
- if (row.getViewType() == TASK_ROW_VIEW_TYPE) {
- TaskRow taskRow = (TaskRow) row;
- Task task = taskRow.task;
- mStack.removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
- EventBus.getDefault().send(new DeleteTaskDataEvent(task));
- i = removeTaskRow(i);
- }
- }
- dismissHistory();
- }
-
- /**
- * Returns the row at the given {@param position}.
- */
- public Row getRow(int position) {
- return mRows.get(position);
- }
-
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- switch (viewType) {
- case DATE_ROW_VIEW_TYPE:
- return new ViewHolder(mLayoutInflater.inflate(R.layout.recents_history_date, parent,
- false));
- case TASK_ROW_VIEW_TYPE:
- return new ViewHolder(mLayoutInflater.inflate(R.layout.recents_history_task, parent,
- false));
- default:
- return new ViewHolder(null);
- }
- }
-
- @Override
- public void onBindViewHolder(ViewHolder holder, int position) {
- RecentsTaskLoader loader = Recents.getTaskLoader();
-
- Row row = mRows.get(position);
- int viewType = row.getViewType();
- switch (viewType) {
- case DATE_ROW_VIEW_TYPE: {
- TextView tv = (TextView) holder.content;
- tv.setText(((DateRow) row).date);
- break;
- }
- case TASK_ROW_VIEW_TYPE: {
- TaskRow taskRow = (TaskRow) row;
- TextView tv = (TextView) holder.content.findViewById(R.id.description);
- tv.setText(taskRow.task.title);
- ImageView iv = (ImageView) holder.content.findViewById(R.id.icon);
- iv.setAlpha(0f);
- holder.content.setOnClickListener(taskRow);
-
- holder.bindToTask(taskRow.task);
- loader.loadTaskData(taskRow.task, false /* fetchAndInvalidateThumbnails */);
- break;
- }
- }
- }
-
- @Override
- public void onViewRecycled(ViewHolder holder) {
- RecentsTaskLoader loader = Recents.getTaskLoader();
- int position = holder.getAdapterPosition();
- if (position != RecyclerView.NO_POSITION) {
- Row row = mRows.get(position);
- int viewType = row.getViewType();
- if (viewType == TASK_ROW_VIEW_TYPE) {
- TaskRow taskRow = (TaskRow) row;
- loader.unloadTaskData(taskRow.task);
- holder.unbindFromTask();
- }
- }
- }
-
- @Override
- public boolean onFailedToRecycleView(ViewHolder holder) {
- // Always recycle views, even if it is animating
- onViewRecycled(holder);
- return true;
- }
-
- public void onTaskRemoved(Task task, int position) {
- // Since this is removed from the history, we need to update the stack as well to ensure
- // that the model is correct. Since the stack is hidden, we can update it immediately.
- mStack.removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
- removeTaskRow(position);
- if (mRows.isEmpty()) {
- dismissHistory();
- }
- }
-
- @Override
- public int getItemCount() {
- return mRows.size();
- }
-
- @Override
- public int getItemViewType(int position) {
- return mRows.get(position).getViewType();
- }
-
- /**
- * Removes a task row, also removing the associated {@link DateRow} if there are no more tasks
- * in that date group.
- *
- * @param position an adapter position of a task row such that 0 < position < num rows.
- * @return the index of the last removed row
- */
- private int removeTaskRow(int position) {
- // Remove the task at that row
- TaskRow taskRow = (TaskRow) mRows.remove(position);
- int numTasks = mTaskRowCount.get(taskRow.dateKey) - 1;
- mTaskRowCount.put(taskRow.dateKey, numTasks);
- notifyItemRemoved(position);
-
- if (numTasks == 0) {
- // If that was the last task row in the group, then remove the date as well
- mRows.remove(position - 1);
- mTaskRowCount.removeAt(mTaskRowCount.indexOfKey(taskRow.dateKey));
- notifyItemRemoved(position - 1);
- return position - 1;
- } else {
- return position;
- }
- }
-
- /**
- * Dismisses history back to the stack view.
- */
- private void dismissHistory() {
- EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
- EventBus.getDefault().send(new HideHistoryButtonEvent());
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
deleted file mode 100644
index 3d1ea8e..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2015 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.recents.history;
-
-import android.content.Context;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-
-
-/**
- * An item touch handler for items in the history view.
- */
-public class RecentsHistoryItemTouchCallbacks extends ItemTouchHelper.SimpleCallback {
-
- private Context mContext;
- private RecentsHistoryAdapter mAdapter;
-
- public RecentsHistoryItemTouchCallbacks(Context context, RecentsHistoryAdapter adapter) {
- super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
- mContext = context;
- mAdapter = adapter;
- }
-
- @Override
- public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
- RecyclerView.ViewHolder target) {
- return false;
- }
-
- @Override
- public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
- int viewType = mAdapter.getItemViewType(viewHolder.getAdapterPosition());
- switch (viewType) {
- case RecentsHistoryAdapter.DATE_ROW_VIEW_TYPE:
- // Disallow swiping
- return 0;
- default:
- return super.getSwipeDirs(recyclerView, viewHolder);
- }
- }
-
- @Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
- int position = viewHolder.getAdapterPosition();
- if (position != RecyclerView.NO_POSITION) {
- RecentsHistoryAdapter.Row row = mAdapter.getRow(position);
- RecentsHistoryAdapter.TaskRow taskRow = (RecentsHistoryAdapter.TaskRow) row;
-
- // Remove the task from the system
- EventBus.getDefault().send(new DeleteTaskDataEvent(taskRow.task));
- mAdapter.onTaskRemoved(taskRow.task, position);
-
- // Keep track of deletions by swiping within history
- MetricsLogger.histogram(mContext, "overview_task_dismissed_source",
- Constants.Metrics.DismissSourceHistorySwipeGesture);
- MetricsLogger.action(mContext, MetricsEvent.OVERVIEW_DISMISS,
- taskRow.task.key.getComponent().toString());
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
deleted file mode 100644
index 3c4adb2..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2015 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.recents.history;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowInsets;
-import android.widget.LinearLayout;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ClearHistoryEvent;
-import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
-import com.android.systemui.recents.events.ui.UpdateBackgroundScrimEvent;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.AnimateableViewBounds;
-
-/**
- * A list of the recent tasks that are not in the stack.
- */
-public class RecentsHistoryView extends LinearLayout
- implements ValueAnimator.AnimatorUpdateListener {
-
- private static final float TRANSLATION_Y_PCT = 0.25f;
- private static final float BG_SCRIM_ALPHA = 0.625f;
-
- private RecyclerView mRecyclerView;
- private RecentsHistoryAdapter mAdapter;
- private RecentsHistoryItemTouchCallbacks mItemTouchHandler;
- private AnimateableViewBounds mViewBounds;
- private boolean mIsVisible;
- private Rect mSystemInsets = new Rect();
- private int mHeaderHeight;
-
- private int mHistoryTransitionDuration;
-
- public RecentsHistoryView(Context context) {
- super(context);
- }
-
- public RecentsHistoryView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RecentsHistoryView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public RecentsHistoryView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- Resources res = context.getResources();
- mAdapter = new RecentsHistoryAdapter(context);
- mItemTouchHandler = new RecentsHistoryItemTouchCallbacks(context, mAdapter);
- mHistoryTransitionDuration = res.getInteger(R.integer.recents_history_transition_duration);
- mViewBounds = new AnimateableViewBounds(this, 0);
- setOutlineProvider(mViewBounds);
- }
-
- /**
- * Updates this history view with the recent tasks, and then shows it.
- */
- public void show(TaskStack stack, int stackHeight, View clearAllButton) {
- setVisibility(View.VISIBLE);
- setAlpha(0f);
- setTranslationY(-stackHeight * TRANSLATION_Y_PCT);
- animate()
- .alpha(1f)
- .translationY(0f)
- .setDuration(mHistoryTransitionDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setUpdateListener(this)
- .start();
- clearAllButton.setVisibility(View.VISIBLE);
- clearAllButton.setAlpha(0f);
- clearAllButton.animate()
- .alpha(1f)
- .setDuration(mHistoryTransitionDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withLayer()
- .start();
- mAdapter.updateTasks(getContext(), stack);
- mIsVisible = true;
- EventBus.getDefault().send(new UpdateBackgroundScrimEvent(BG_SCRIM_ALPHA));
-
- MetricsLogger.visible(mRecyclerView.getContext(), MetricsEvent.OVERVIEW_HISTORY);
- }
-
- /**
- * Hides this history view.
- */
- public void hide(boolean animate, int stackHeight, final View clearAllButton) {
- if (animate) {
- animate()
- .alpha(0f)
- .translationY(-stackHeight * TRANSLATION_Y_PCT)
- .setDuration(mHistoryTransitionDuration)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setUpdateListener(this)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- setVisibility(View.INVISIBLE);
- }
- })
- .start();
- clearAllButton.animate()
- .alpha(0f)
- .translationY(0f)
- .setDuration(mHistoryTransitionDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- clearAllButton.setVisibility(View.INVISIBLE);
- }
- })
- .withLayer()
- .start();
- } else {
- setAlpha(0f);
- setVisibility(View.INVISIBLE);
- clearAllButton.setAlpha(0f);
- clearAllButton.setVisibility(View.INVISIBLE);
- }
- mIsVisible = false;
- EventBus.getDefault().send(new ResetBackgroundScrimEvent());
-
- MetricsLogger.hidden(mRecyclerView.getContext(), MetricsEvent.OVERVIEW_HISTORY);
- }
-
- /**
- * Updates the system insets of this history view to the provided values.
- */
- public void setSystemInsets(Rect systemInsets) {
- mSystemInsets.set(systemInsets);
- requestLayout();
- }
-
- /**
- * Updates the header height to account for the history button bar.
- */
- public void setHeaderHeight(int height) {
- mHeaderHeight = height;
- requestLayout();
- }
-
- /**
- * Returns whether this view is visible.
- */
- public boolean isVisible() {
- return mIsVisible;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mRecyclerView = (RecyclerView) findViewById(R.id.list);
- mRecyclerView.setAdapter(mAdapter);
- mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- mRecyclerView.getItemAnimator().setRemoveDuration(100);
- ItemTouchHelper touchHelper = new ItemTouchHelper(mItemTouchHandler);
- touchHelper.attachToRecyclerView(mRecyclerView);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- RecentsConfiguration config = Recents.getConfiguration();
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- // Pad the view to align the history with the stack layout
- Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
- mSystemInsets.right, new Rect() /* searchBarSpaceBounds */, taskStackBounds);
- int stackWidthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
- int stackHeightPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_stack_top_padding);
- mRecyclerView.setPadding(stackWidthPadding + mSystemInsets.left,
- stackHeightPadding + mSystemInsets.top + mHeaderHeight,
- stackWidthPadding + mSystemInsets.right, mSystemInsets.bottom);
-
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- protected void onAttachedToWindow() {
- EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- super.onAttachedToWindow();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- setSystemInsets(insets.getSystemWindowInsets());
- return insets;
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- // Clip the top of the view by the header bar height
- int top = Math.max(0, (int) -getTranslationY()) + mSystemInsets.top + mHeaderHeight;
- mViewBounds.setClipTop(top);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- /**** EventBus Events ****/
-
- public final void onBusEvent(PackagesChangedEvent event) {
- mAdapter.removeTasks(event.packageName, event.userId);
- }
-
- public final void onBusEvent(ClearHistoryEvent event) {
- mAdapter.removeAllTasks();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 330d138..3b759c02 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,15 +16,18 @@
package com.android.systemui.recents.misc;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
-import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -34,6 +37,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -47,7 +51,10 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -57,10 +64,9 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.MutableBoolean;
-import android.util.Pair;
import android.view.Display;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener;
-import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.view.WindowManagerGlobal;
@@ -68,11 +74,11 @@
import com.android.internal.app.AssistUtils;
import com.android.internal.os.BackgroundThread;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
import com.android.systemui.recents.tv.RecentsTvImpl;
+import com.android.systemui.recents.model.ThumbnailData;
import java.io.IOException;
import java.util.ArrayList;
@@ -80,12 +86,6 @@
import java.util.List;
import java.util.Random;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
-
/**
* Acts as a shim around the real system services that we need to access data from, and provides
* a point of injection when testing UI.
@@ -106,10 +106,11 @@
sRecentsBlacklist.add("com.android.systemui.tv.pip.PipMenuActivity");
}
+ private static SystemServicesProxy sSystemServicesProxy;
+
AccessibilityManager mAccm;
ActivityManager mAm;
IActivityManager mIam;
- AppWidgetManager mAwm;
PackageManager mPm;
IPackageManager mIpm;
AssistUtils mAssistUtils;
@@ -128,12 +129,69 @@
Paint mBgProtectionPaint;
Canvas mBgProtectionCanvas;
+ private final Handler mHandler = new H();
+
+ /**
+ * An abstract class to track task stack changes.
+ * Classes should implement this instead of {@link android.app.ITaskStackListener}
+ * to reduce IPC calls from system services. These callbacks will be called on the main thread.
+ */
+ public abstract static class TaskStackListener {
+ public void onTaskStackChanged() { }
+ public void onActivityPinned() { }
+ public void onPinnedActivityRestartAttempt() { }
+ public void onPinnedStackAnimationEnded() { }
+ public void onActivityForcedResizable(String packageName, int taskId) { }
+ }
+
+ /**
+ * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from
+ * ActivityManagerNative.
+ * This simply passes callbacks to listeners through {@link H}.
+ * */
+ private ITaskStackListener.Stub mTaskStackListener = new ITaskStackListener.Stub() {
+ @Override
+ public void onTaskStackChanged() throws RemoteException {
+ mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
+ mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
+ }
+
+ @Override
+ public void onActivityPinned() throws RemoteException {
+ mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
+ mHandler.sendEmptyMessage(H.ON_ACTIVITY_PINNED);
+ }
+
+ @Override
+ public void onPinnedActivityRestartAttempt() throws RemoteException{
+ mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+ mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+ }
+
+ @Override
+ public void onPinnedStackAnimationEnded() throws RemoteException {
+ mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
+ mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
+ }
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId)
+ throws RemoteException {
+ mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName)
+ .sendToTarget();
+ }
+ };
+
+ /**
+ * List of {@link TaskStackListener} registered from {@link #registerTaskStackListener}.
+ */
+ private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
+
/** Private constructor */
- public SystemServicesProxy(Context context) {
+ private SystemServicesProxy(Context context) {
mAccm = AccessibilityManager.getInstance(context);
mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mIam = ActivityManagerNative.getDefault();
- mAwm = AppWidgetManager.getInstance(context);
mPm = context.getPackageManager();
mIpm = AppGlobals.getPackageManager();
mAssistUtils = new AssistUtils(context);
@@ -170,6 +228,20 @@
}
}
+ /**
+ * Returns the single instance of the {@link SystemServicesProxy}.
+ * This should only be called on the main thread.
+ */
+ public static SystemServicesProxy getInstance(Context context) {
+ if (!Looper.getMainLooper().isCurrentThread()) {
+ throw new RuntimeException("Must be called on the UI thread");
+ }
+ if (sSystemServicesProxy == null) {
+ sSystemServicesProxy = new SystemServicesProxy(context);
+ }
+ return sSystemServicesProxy;
+ }
+
/** Returns a list of the recents tasks */
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
boolean isTopTaskHome, ArraySet<Integer> quietProfileIds) {
@@ -329,13 +401,12 @@
}
/** Docks a task to the side of the screen and starts it. */
- public void startTaskInDockedMode(Context context, View view, int taskId, int createMode) {
+ public void startTaskInDockedMode(int taskId, int createMode) {
if (mIam == null) return;
try {
// TODO: Determine what animation we want for the incoming task
- final ActivityOptions options = ActivityOptions.makeThumbnailAspectScaleUpAnimation(
- view, null, 0, 0, view.getWidth(), view.getHeight(), null, null);
+ final ActivityOptions options = ActivityOptions.makeBasic();
options.setDockCreateMode(createMode);
options.setLaunchStackId(DOCKED_STACK_ID);
mIam.startActivityFromRecents(taskId, options.toBundle());
@@ -450,44 +521,47 @@
}
/** Returns the top task thumbnail for the given task id */
- public Bitmap getTaskThumbnail(int taskId) {
+ public ThumbnailData getTaskThumbnail(int taskId) {
if (mAm == null) return null;
+ ThumbnailData thumbnailData = new ThumbnailData();
// If we are mocking, then just return a dummy thumbnail
if (RecentsDebugFlags.Static.EnableMockTasks) {
- Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
- Bitmap.Config.ARGB_8888);
- thumbnail.eraseColor(0xff333333);
- return thumbnail;
+ thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
+ mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
+ thumbnailData.thumbnail.eraseColor(0xff333333);
+ return thumbnailData;
}
- Bitmap thumbnail = getThumbnail(taskId);
- if (thumbnail != null) {
- thumbnail.setHasAlpha(false);
+ getThumbnail(taskId, thumbnailData);
+ if (thumbnailData.thumbnail != null) {
+ thumbnailData.thumbnail.setHasAlpha(false);
// We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
// left pixel, then assume the whole thumbnail is transparent. Generally, proper
// screenshots are always composed onto a bitmap that has no alpha.
- if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) {
- mBgProtectionCanvas.setBitmap(thumbnail);
- mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(),
- mBgProtectionPaint);
+ if (Color.alpha(thumbnailData.thumbnail.getPixel(0, 0)) == 0) {
+ mBgProtectionCanvas.setBitmap(thumbnailData.thumbnail);
+ mBgProtectionCanvas.drawRect(0, 0, thumbnailData.thumbnail.getWidth(),
+ thumbnailData.thumbnail.getHeight(), mBgProtectionPaint);
mBgProtectionCanvas.setBitmap(null);
Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
}
}
- return thumbnail;
+ return thumbnailData;
}
/**
* Returns a task thumbnail from the activity manager
*/
- public Bitmap getThumbnail(int taskId) {
+ public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
if (mAm == null) {
- return null;
+ return;
}
ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
- if (taskThumbnail == null) return null;
+ if (taskThumbnail == null) {
+ return;
+ }
Bitmap thumbnail = taskThumbnail.mainThumbnail;
ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
@@ -501,7 +575,8 @@
} catch (IOException e) {
}
}
- return thumbnail;
+ thumbnailDataOut.thumbnail = thumbnail;
+ thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
}
/**
@@ -785,89 +860,6 @@
}
/**
- * Returns the current search widget id.
- */
- public int getSearchAppWidgetId(Context context) {
- return Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1);
- }
-
- /**
- * Returns the current search widget info, binding a new one if necessary.
- */
- public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) {
- int searchWidgetId = Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1);
- AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId);
- AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget();
-
- // Return the search widget info if it hasn't changed
- if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null &&
- searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) {
- if (Prefs.getString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null) == null) {
- Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
- searchWidgetInfo.provider.getPackageName());
- }
- return searchWidgetInfo;
- }
-
- // Delete the old widget
- if (searchWidgetId != -1) {
- host.deleteAppWidgetId(searchWidgetId);
- }
-
- // And rebind a new search widget
- if (resolvedSearchWidgetInfo != null) {
- Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host,
- resolvedSearchWidgetInfo);
- if (widgetInfo != null) {
- Prefs.putInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, widgetInfo.first);
- Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
- widgetInfo.second.provider.getPackageName());
- return widgetInfo.second;
- }
- }
-
- // If we fall through here, then there is no resolved search widget, so clear the state
- Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID);
- Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE);
- return null;
- }
-
- /**
- * Returns the first Recents widget from the same package as the global assist activity.
- */
- public AppWidgetProviderInfo resolveSearchAppWidget() {
- if (mAssistComponent == null) return null;
- List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders(
- AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
- for (AppWidgetProviderInfo info : widgets) {
- if (info.provider.getPackageName().equals(mAssistComponent.getPackageName())) {
- return info;
- }
- }
- return null;
- }
-
- /**
- * Resolves and binds the search app widget that is to appear in the recents.
- */
- private Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host,
- AppWidgetProviderInfo resolvedSearchWidgetInfo) {
- if (mAwm == null) return null;
- if (mAssistComponent == null) return null;
-
- // Allocate a new widget id and try and bind the app widget (if that fails, then just skip)
- int searchWidgetId = host.allocateAppWidgetId();
- Bundle opts = new Bundle();
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
- if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, resolvedSearchWidgetInfo.provider, opts)) {
- host.deleteAppWidgetId(searchWidgetId);
- return null;
- }
- return new Pair<>(searchWidgetId, resolvedSearchWidgetInfo);
- }
-
- /**
* Returns whether touch exploration is currently enabled.
*/
public boolean isTouchExplorationEnabled() {
@@ -916,28 +908,40 @@
* Returns the smallest width/height.
*/
public int getDeviceSmallestWidth() {
- if (mWm == null) return 0;
+ if (mDisplay == null) return 0;
Point smallestSizeRange = new Point();
Point largestSizeRange = new Point();
- mWm.getDefaultDisplay().getCurrentSizeRange(smallestSizeRange, largestSizeRange);
+ mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
return smallestSizeRange.x;
}
/**
- * Returns the display rect.
+ * Returns the current display rect in the current display orientation.
*/
public Rect getDisplayRect() {
Rect displayRect = new Rect();
- if (mWm == null) return displayRect;
+ if (mDisplay == null) return displayRect;
Point p = new Point();
- mWm.getDefaultDisplay().getRealSize(p);
+ mDisplay.getRealSize(p);
displayRect.set(0, 0, p.x, p.y);
return displayRect;
}
/**
+ * Returns the current display orientation.
+ */
+ public int getDisplayOrientation() {
+ // Because of multi-window, the configuration orientation does not necessarily reflect the
+ // orientation of the display, instead we just use the display's real-size.
+ Rect displayRect = getDisplayRect();
+ return displayRect.width() > displayRect.height()
+ ? Configuration.ORIENTATION_LANDSCAPE
+ : Configuration.ORIENTATION_PORTRAIT;
+ }
+
+ /**
* Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
*/
public Rect getWindowRect() {
@@ -982,14 +986,21 @@
}
}
- /** Registers a task stack listener with the system. */
- public void registerTaskStackListener(ITaskStackListener listener) {
+ /**
+ * Registers a task stack listener with the system.
+ * This should be called on the main thread.
+ */
+ public void registerTaskStackListener(TaskStackListener listener) {
if (mIam == null) return;
- try {
- mIam.registerTaskStackListener(listener);
- } catch (Exception e) {
- e.printStackTrace();
+ mTaskStackListeners.add(listener);
+ if (mTaskStackListeners.size() == 1) {
+ // Register mTaskStackListener to IActivityManager only once if needed.
+ try {
+ mIam.registerTaskStackListener(mTaskStackListener);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to call registerTaskStackListener", e);
+ }
}
}
@@ -1026,8 +1037,9 @@
return dividerWindowWidth - 2 * dividerInsets;
}
- public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) {
- mWm.requestAppKeyboardShortcuts(receiver);
+ public void requestKeyboardShortcuts(
+ Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
+ mWm.requestAppKeyboardShortcuts(receiver, deviceId);
}
public void getStableInsets(Rect outStableInsets) {
@@ -1039,4 +1051,71 @@
e.printStackTrace();
}
}
+
+ public void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
+ boolean scaleUp) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
+ scaleUp);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override transition: " + e);
+ }
+ }
+
+ /**
+ * Returns whether the device has a transposed nav bar (on the right of the screen) in the
+ * current display orientation.
+ */
+ public boolean hasTransposedNavBar() {
+ Rect insets = new Rect();
+ getStableInsets(insets);
+ return insets.right > 0;
+ }
+
+ private final class H extends Handler {
+ private static final int ON_TASK_STACK_CHANGED = 1;
+ private static final int ON_ACTIVITY_PINNED = 2;
+ private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
+ private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
+ private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ON_TASK_STACK_CHANGED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskStackChanged();
+ }
+ break;
+ }
+ case ON_ACTIVITY_PINNED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityPinned();
+ }
+ break;
+ }
+ case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+ }
+ break;
+ }
+ case ON_PINNED_STACK_ANIMATION_ENDED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
+ }
+ break;
+ }
+ case ON_ACTIVITY_FORCED_RESIZABLE: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityForcedResizable(
+ (String) msg.obj, msg.arg1);
+ }
+ break;
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 72b1cab..e28612a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.annotation.FloatRange;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -26,6 +27,7 @@
import android.util.ArraySet;
import android.util.IntProperty;
import android.util.Property;
+import android.util.TypedValue;
import android.view.View;
import android.view.ViewParent;
@@ -67,6 +69,8 @@
public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
+ public static final Rect EMPTY_RECT = new Rect();
+
/**
* @return the first parent walking up the view hierarchy that has the given class type.
*
@@ -232,4 +236,11 @@
transforms.subList(taskCount, taskTransformCount).clear();
}
}
+
+ /**
+ * Used for debugging, converts DP to PX.
+ */
+ public static float dpToPx(Resources res, float dp) {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 6ae07fc..76ca6ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -39,7 +39,6 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Formatter;
import java.util.List;
@@ -161,8 +160,15 @@
long parentTaskLastActiveTime = parentTask != null
? parentTask.lastActiveTime
: prevLastActiveTime;
- t.lastActiveTime = parentTaskLastActiveTime +
- affiliatedTaskCounts.get(t.affiliatedTaskId, 0) + 1;
+ if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+ t.lastActiveTime = parentTaskLastActiveTime +
+ affiliatedTaskCounts.get(t.affiliatedTaskId, 0) + 1;
+ } else {
+ if (t.lastActiveTime == 0) {
+ t.lastActiveTime = parentTaskLastActiveTime -
+ affiliatedTaskCounts.get(t.affiliatedTaskId, 0) - 1;
+ }
+ }
}
// Compose the task key
@@ -196,13 +202,12 @@
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, contentDescription, dismissDescription, activityColor,
- backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.isDockable,
+ backgroundColor, isLaunchTarget, isStackTask, isSystemApp, t.isDockable,
t.bounds, t.taskDescription);
allTasks.add(task);
affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
affiliatedTasks.put(taskKey.id, taskKey);
-
prevLastActiveTime = t.lastActiveTime;
}
if (newLastStackActiveTime != -1) {
@@ -241,8 +246,8 @@
if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
if (task.icon == null) {
- task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
- res, true);
+ task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
+ true);
}
}
if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
@@ -273,7 +278,7 @@
}
/**
- * Returns whether this task is considered a task to be shown in the history.
+ * Returns whether this task is too old to be shown.
*/
private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) {
return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index ee4d95e..82c81ae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -95,7 +95,7 @@
TaskResourceLoadQueue mLoadQueue;
TaskKeyLruCache<Drawable> mIconCache;
- TaskKeyLruCache<Bitmap> mThumbnailCache;
+ TaskKeyLruCache<ThumbnailData> mThumbnailCache;
Bitmap mDefaultThumbnail;
BitmapDrawable mDefaultIcon;
@@ -104,7 +104,7 @@
/** Constructor, creates a new loading thread that loads task resources in the background */
public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
- TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
+ TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<ThumbnailData> thumbnailCache,
Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
mLoadQueue = loadQueue;
mIconCache = iconCache;
@@ -165,7 +165,7 @@
final Task t = mLoadQueue.nextTask();
if (t != null) {
Drawable cachedIcon = mIconCache.get(t.key);
- Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
+ ThumbnailData cachedThumbnailData = mThumbnailCache.get(t.key);
// Load the icon if it is stale or we haven't cached one yet
if (cachedIcon == null) {
@@ -190,30 +190,32 @@
mIconCache.put(t.key, cachedIcon);
}
// Load the thumbnail if it is stale or we haven't cached one yet
- if (cachedThumbnail == null) {
+ if (cachedThumbnailData == null) {
if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
- cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
+ cachedThumbnailData = ssp.getTaskThumbnail(t.key.id);
}
- if (cachedThumbnail == null) {
- cachedThumbnail = mDefaultThumbnail;
+
+ if (cachedThumbnailData.thumbnail == null) {
+ cachedThumbnailData.thumbnail = mDefaultThumbnail;
}
+
// When svelte, we trim the memory to just the visible thumbnails when
// leaving, so don't thrash the cache as the user scrolls (just load
// them from scratch each time)
if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) {
- mThumbnailCache.put(t.key, cachedThumbnail);
+ mThumbnailCache.put(t.key, cachedThumbnailData);
}
}
if (!mCancelled) {
// Notify that the task data has changed
final Drawable newIcon = cachedIcon;
- final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
- ? null : cachedThumbnail;
+ final ThumbnailData newThumbnailData = cachedThumbnailData;
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
- t.notifyTaskDataLoaded(newThumbnail, newIcon);
+ t.notifyTaskDataLoaded(newThumbnailData.thumbnail, newIcon,
+ newThumbnailData.thumbnailInfo);
}
});
}
@@ -252,7 +254,7 @@
// package in the cache has been updated, so that we may remove it.
private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
private final TaskKeyLruCache<Drawable> mIconCache;
- private final TaskKeyLruCache<Bitmap> mThumbnailCache;
+ private final TaskKeyLruCache<ThumbnailData> mThumbnailCache;
private final TaskKeyLruCache<String> mActivityLabelCache;
private final TaskKeyLruCache<String> mContentDescriptionCache;
private final TaskResourceLoadQueue mLoadQueue;
@@ -272,7 +274,9 @@
new TaskKeyLruCache.EvictionCallback() {
@Override
public void onEntryEvicted(Task.TaskKey key) {
- mActivityInfoCache.remove(key.getComponent());
+ if (key != null) {
+ mActivityInfoCache.remove(key.getComponent());
+ }
}
};
@@ -356,9 +360,16 @@
*/
public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
- Bitmap thumbnail = mDefaultThumbnail;
+ Bitmap thumbnail = null;
+ ActivityManager.TaskThumbnailInfo thumbnailInfo = null;
if (fetchAndInvalidateThumbnails) {
- thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
+ ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
+ if (thumbnailData != null) {
+ thumbnail = thumbnailData.thumbnail;
+ thumbnailInfo = thumbnailData.thumbnailInfo;
+ }
+ } else {
+ thumbnail = mDefaultThumbnail;
}
// Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
@@ -368,7 +379,8 @@
if (requiresLoad) {
mLoadQueue.addTask(t);
}
- t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon);
+ t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon,
+ thumbnailInfo);
}
/** Releases the task resource data back into the pool. */
@@ -535,19 +547,19 @@
SystemServicesProxy ssp = Recents.getSystemServices();
// Return the cached thumbnail if it exists
- Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
- if (thumbnail != null) {
- return thumbnail;
+ ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(taskKey);
+ if (thumbnailData != null) {
+ return thumbnailData.thumbnail;
}
if (loadIfNotCached) {
RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
// Load the thumbnail from the system
- thumbnail = ssp.getTaskThumbnail(taskKey.id);
- if (thumbnail != null) {
- mThumbnailCache.put(taskKey, thumbnail);
- return thumbnail;
+ thumbnailData = ssp.getTaskThumbnail(taskKey.id);
+ if (thumbnailData.thumbnail != null) {
+ mThumbnailCache.put(taskKey, thumbnailData);
+ return thumbnailData.thumbnail;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index b5a5949..d5d5aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -40,7 +40,7 @@
/* Task callbacks */
public interface TaskCallbacks {
/* Notifies when a task has been bound */
- public void onTaskDataLoaded(Task task);
+ public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo);
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
/* Notifies when a task's stack id has changed. */
@@ -161,7 +161,7 @@
@ViewDebug.ExportedProperty(category="recents")
public boolean isLaunchTarget;
@ViewDebug.ExportedProperty(category="recents")
- public boolean isHistorical;
+ public boolean isStackTask;
@ViewDebug.ExportedProperty(category="recents")
public boolean isSystemApp;
@ViewDebug.ExportedProperty(category="recents")
@@ -176,7 +176,7 @@
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
Bitmap thumbnail, String title, String contentDescription,
String dismissDescription, int colorPrimary, int colorBackground,
- boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp,
+ boolean isLaunchTarget, boolean isStackTask, boolean isSystemApp,
boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
@@ -195,7 +195,7 @@
this.bounds = bounds;
this.taskDescription = taskDescription;
this.isLaunchTarget = isLaunchTarget;
- this.isHistorical = isHistorical;
+ this.isStackTask = isStackTask;
this.isSystemApp = isSystemApp;
this.isDockable = isDockable;
}
@@ -217,8 +217,9 @@
this.colorBackground = o.colorBackground;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
this.bounds = o.bounds;
+ this.taskDescription = o.taskDescription;
this.isLaunchTarget = o.isLaunchTarget;
- this.isHistorical = o.isHistorical;
+ this.isStackTask = o.isStackTask;
this.isSystemApp = o.isSystemApp;
this.isDockable = o.isDockable;
}
@@ -264,12 +265,13 @@
}
/** Notifies the callback listeners that this task has been loaded */
- public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
+ public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon,
+ ActivityManager.TaskThumbnailInfo thumbnailInfo) {
this.icon = applicationIcon;
this.thumbnail = thumbnail;
int callbackCount = mCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
- mCallbacks.get(i).onTaskDataLoaded(this);
+ mCallbacks.get(i).onTaskDataLoaded(this, thumbnailInfo);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 193bfff..5a2507d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,6 +16,16 @@
package com.android.systemui.recents.model;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
@@ -37,13 +47,13 @@
import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.NamedCounter;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.views.DropTarget;
import com.android.systemui.recents.views.AnimationProps;
+import com.android.systemui.recents.views.DropTarget;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import java.util.ArrayList;
import java.util.Collections;
@@ -51,16 +61,6 @@
import java.util.List;
import java.util.Random;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-
/**
* An interface for a task filter to query whether a particular task should show in a stack.
@@ -222,9 +222,9 @@
Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture);
/**
- * Notifies when a task has been removed from the history.
+ * Notifies when tasks in the stack have been updated.
*/
- void onHistoryTaskRemoved(TaskStack stack, Task removedTask, AnimationProps animation);
+ void onStackTasksUpdated(TaskStack stack);
}
/**
@@ -375,29 +375,24 @@
* {@param height}.
*/
public Rect getDockedTaskStackBounds(int width, int height, int dividerSize, Rect insets,
- Resources res) {
- RecentsConfiguration config = Recents.getConfiguration();
-
+ TaskStackLayoutAlgorithm layoutAlgorithm, Resources res, Rect windowRectOut) {
// Calculate the inverse docked task bounds
boolean isHorizontalDivision =
res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
insets, width, height, dividerSize);
- Rect newWindowBounds = new Rect();
DockedDividerUtils.calculateBoundsForPosition(position,
- DockedDividerUtils.invertDockSide(dockSide), newWindowBounds, width, height,
+ DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
dividerSize);
// Calculate the task stack bounds from the new window bounds
- Rect searchBarSpaceBounds = new Rect();
Rect taskStackBounds = new Rect();
// If the task stack bounds is specifically under the dock area, then ignore the top
// inset
int top = dockArea.bottom < 1f
? 0
: insets.top;
- config.getTaskStackBounds(newWindowBounds, top, insets.right,
- searchBarSpaceBounds, taskStackBounds);
+ layoutAlgorithm.getTaskStackBounds(windowRectOut, top, insets.right, taskStackBounds);
return taskStackBounds;
}
}
@@ -429,7 +424,6 @@
ArrayList<Task> mRawTaskList = new ArrayList<>();
FilteredTaskList mStackTaskList = new FilteredTaskList();
- FilteredTaskList mHistoryTaskList = new FilteredTaskList();
TaskStackCallbacks mCb;
ArrayList<TaskGrouping> mGroups = new ArrayList<>();
@@ -440,29 +434,17 @@
mStackTaskList.setFilter(new TaskFilter() {
@Override
public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
- if (t.isAffiliatedTask()) {
- // If this task is affiliated with another parent in the stack, then the
- // historical state of this task depends on the state of the parent task
- Task parentTask = taskIdMap.get(t.affiliationTaskId);
- if (parentTask != null) {
- t = parentTask;
+ if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+ if (t.isAffiliatedTask()) {
+ // If this task is affiliated with another parent in the stack, then the
+ // historical state of this task depends on the state of the parent task
+ Task parentTask = taskIdMap.get(t.affiliationTaskId);
+ if (parentTask != null) {
+ t = parentTask;
+ }
}
}
- return !t.isHistorical;
- }
- });
- mHistoryTaskList.setFilter(new TaskFilter() {
- @Override
- public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
- if (t.isAffiliatedTask()) {
- // If this task is affiliated with another parent in the stack, then the
- // historical state of this task depends on the state of the parent task
- Task parentTask = taskIdMap.get(t.affiliationTaskId);
- if (parentTask != null) {
- t = parentTask;
- }
- }
- return t.isHistorical;
+ return t.isStackTask;
}
});
}
@@ -523,12 +505,6 @@
mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask, animation,
fromDockGesture);
}
- } else if (mHistoryTaskList.contains(t)) {
- removeTaskImpl(mHistoryTaskList, t);
- if (mCb != null) {
- // Notify that a task has been removed
- mCb.onHistoryTaskRemoved(this, t, animation);
- }
}
mRawTaskList.remove(t);
}
@@ -586,31 +562,22 @@
// Sort all the tasks to ensure they are ordered correctly
Collections.sort(allTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
- // Filter out the historical tasks from this new list
- ArrayList<Task> stackTasks = new ArrayList<>();
- ArrayList<Task> historyTasks = new ArrayList<>();
- int newTaskCount = allTasks.size();
- for (int i = 0; i < newTaskCount; i++) {
- Task task = allTasks.get(i);
- if (task.isHistorical) {
- historyTasks.add(task);
- } else {
- stackTasks.add(task);
- }
- }
-
- mStackTaskList.set(stackTasks);
- mHistoryTaskList.set(historyTasks);
+ mStackTaskList.set(allTasks);
mRawTaskList = allTasks;
+ // Update the affiliated groupings
+ createAffiliatedGroupings(context);
+
// Only callback for the newly added tasks after this stack has been updated
int addedTaskCount = addedTasks.size();
for (int i = 0; i < addedTaskCount; i++) {
mCb.onStackTaskAdded(this, addedTasks.get(i));
}
- // Update the affiliated groupings
- createAffiliatedGroupings(context);
+ // Notify that the task stack has been updated
+ if (notifyStackChanges) {
+ mCb.onStackTasksUpdated(this);
+ }
}
/**
@@ -650,14 +617,6 @@
}
/**
- * Returns the set of tasks that are inactive. These tasks will be presented in a separate
- * history view.
- */
- public ArrayList<Task> getHistoricalTasks() {
- return mHistoryTaskList.getTasks();
- }
-
- /**
* Returns the set of "freeform" tasks in the stack.
*/
public ArrayList<Task> getFreeformTasks() {
@@ -679,7 +638,6 @@
public ArrayList<Task> computeAllTasksList() {
ArrayList<Task> tasks = new ArrayList<>();
tasks.addAll(mStackTaskList.getTasks());
- tasks.addAll(mHistoryTaskList.getTasks());
Collections.sort(tasks, LAST_ACTIVE_TIME_COMPARATOR);
return tasks;
}
@@ -927,10 +885,6 @@
for (Task t : mStackTaskList.getTasks()) {
str += " " + t.toString() + "\n";
}
- str += "Historical Tasks(" + mHistoryTaskList.size() + "):\n";
- for (Task t : mHistoryTaskList.getTasks()) {
- str += " " + t.toString() + "\n";
- }
return str;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
index aaf77af..d0cdae5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.activity;
+package com.android.systemui.recents.model;
-import com.android.systemui.recents.events.EventBus;
+import android.app.ActivityManager;
+import android.graphics.Bitmap;
/**
- * This is sent when the history view button is clicked.
+ * Data for a single thumbnail.
*/
-public class ToggleHistoryEvent extends EventBus.AnimatedEvent {
-
- // Simple event
-
+public class ThumbnailData {
+ public Bitmap thumbnail;
+ public ActivityManager.TaskThumbnailInfo thumbnailInfo;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 960bd8c..134b90c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.recents.tv;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
@@ -57,6 +59,7 @@
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.tv.pip.PipManager;
+import com.android.systemui.tv.pip.PipControlsView;
import java.util.ArrayList;
import java.util.Collections;
@@ -77,8 +80,10 @@
private boolean mIgnoreAltTabRelease;
private RecentsTvView mRecentsView;
- private View mPipView;
+ private PipControlsView mPipControlsView;
private View mPipShadeView;
+ private AnimatorSet mPipControlsViewFadeInAnimator;
+ private AnimatorSet mPipControlsViewFadeOutAnimator;
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
@@ -95,10 +100,16 @@
}
@Override
- public void onShowPipMenu() { }
+ public void onShowPipMenu() {
+ updatePipUI();
+ }
@Override
- public void onMoveToFullscreen() { }
+ public void onMoveToFullscreen() {
+ // Recents should be dismissed when PIP moves to fullscreen. If not, Recents will
+ // be unnecessarily shown in the scenario: PIP->Fullscreen->PIP.
+ dismissRecentsToLaunchTargetTaskOrHome();
+ }
@Override
public void onPipResizeAboutToStart() { }
@@ -106,7 +117,6 @@
@Override
public void onMediaControllerChanged() { }
};
- private boolean mHasPip;
/**
* A common Runnable to finish Recents by launching Home with an animation depending on the
@@ -126,15 +136,8 @@
@Override
public void run() {
try {
- RecentsActivityLaunchState launchState =
- Recents.getConfiguration().getLaunchState();
ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsTvActivity.this,
- launchState.launchedFromSearchHome ?
- R.anim.recents_to_search_launcher_enter :
- R.anim.recents_to_launcher_enter,
- launchState.launchedFromSearchHome ?
- R.anim.recents_to_search_launcher_exit :
- R.anim.recents_to_launcher_exit);
+ R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
@@ -260,8 +263,22 @@
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- mPipView = findViewById(R.id.pip);
+ mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
+ mPipControlsView.setListener(new PipControlsView.Listener() {
+ @Override
+ public void onClosed() {
+ dismissRecentsToLaunchTargetTaskOrHome();
+ }
+ });
mPipShadeView = findViewById(R.id.pip_shade);
+
+ mPipControlsViewFadeInAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
+ R.anim.tv_pip_controls_fade_in);
+ mPipControlsViewFadeInAnimator.setTarget(mPipControlsView);
+ mPipControlsViewFadeOutAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
+ R.anim.tv_pip_controls_fade_out);
+ mPipControlsViewFadeOutAnimator.setTarget(mPipControlsView);
+
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -272,7 +289,6 @@
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
- mHasPip = false;
updatePipUI();
mPipManager.addListener(mPipListener);
}
@@ -297,7 +313,7 @@
RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromApp;
- if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
+ if (wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
@@ -463,37 +479,25 @@
}
private void updatePipUI() {
- if (mHasPip == mPipManager.isPipShown()) {
- return;
- }
- mHasPip = mPipManager.isPipShown();
- if (mHasPip) {
- // Place mPipView at the PIP bounds for fine tuned focus handling.
- Rect pipBounds = mPipManager.getPipBounds();
- LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
- lp.width = pipBounds.width();
- lp.height = pipBounds.height();
- lp.leftMargin = pipBounds.left;
- lp.topMargin = pipBounds.top;
- mPipView.setLayoutParams(lp);
-
- mPipView.setVisibility(View.VISIBLE);
- mPipView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_MENU);
- }
- });
- mPipView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ if (mPipManager.isPipShown()) {
+ mPipControlsView.setAlpha(0);
+ mPipControlsView.setVisibility(View.VISIBLE);
+ mPipShadeView.setVisibility(View.INVISIBLE);
+ mPipControlsView.setOnChildFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
mPipManager.onPipViewFocusChangedInRecents(hasFocus);
+ if (hasFocus) {
+ mPipControlsViewFadeInAnimator.start();
+ } else {
+ mPipControlsViewFadeOutAnimator.start();
+ }
mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
mPipShadeView.setVisibility(View.GONE);
} else {
- mPipView.setVisibility(View.GONE);
+ mPipControlsView.setVisibility(View.GONE);
mPipShadeView.setVisibility(View.GONE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
index 9fd5d55..c1b47dc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -24,10 +24,13 @@
import android.graphics.Rect;
import android.os.SystemClock;
import android.os.UserHandle;
-import com.android.systemui.recents.*;
+
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsImpl;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.tv.views.TaskCardView;
@@ -81,8 +84,7 @@
// If there is no thumbnail transition, but is launching from home into recents, then
// use a quick home transition and do the animation from home
if (hasRecentTasks) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ActivityOptions opts = getHomeTransitionActivityOptions(false);
+ ActivityOptions opts = getHomeTransitionActivityOptions();
startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */);
} else {
// Otherwise we do the normal fade from an unknown source
@@ -99,12 +101,9 @@
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.launchedFromHome = fromHome;
- launchState.launchedFromSearchHome = false;
launchState.launchedFromApp = fromThumbnail;
launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
launchState.launchedWithAltTab = mTriggeredFromAltTab;
- launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- launchState.launchedHasConfigurationChanged = false;
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, RECENTS_TV_ACTIVITY);
@@ -117,7 +116,6 @@
} else {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
- mCanReuseTaskStackViews = true;
EventBus.getDefault().send(new RecentsActivityStartingEvent());
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index bf6229c..3d5176f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -104,28 +104,6 @@
requestLayout();
}
- public Task getNextTaskOrTopTask(Task taskToSearch) {
- Task returnTask = null;
- boolean found = false;
- if (mTaskStackHorizontalView != null) {
- TaskStack stack = mTaskStackHorizontalView.getStack();
- ArrayList<Task> taskList = stack.getStackTasks();
- // Iterate the stack views and try and find the focused task
- for (int j = taskList.size() - 1; j >= 0; --j) {
- Task task = taskList.get(j);
- // Return the next task in the line.
- if (found)
- return task;
- // Remember the first possible task as the top task.
- if (returnTask == null)
- returnTask = task;
- if (task == taskToSearch)
- found = true;
- }
- }
- return returnTask;
- }
-
public boolean launchFocusedTask() {
if (mTaskStackHorizontalView != null) {
Task task = mTaskStackHorizontalView.getFocusedTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 5c2de8e..3d0e75a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -158,7 +158,7 @@
}
@Override
- public void onHistoryTaskRemoved(TaskStack stack, Task removedTask, AnimationProps animation) {
- //No history task on tv
+ public void onStackTasksUpdated(TaskStack stack) {
+ // Do nothing
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index 72b914c..035c058 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -46,7 +46,7 @@
public void reloadOnConfigurationChange(Context context) {
// This is applied to the edges of each task
mTaskPadding = context.getResources().getDimensionPixelSize(
- R.dimen.recents_freeform_workspace_task_padding) / 2;
+ R.dimen.recents_freeform_layout_task_padding) / 2;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index a91bbd4..98616f4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -16,9 +16,15 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
import android.annotation.Nullable;
import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
+import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -27,10 +33,8 @@
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteException;
-import android.util.Log;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.recents.Recents;
@@ -47,13 +51,9 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
/**
* A helper class to create transitions to/from Recents
*/
@@ -82,9 +82,9 @@
}
};
- public RecentsTransitionHelper(Context context, Handler handler) {
+ public RecentsTransitionHelper(Context context) {
mContext = context;
- mHandler = handler;
+ mHandler = new Handler();
}
/**
@@ -92,7 +92,7 @@
*/
public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
final TaskStackView stackView, final TaskView taskView,
- final boolean screenPinningRequested, final Rect bounds, int destinationStack) {
+ final boolean screenPinningRequested, final Rect bounds, final int destinationStack) {
final ActivityOptions opts = ActivityOptions.makeBasic();
if (bounds != null) {
opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
@@ -101,7 +101,12 @@
final ActivityOptions.OnAnimationStartedListener animStartedListener;
final IAppTransitionAnimationSpecsFuture transitionFuture;
if (taskView != null) {
- transitionFuture = getAppTransitionFuture(task, stackView, destinationStack);
+ transitionFuture = getAppTransitionFuture(new AnimationSpecComposer() {
+ @Override
+ public List<AppTransitionAnimationSpec> composeSpecs() {
+ return composeAnimationSpecs(task, stackView, destinationStack);
+ }
+ });
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
@@ -154,6 +159,23 @@
}
}
+ public IRemoteCallback wrapStartedListener(final OnAnimationStartedListener listener) {
+ if (listener == null) {
+ return null;
+ }
+ return new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onAnimationStarted();
+ }
+ });
+ }
+ };
+ }
+
/**
* Starts the activity for the launch task.
*
@@ -181,40 +203,21 @@
}
if (transitionFuture != null) {
- IRemoteCallback.Stub callback = null;
- if (animStartedListener != null) {
- callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (animStartedListener != null) {
- animStartedListener.onAnimationStarted();
- }
- }
- });
- }
- };
- }
- try {
- synchronized (this) {
- mAppTransitionAnimationSpecs = SPECS_WAITING;
- }
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionMultiThumbFuture(transitionFuture,
- callback, true /* scaleUp */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to override transition: " + e);
- }
+ ssp.overridePendingAppTransitionMultiThumbFuture(transitionFuture,
+ wrapStartedListener(animStartedListener), true /* scaleUp */);
}
}
/**
* Creates a future which will later be queried for animation specs for this current transition.
+ *
+ * @param composer The implementation that composes the specs on the UI thread.
*/
- private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final Task task,
- final TaskStackView stackView, final int destinationStack) {
+ public IAppTransitionAnimationSpecsFuture getAppTransitionFuture(
+ final AnimationSpecComposer composer) {
+ synchronized (this) {
+ mAppTransitionAnimationSpecs = SPECS_WAITING;
+ }
return new IAppTransitionAnimationSpecsFuture.Stub() {
@Override
public AppTransitionAnimationSpec[] get() throws RemoteException {
@@ -222,8 +225,7 @@
@Override
public void run() {
synchronized (RecentsTransitionHelper.this) {
- mAppTransitionAnimationSpecs = composeAnimationSpecs(task, stackView,
- destinationStack);
+ mAppTransitionAnimationSpecs = composer.composeSpecs();
RecentsTransitionHelper.this.notifyAll();
}
}
@@ -248,6 +250,17 @@
}
/**
+ * Composes the transition spec when docking a task, which includes a full task bitmap.
+ */
+ public List<AppTransitionAnimationSpec> composeDockAnimationSpec(
+ TaskView taskView, Rect transform) {
+ TaskViewTransform viewTransform = new TaskViewTransform();
+ viewTransform.fillIn(taskView);
+ return Collections.singletonList(new AppTransitionAnimationSpec(taskView.getTask().key.id,
+ RecentsTransitionHelper.composeTaskBitmap(taskView, viewTransform), transform));
+ }
+
+ /**
* Composes the animation specs for all the tasks in the target stack.
*/
private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
@@ -319,6 +332,43 @@
return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
}
+ public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
+ float scale = transform.scale;
+ int fromWidth = (int) (transform.rect.width() * scale);
+ int fromHeight = (int) (transform.rect.height() * scale);
+ Bitmap b = Bitmap.createBitmap(fromWidth, fromHeight,
+ Bitmap.Config.ARGB_8888);
+
+ if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+ b.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(b);
+ c.scale(scale, scale);
+ taskView.draw(c);
+ c.setBitmap(null);
+ }
+ return b.createAshmemBitmap();
+ }
+
+ private static Bitmap composeHeaderBitmap(TaskView taskView,
+ TaskViewTransform transform) {
+ float scale = transform.scale;
+ int fromHeaderWidth = (int) (transform.rect.width());
+ int fromHeaderHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
+ Bitmap b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+
+ if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+ b.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(b);
+ c.scale(scale, scale);
+ taskView.mHeaderView.draw(c);
+ c.setBitmap(null);
+ }
+ return b.createAshmemBitmap();
+ }
+
/**
* Composes a single animation spec for the given {@link TaskView}
*/
@@ -326,21 +376,7 @@
TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
Bitmap b = null;
if (addHeaderBitmap) {
- float scale = transform.scale;
- int fromHeaderWidth = (int) (transform.rect.width());
- int fromHeaderHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
- b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
- Bitmap.Config.ARGB_8888);
-
- if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- b.eraseColor(0xFFff0000);
- } else {
- Canvas c = new Canvas(b);
- c.scale(scale, scale);
- taskView.mHeaderView.draw(c);
- c.setBitmap(null);
- }
- b = b.createAshmemBitmap();
+ b = composeHeaderBitmap(taskView, transform);
}
Rect taskRect = new Rect();
@@ -351,4 +387,8 @@
}
return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
}
+
+ public interface AnimationSpecComposer {
+ List<AppTransitionAnimationSpec> composeSpecs();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index db97e8f..a1ba493 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -19,8 +19,8 @@
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -28,9 +28,10 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -48,19 +49,15 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ClearHistoryEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
-import com.android.systemui.recents.events.activity.HideHistoryEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
-import com.android.systemui.recents.events.activity.ShowHistoryEvent;
-import com.android.systemui.recents.events.activity.ToggleHistoryEvent;
+import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
@@ -68,12 +65,12 @@
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.history.RecentsHistoryView;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -90,15 +87,10 @@
private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
private static final float DEFAULT_SCRIM_ALPHA = 0.33f;
- private final Handler mHandler;
-
private TaskStack mStack;
private TaskStackView mTaskStackView;
- private RecentsAppWidgetHostView mSearchBar;
- private TextView mHistoryButton;
- private TextView mHistoryClearAllButton;
+ private TextView mStackActionButton;
private TextView mEmptyView;
- private RecentsHistoryView mHistoryView;
private boolean mAwaitingFirstLayout = true;
private boolean mLastTaskLaunchedWasFreeform;
@@ -132,27 +124,26 @@
setWillNotDraw(false);
SystemServicesProxy ssp = Recents.getSystemServices();
- mHandler = new Handler();
- mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler);
+ mTransitionHelper = new RecentsTransitionHelper(getContext());
mDividerSize = ssp.getDockedDividerSize(context);
mTouchHandler = new RecentsViewTouchHandler(this);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
- final float cornerRadius = context.getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_rounded_corners_radius);
LayoutInflater inflater = LayoutInflater.from(context);
- if (RecentsDebugFlags.Static.EnableHistory) {
- mHistoryButton = (TextView) inflater.inflate(R.layout.recents_history_button, this,
- false);
- mHistoryButton.setOnClickListener(new View.OnClickListener() {
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ float cornerRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
+ mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,
+ this, false);
+ mStackActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- EventBus.getDefault().send(new ToggleHistoryEvent());
+ // TODO: To be implemented
}
});
- addView(mHistoryButton);
- mHistoryButton.setClipToOutline(true);
- mHistoryButton.setOutlineProvider(new ViewOutlineProvider() {
+ addView(mStackActionButton);
+ mStackActionButton.setClipToOutline(true);
+ mStackActionButton.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
@@ -165,16 +156,17 @@
setBackground(mBackgroundScrim);
}
- /** Set/get the bsp root node */
- public void onResume(boolean isResumingFromVisible, TaskStack stack) {
+ /**
+ * Called from RecentsActivity when it is relaunched.
+ */
+ public void onReload(boolean isResumingFromVisible, boolean isTaskStackEmpty) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- if (mTaskStackView == null || !launchState.launchedReuseTaskStackViews) {
+ if (mTaskStackView == null) {
isResumingFromVisible = false;
- removeView(mTaskStackView);
mTaskStackView = new TaskStackView(getContext());
- mStack = mTaskStackView.getStack();
+ mTaskStackView.setSystemInsets(mSystemInsets);
addView(mTaskStackView);
}
@@ -183,9 +175,7 @@
mLastTaskLaunchedWasFreeform = false;
// Update the stack
- mTaskStackView.onResume(isResumingFromVisible);
- mTaskStackView.setTasks(stack, isResumingFromVisible /* notifyStackChanges */,
- true /* relayoutTaskStack */);
+ mTaskStackView.onReload(isResumingFromVisible);
if (isResumingFromVisible) {
// If we are already visible, then restore the background scrim
@@ -195,12 +185,20 @@
// Otherwise, defer until the enter animation completes to animate the scrim alpha with
// the tasks for the home animation.
if (launchState.launchedWhileDocking || launchState.launchedFromApp
- || mStack.getTaskCount() == 0) {
+ || isTaskStackEmpty) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
} else {
mBackgroundScrim.setAlpha(0);
}
}
+ }
+
+ /**
+ * Called from RecentsActivity when the task stack is updated.
+ */
+ public void updateStack(TaskStack stack) {
+ mStack = stack;
+ mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
// Update the top level view's visibilities
if (stack.getTaskCount() > 0) {
@@ -211,49 +209,19 @@
}
/**
+ * Returns the current TaskStack.
+ */
+ public TaskStack getStack() {
+ return mStack;
+ }
+
+ /**
* Returns whether the last task launched was in the freeform stack or not.
*/
public boolean isLastTaskLaunchedFreeform() {
return mLastTaskLaunchedWasFreeform;
}
- /**
- * Returns whether the history is visible or not.
- */
- public boolean isHistoryVisible() {
- return mHistoryView != null && mHistoryView.isVisible();
- }
-
- /**
- * Returns the currently set task stack.
- */
- public TaskStack getTaskStack() {
- return mStack;
- }
-
- /** Gets the next task in the stack - or if the last - the top task */
- public Task getNextTaskOrTopTask(Task taskToSearch) {
- Task returnTask = null;
- boolean found = false;
- if (mTaskStackView != null) {
- TaskStack stack = mTaskStackView.getStack();
- ArrayList<Task> taskList = stack.getStackTasks();
- // Iterate the stack views and try and find the focused task
- for (int j = taskList.size() - 1; j >= 0; --j) {
- Task task = taskList.get(j);
- // Return the next task in the line.
- if (found)
- return task;
- // Remember the first possible task as the top task.
- if (returnTask == null)
- returnTask = task;
- if (task == taskToSearch)
- found = true;
- }
- }
- return returnTask;
- }
-
/** Launches the focused task from the first stack if possible */
public boolean launchFocusedTask(int logEvent) {
if (mTaskStackView != null) {
@@ -306,37 +274,16 @@
return false;
}
- /** Adds the search bar */
- public void setSearchBar(RecentsAppWidgetHostView searchBar) {
- // Remove the previous search bar if one exists
- if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
- removeView(mSearchBar);
- }
- // Add the new search bar
- if (searchBar != null) {
- mSearchBar = searchBar;
- addView(mSearchBar);
- }
- }
-
- /** Returns whether there is currently a search bar */
- public boolean hasValidSearchBar() {
- return mSearchBar != null && !mSearchBar.isReinflateRequired();
- }
-
/**
* Hides the task stack and shows the empty view.
*/
public void showEmptyView(int msgResId) {
- if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) {
- mSearchBar.setVisibility(View.INVISIBLE);
- }
mTaskStackView.setVisibility(View.INVISIBLE);
mEmptyView.setText(msgResId);
mEmptyView.setVisibility(View.VISIBLE);
mEmptyView.bringToFront();
- if (RecentsDebugFlags.Static.EnableHistory) {
- mHistoryButton.bringToFront();
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ mStackActionButton.bringToFront();
}
}
@@ -346,15 +293,9 @@
public void hideEmptyView() {
mEmptyView.setVisibility(View.INVISIBLE);
mTaskStackView.setVisibility(View.VISIBLE);
- if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) {
- mSearchBar.setVisibility(View.VISIBLE);
- }
mTaskStackView.bringToFront();
- if (mSearchBar != null) {
- mSearchBar.bringToFront();
- }
- if (RecentsDebugFlags.Static.EnableHistory) {
- mHistoryButton.bringToFront();
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ mStackActionButton.bringToFront();
}
}
@@ -377,25 +318,10 @@
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- RecentsConfiguration config = Recents.getConfiguration();
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
- // Get the search bar bounds and measure the search bar layout
- Rect searchBarSpaceBounds = new Rect();
- if (mSearchBar != null) {
- config.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
- searchBarSpaceBounds);
- mSearchBar.measure(
- MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
- }
-
- Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
- mSystemInsets.right, searchBarSpaceBounds, taskStackBounds);
- if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
- mTaskStackView.setTaskStackBounds(taskStackBounds, mSystemInsets);
+ if (mTaskStackView.getVisibility() != GONE) {
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
@@ -405,23 +331,12 @@
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
- if (RecentsDebugFlags.Static.EnableHistory) {
- // Measure the history view
- if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
- measureChild(mHistoryView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
- }
-
- // Measure the history button within the constraints of the space above the stack
- Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
- measureChild(mHistoryButton,
- MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
- if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
- measureChild(mHistoryClearAllButton,
- MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
- }
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ // Measure the stack action button within the constraints of the space above the stack
+ Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
+ measureChild(mStackActionButton,
+ MeasureSpec.makeMeasureSpec(actionButtonRect.width(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(actionButtonRect.height(), MeasureSpec.AT_MOST));
}
setMeasuredDimension(width, height);
@@ -432,19 +347,7 @@
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- RecentsConfiguration config = Recents.getConfiguration();
-
- // Get the search bar bounds so that we lay it out
- Rect measuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
- Rect searchBarSpaceBounds = new Rect();
- if (mSearchBar != null) {
- config.getSearchBarBounds(measuredRect,
- mSystemInsets.top, searchBarSpaceBounds);
- mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
- searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
- }
-
- if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
+ if (mTaskStackView.getVisibility() != GONE) {
mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
}
@@ -459,39 +362,19 @@
mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
}
- if (RecentsDebugFlags.Static.EnableHistory) {
- // Layout the history view
- if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
- mHistoryView.layout(left, top, right, bottom);
- }
-
- // Layout the history button such that its drawable is start-aligned with the stack,
- // vertically centered in the available space above the stack
- Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
- int historyLeft = isLayoutRtl()
- ? historyButtonRect.right + mHistoryButton.getPaddingStart()
- - mHistoryButton.getMeasuredWidth()
- : historyButtonRect.left - mHistoryButton.getPaddingStart();
- int historyTop = historyButtonRect.top +
- (historyButtonRect.height() - mHistoryButton.getMeasuredHeight()) / 2;
- mHistoryButton.layout(historyLeft, historyTop,
- historyLeft + mHistoryButton.getMeasuredWidth(),
- historyTop + mHistoryButton.getMeasuredHeight());
-
- // Layout the history clear all button such that it is end-aligned with the stack,
- // vertically centered in the available space above the stack
- if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
- int clearAllLeft = isLayoutRtl()
- ? historyButtonRect.left - mHistoryClearAllButton.getPaddingStart()
- : historyButtonRect.right + mHistoryClearAllButton.getPaddingStart()
- - mHistoryClearAllButton.getMeasuredWidth();
- int clearAllTop = historyButtonRect.top +
- (historyButtonRect.height() - mHistoryClearAllButton.getMeasuredHeight()) /
- 2;
- mHistoryClearAllButton.layout(clearAllLeft, clearAllTop,
- clearAllLeft + mHistoryClearAllButton.getMeasuredWidth(),
- clearAllTop + mHistoryClearAllButton.getMeasuredHeight());
- }
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ // Layout the stack action button such that its drawable is start-aligned with the
+ // stack, vertically centered in the available space above the stack
+ Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
+ int buttonLeft = isLayoutRtl()
+ ? actionButtonRect.right + mStackActionButton.getPaddingStart()
+ - mStackActionButton.getMeasuredWidth()
+ : actionButtonRect.left - mStackActionButton.getPaddingStart();
+ int buttonTop = actionButtonRect.top +
+ (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
+ mStackActionButton.layout(buttonLeft, buttonTop,
+ buttonLeft + mStackActionButton.getMeasuredWidth(),
+ buttonTop + mStackActionButton.getMeasuredHeight());
}
if (mAwaitingFirstLayout) {
@@ -511,6 +394,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mSystemInsets.set(insets.getSystemWindowInsets());
+ mTaskStackView.setSystemInsets(mSystemInsets);
requestLayout();
return insets;
}
@@ -560,9 +444,9 @@
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
- if (RecentsDebugFlags.Static.EnableHistory) {
- // Hide the history button
- hideHistoryButton(taskViewExitToHomeDuration, false /* translate */);
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ // Hide the stack action button
+ hideStackActionButton(taskViewExitToHomeDuration, false /* translate */);
}
animateBackgroundScrim(0f, taskViewExitToHomeDuration);
}
@@ -611,31 +495,30 @@
event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
taskViewRect.right, taskViewRect.bottom);
- // Remove the task view after it is docked
- mTaskStackView.updateLayoutAlgorithm(false /* boundScroll */);
- stackLayout.getStackTransform(event.task, stackScroller.getStackScroll(), tmpTransform,
- null);
- tmpTransform.alpha = 0;
- tmpTransform.scale = 1f;
- tmpTransform.rect.set(taskViewRect);
- mTaskStackView.updateTaskViewToTransform(event.taskView, tmpTransform,
- new AnimationProps(125, Interpolators.ALPHA_OUT,
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Dock the task and launch it
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startTaskInDockedMode(getContext(), event.taskView,
- event.task.key.id, dockState.createMode);
+ final OnAnimationStartedListener startedListener = new OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
+ mTaskStackView.getStack().removeTask(event.task, AnimationProps.IMMEDIATE,
+ true /* fromDockGesture */);
+ }
+ };
- // Animate the stack accordingly
- AnimationProps stackAnim = new AnimationProps(
- TaskStackView.DEFAULT_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN);
- mTaskStackView.getStack().removeTask(event.task, stackAnim,
- true /* fromDockGesture */);
- }
- }));
+ // Dock the task and launch it
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
+ final Rect taskRect = getTaskRect(event.taskView);
+ IAppTransitionAnimationSpecsFuture future = mTransitionHelper.getAppTransitionFuture(
+ new AnimationSpecComposer() {
+ @Override
+ public List<AppTransitionAnimationSpec> composeSpecs() {
+ return mTransitionHelper.composeDockAnimationSpec(
+ event.taskView, taskRect);
+ }
+ });
+ ssp.overridePendingAppTransitionMultiThumbFuture(future,
+ mTransitionHelper.wrapStartedListener(startedListener),
+ true /* scaleUp */);
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP);
} else {
@@ -645,6 +528,15 @@
}
}
+ private Rect getTaskRect(TaskView taskView) {
+ int[] location = taskView.getLocationOnScreen();
+ int viewX = location[0];
+ int viewY = location[1];
+ return new Rect(viewX, viewY,
+ (int) (viewX + taskView.getWidth() * taskView.getScaleX()),
+ (int) (viewY + taskView.getHeight() * taskView.getScaleY()));
+ }
+
public final void onBusEvent(DraggingInRecentsEvent event) {
if (mTaskStackView.getTaskViews().size() > 0) {
setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
@@ -687,144 +579,47 @@
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
}
- public final void onBusEvent(ToggleHistoryEvent event) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
+ public final void onBusEvent(ShowStackActionButtonEvent event) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
return;
}
- if (mHistoryView != null && mHistoryView.isVisible()) {
- EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
- } else {
- EventBus.getDefault().send(new ShowHistoryEvent());
- }
+ showStackActionButton(150, event.translate);
}
- public final void onBusEvent(ShowHistoryEvent event) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
+ public final void onBusEvent(HideStackActionButtonEvent event) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
return;
}
- if (mHistoryView == null) {
- LayoutInflater inflater = LayoutInflater.from(getContext());
- mHistoryView = (RecentsHistoryView) inflater.inflate(R.layout.recents_history, this,
- false);
- addView(mHistoryView);
-
- final float cornerRadius = getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_rounded_corners_radius);
- mHistoryClearAllButton = (TextView) inflater.inflate(
- R.layout.recents_history_clear_all_button, this, false);
- mHistoryClearAllButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- EventBus.getDefault().send(new ClearHistoryEvent());
- }
- });
- mHistoryClearAllButton.setClipToOutline(true);
- mHistoryClearAllButton.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
- }
- });
- addView(mHistoryClearAllButton);
-
- // Since this history view is inflated by a view stub after the insets have already
- // been applied, we have to set them ourselves initial from the insets that were last
- // provided.
- mHistoryView.setSystemInsets(mSystemInsets);
- mHistoryView.setHeaderHeight(mHistoryButton.getMeasuredHeight());
- mHistoryButton.bringToFront();
- mHistoryClearAllButton.bringToFront();
- }
-
- // Animate the empty view in parallel with the history view (the task view animations are
- // handled in TaskStackView)
- Rect stackRect = mTaskStackView.mLayoutAlgorithm.mStackRect;
- if (mEmptyView.getVisibility() == View.VISIBLE) {
- int historyTransitionDuration = getResources().getInteger(
- R.integer.recents_history_transition_duration);
- mEmptyView.animate()
- .alpha(0f)
- .translationY(stackRect.height() / 2)
- .setDuration(historyTransitionDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mEmptyView.setVisibility(View.INVISIBLE);
- }
- })
- .start();
- }
-
- mHistoryView.show(mStack, stackRect.height(), mHistoryClearAllButton);
- }
-
- public final void onBusEvent(HideHistoryEvent event) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
- return;
- }
-
- // Animate the empty view in parallel with the history view (the task view animations are
- // handled in TaskStackView)
- Rect stackRect = mTaskStackView.mLayoutAlgorithm.mStackRect;
- if (mStack.getTaskCount() == 0) {
- int historyTransitionDuration = getResources().getInteger(
- R.integer.recents_history_transition_duration);
- mEmptyView.setVisibility(View.VISIBLE);
- mEmptyView.animate()
- .alpha(1f)
- .translationY(0)
- .setDuration(historyTransitionDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .start();
- }
-
- mHistoryView.hide(event.animate, stackRect.height(), mHistoryClearAllButton);
- }
-
- public final void onBusEvent(ShowHistoryButtonEvent event) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
- return;
- }
-
- showHistoryButton(150, event.translate);
- }
-
- public final void onBusEvent(HideHistoryButtonEvent event) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
- return;
- }
-
- hideHistoryButton(100, true /* translate */);
+ hideStackActionButton(100, true /* translate */);
}
/**
- * Shows the history button.
+ * Shows the stack action button.
*/
- private void showHistoryButton(final int duration, final boolean translate) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
+ private void showStackActionButton(final int duration, final boolean translate) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
return;
}
final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
- if (mHistoryButton.getVisibility() == View.INVISIBLE) {
- mHistoryButton.setVisibility(View.VISIBLE);
- mHistoryButton.setAlpha(0f);
+ if (mStackActionButton.getVisibility() == View.INVISIBLE) {
+ mStackActionButton.setVisibility(View.VISIBLE);
+ mStackActionButton.setAlpha(0f);
if (translate) {
- mHistoryButton.setTranslationY(-mHistoryButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
} else {
- mHistoryButton.setTranslationY(0f);
+ mStackActionButton.setTranslationY(0f);
}
postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
if (translate) {
- mHistoryButton.animate()
+ mStackActionButton.animate()
.translationY(0f);
}
- mHistoryButton.animate()
+ mStackActionButton.animate()
.alpha(1f)
.setDuration(duration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
@@ -837,40 +632,40 @@
}
/**
- * Hides the history button.
+ * Hides the stack action button.
*/
- private void hideHistoryButton(int duration, boolean translate) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
+ private void hideStackActionButton(int duration, boolean translate) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
return;
}
final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
- hideHistoryButton(duration, translate, postAnimationTrigger);
+ hideStackActionButton(duration, translate, postAnimationTrigger);
postAnimationTrigger.flushLastDecrementRunnables();
}
/**
- * Hides the history button.
+ * Hides the stack action button.
*/
- private void hideHistoryButton(int duration, boolean translate,
- final ReferenceCountedTrigger postAnimationTrigger) {
- if (!RecentsDebugFlags.Static.EnableHistory) {
+ private void hideStackActionButton(int duration, boolean translate,
+ final ReferenceCountedTrigger postAnimationTrigger) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
return;
}
- if (mHistoryButton.getVisibility() == View.VISIBLE) {
+ if (mStackActionButton.getVisibility() == View.VISIBLE) {
if (translate) {
- mHistoryButton.animate()
- .translationY(-mHistoryButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.animate()
+ .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
}
- mHistoryButton.animate()
+ mStackActionButton.animate()
.alpha(0f)
.setDuration(duration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.withEndAction(new Runnable() {
@Override
public void run() {
- mHistoryButton.setVisibility(View.INVISIBLE);
+ mStackActionButton.setVisibility(View.INVISIBLE);
postAnimationTrigger.decrement();
}
})
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 19b219a..9c8189a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -16,11 +16,9 @@
package com.android.systemui.recents.views;
-import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.view.View;
-import android.view.ViewPropertyAnimator;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -48,7 +46,7 @@
/**
* Prepares the scrim views for animating when entering Recents. This will be called before
- * the first draw.
+ * the first draw, unless we are updating the scrim on configuration change.
*/
public void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
mHasNavBarScrim = hasNavBarScrim;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index b36d5d1..1c7d609 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -65,6 +65,12 @@
*/
void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
ReferenceCountedTrigger postAnimationTrigger);
+
+ /**
+ * Callback to start the animation for the front {@link TaskView} if there is no launch
+ * target.
+ */
+ void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled);
}
private static final int FRAME_OFFSET_MS = 16;
@@ -126,9 +132,9 @@
int offscreenYOffset = stackLayout.mStackRect.height();
int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
- R.dimen.recents_task_view_affiliate_group_enter_offset);
+ R.dimen.recents_task_stack_animation_affiliate_enter_offset);
int launchedWhileDockingOffset = res.getDimensionPixelSize(
- R.dimen.recents_task_view_launched_while_docking_offset);
+ R.dimen.recents_task_stack_animation_launched_while_docking_offset);
// Prepare each of the task views for their enter animation from front to back
List<TaskView> taskViews = mStackView.getTaskViews();
@@ -146,8 +152,6 @@
if (hideTask) {
tv.setVisibility(View.INVISIBLE);
- } else if (launchState.launchedHasConfigurationChanged) {
- // Just load the views as-is
} else if (launchState.launchedFromApp && !launchState.launchedWhileDocking) {
if (task.isLaunchTarget) {
tv.onPrepareLaunchTargetForEnterAnimation();
@@ -164,6 +168,7 @@
// Move the task view off screen (below) so we can animate it in
RectF bounds = new RectF(mTmpTransform.rect);
bounds.offset(0, offscreenYOffset);
+ tv.setAlpha(0f);
tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
(int) bounds.bottom);
} else if (launchState.launchedWhileDocking) {
@@ -232,7 +237,7 @@
@Override
public void onAnimationEnd(Animator animation) {
postAnimationTrigger.decrement();
- tv.setClipViewInStack(false);
+ tv.setClipViewInStack(true);
}
});
postAnimationTrigger.increment();
@@ -254,6 +259,9 @@
.setListener(postAnimationTrigger.decrementOnAnimationEnd());
postAnimationTrigger.increment();
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+ if (i == taskViewCount - 1) {
+ tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
+ }
} else if (launchState.launchedWhileDocking) {
// Animate the tasks up
AnimationProps taskAnimation = new AnimationProps()
@@ -312,6 +320,7 @@
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
+ mTmpTransform.alpha = 0f;
mTmpTransform.rect.offset(0, offscreenYOffset);
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
@@ -330,7 +339,7 @@
int taskViewExitToAppDuration = res.getInteger(
R.integer.recents_task_exit_to_app_duration);
int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
- R.dimen.recents_task_view_affiliate_group_enter_offset);
+ R.dimen.recents_task_stack_animation_affiliate_enter_offset);
Task launchingTask = launchingTaskView.getTask();
List<TaskView> taskViews = mStackView.getTaskViews();
@@ -343,6 +352,12 @@
if (tv == launchingTaskView) {
tv.setClipViewInStack(false);
+ postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ tv.setClipViewInStack(true);
+ }
+ });
tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
screenPinningRequested, postAnimationTrigger);
} else if (currentTaskOccludesLaunchTarget) {
@@ -375,7 +390,8 @@
int taskViewRemoveAnimTranslationXPx = res.getDimensionPixelSize(
R.dimen.recents_task_view_remove_anim_translation_x);
- // Disabling clipping with the stack while the view is animating away
+ // Disabling clipping with the stack while the view is animating away, this will get
+ // restored when the task is next picked up from the view pool
deleteTaskView.setClipViewInStack(false);
// Compose the new animation and transform and star the animation
@@ -384,9 +400,6 @@
@Override
public void onAnimationEnd(Animator animation) {
postAnimationTrigger.decrement();
-
- // Re-enable clipping with the stack (we will reuse this view)
- deleteTaskView.setClipViewInStack(true);
}
});
postAnimationTrigger.increment();
@@ -399,62 +412,6 @@
}
/**
- * Starts the animation to hide the {@link TaskView}s when the history is shown.
- */
- public void startShowHistoryAnimation(ReferenceCountedTrigger postAnimationTrigger) {
- Resources res = mStackView.getResources();
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
-
- int offscreenY = stackLayout.mStackRect.bottom;
- int historyTransitionDuration = res.getInteger(
- R.integer.recents_history_transition_duration);
- int startDelayIncr = 16;
-
- List<TaskView> taskViews = mStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
- AnimationProps taskAnimation = new AnimationProps(startDelayIncr * i,
- historyTransitionDuration, Interpolators.FAST_OUT_SLOW_IN,
- postAnimationTrigger.decrementOnAnimationEnd());
- postAnimationTrigger.increment();
-
- stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
- null);
- mTmpTransform.alpha = 0f;
- mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
- }
- }
-
- /**
- * Starts the animation to show the {@link TaskView}s when the history is hidden.
- */
- public void startHideHistoryAnimation() {
- Resources res = mStackView.getResources();
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
-
- int historyTransitionDuration = res.getInteger(
- R.integer.recents_history_transition_duration);
- int startDelayIncr = 16;
-
- List<TaskView> taskViews = mStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- AnimationProps taskAnimation = new AnimationProps(startDelayIncr * i,
- historyTransitionDuration, Interpolators.FAST_OUT_SLOW_IN);
- stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
- mTmpTransform, null);
- mTmpTransform.alpha = 1f;
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
- }
- }
-
- /**
* Starts the animation to focus the next {@link TaskView} when paging through recents.
*
* @return whether or not this will trigger a scroll in the stack
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 6df5884..8a1727a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -16,7 +16,9 @@
package com.android.systemui.recents.views;
+import android.annotation.IntDef;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.Rect;
@@ -36,6 +38,8 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -97,8 +101,10 @@
}
/**
- * The layout logic for a TaskStackView. This layout can have two states focused and unfocused,
- * and in the focused state, there is a task that is displayed more prominently in the stack.
+ * The layout logic for a TaskStackView. This layout needs to be able to calculate the stack layout
+ * without an activity-specific context only with the information passed in. This layout can have
+ * two states focused and unfocused, and in the focused state, there is a task that is displayed
+ * more prominently in the stack.
*/
public class TaskStackLayoutAlgorithm {
@@ -107,13 +113,28 @@
public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
public static final float OUTLINE_ALPHA_MAX_VALUE = 2f;
- // The maximum dim on the tasks
+ // The medium/maximum dim on the tasks
+ private static final float MED_DIM = 0.15f;
private static final float MAX_DIM = 0.25f;
// The various focus states
public static final int STATE_FOCUSED = 1;
public static final int STATE_UNFOCUSED = 0;
+ // The side that an offset is anchored
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FROM_TOP, FROM_BOTTOM})
+ public @interface AnchorSide {}
+ private static final int FROM_TOP = 0;
+ private static final int FROM_BOTTOM = 1;
+
+ // The extent that we care about when calculating fractions
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({WIDTH, HEIGHT})
+ public @interface Extent {}
+ private static final int WIDTH = 0;
+ private static final int HEIGHT = 1;
+
public interface TaskStackLayoutAlgorithmCallbacks {
void onFocusStateChanged(int prevFocusState, int curFocusState);
}
@@ -165,22 +186,24 @@
* @param taskStackBounds the full rect that the freeform rect can take up
*/
public void computeRects(Rect freeformRectOut, Rect stackRectOut,
- Rect taskStackBounds, int widthPadding, int heightPadding, int stackBottomOffset) {
- int availableHeight = taskStackBounds.height() - stackBottomOffset;
+ Rect taskStackBounds, int topMargin, int freeformGap, int stackBottomOffset) {
+ // The freeform height is the visible height (not including system insets) - padding
+ // above freeform and below stack - gap between the freeform and stack
+ int availableHeight = taskStackBounds.height() - topMargin - stackBottomOffset;
int ffPaddedHeight = (int) (availableHeight * freeformHeightPct);
- int ffHeight = Math.max(0, ffPaddedHeight - (2 * heightPadding));
- freeformRectOut.set(taskStackBounds.left + widthPadding,
- taskStackBounds.top + heightPadding,
- taskStackBounds.right - widthPadding,
- taskStackBounds.top + heightPadding + ffHeight);
- stackRectOut.set(taskStackBounds.left + widthPadding,
+ int ffHeight = Math.max(0, ffPaddedHeight - freeformGap);
+ freeformRectOut.set(taskStackBounds.left,
+ taskStackBounds.top + topMargin,
+ taskStackBounds.right,
+ taskStackBounds.top + topMargin + ffHeight);
+ stackRectOut.set(taskStackBounds.left,
taskStackBounds.top,
- taskStackBounds.right - widthPadding,
+ taskStackBounds.right,
taskStackBounds.bottom);
if (ffPaddedHeight > 0) {
stackRectOut.top += ffPaddedHeight;
} else {
- stackRectOut.top += heightPadding;
+ stackRectOut.top += topMargin;
}
}
}
@@ -204,44 +227,53 @@
// The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
@ViewDebug.ExportedProperty(category="recents")
public Rect mTaskRect = new Rect();
- // The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
+ // The freeform workspace bounds, inset by the top system insets and is a fixed height
@ViewDebug.ExportedProperty(category="recents")
public Rect mFreeformRect = new Rect();
- // The stack bounds, inset from the top by the search bar, and runs to
- // the bottom of the screen
+ // The stack bounds, inset from the top system insets, and runs to the bottom of the screen
@ViewDebug.ExportedProperty(category="recents")
public Rect mStackRect = new Rect();
// This is the current system insets
@ViewDebug.ExportedProperty(category="recents")
public Rect mSystemInsets = new Rect();
- // This is the bounds of the history button above the stack rect
+ // This is the bounds of the stack action above the stack rect
@ViewDebug.ExportedProperty(category="recents")
- public Rect mHistoryButtonRect = new Rect();
+ public Rect mStackActionButtonRect = new Rect();
// The visible ranges when the stack is focused and unfocused
private Range mUnfocusedRange;
private Range mFocusedRange;
- // The initial offset from the top and bottom of the stack
+ // The base top margin for the stack from the system insets
@ViewDebug.ExportedProperty(category="recents")
- private int mInitialTopPeekHeight;
+ private int mBaseTopMargin;
+ // The base side margin for the stack from the system insets
@ViewDebug.ExportedProperty(category="recents")
- private int mInitialBottomPeekHeight;
+ private int mBaseSideMargin;
+ // The base bottom margin for the stack from the system insets
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mBaseBottomMargin;
+ private int mMinMargin;
- // The offset from the top when scrolled to the top of the stack
+ // The gap between the freeform and stack layouts
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mFreeformStackGap;
+
+ // The initial offset that the focused task is from the top
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mInitialTopOffset;
+ private int mBaseInitialTopOffset;
+ // The initial offset that the launch-from task is from the bottom
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mInitialBottomOffset;
+ private int mBaseInitialBottomOffset;
+
+ // The height between the top margin and the top of the focused task
@ViewDebug.ExportedProperty(category="recents")
private int mFocusedTopPeekHeight;
+ // The height between the bottom margin and the top of task in front of the focused task
@ViewDebug.ExportedProperty(category="recents")
- private int mFocusedBottomTaskPeekHeight;
-
- // The offset from the top of the stack to the top of the bounds when the stack is scrolled to
- // the end
- @ViewDebug.ExportedProperty(category="recents")
- private int mStackTopOffset;
-
- // The height of the header bar
- @ViewDebug.ExportedProperty(category="recents")
- private int mHeaderBarHeight;
+ private int mFocusedBottomPeekHeight;
// The offset from the bottom of the stack to the bottom of the bounds when the stack is
// scrolled to the front
@@ -307,9 +339,23 @@
TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
+ Resources res = context.getResources();
mContext = context;
mCb = cb;
mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
+ mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
+ mBaseTopMargin = getDimensionForDevice(res,
+ R.dimen.recents_layout_top_margin_phone,
+ R.dimen.recents_layout_top_margin_tablet,
+ R.dimen.recents_layout_top_margin_tablet_xlarge);
+ mBaseSideMargin = getDimensionForDevice(res,
+ R.dimen.recents_layout_side_margin_phone,
+ R.dimen.recents_layout_side_margin_tablet,
+ R.dimen.recents_layout_side_margin_tablet_xlarge);
+ mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
+ mFreeformStackGap =
+ res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
+
reloadOnConfigurationChange(context);
}
@@ -323,17 +369,25 @@
mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
res.getFloat(R.integer.recents_layout_unfocused_range_max));
mFocusState = getInitialFocusState();
- mInitialTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_initial_top_peek_size);
- mInitialBottomPeekHeight =
- res.getDimensionPixelSize(R.dimen.recents_initial_bottom_peek_size);
- mFocusedTopPeekHeight =
- res.getDimensionPixelSize(R.dimen.recents_layout_focused_top_peek_size);
- mFocusedBottomTaskPeekHeight =
- res.getDimensionPixelSize(R.dimen.recents_layout_focused_bottom_task_peek_size);
- mHeaderBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-
- mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
- mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
+ mFocusedTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_top_peek_size);
+ mFocusedBottomPeekHeight =
+ res.getDimensionPixelSize(R.dimen.recents_layout_bottom_peek_size);
+ mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_min);
+ mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_max);
+ mBaseInitialTopOffset = getDimensionForDevice(res,
+ R.dimen.recents_layout_initial_top_offset_phone_port,
+ R.dimen.recents_layout_initial_top_offset_phone_land,
+ R.dimen.recents_layout_initial_top_offset_tablet,
+ R.dimen.recents_layout_initial_top_offset_tablet,
+ R.dimen.recents_layout_initial_top_offset_tablet,
+ R.dimen.recents_layout_initial_top_offset_tablet);
+ mBaseInitialBottomOffset = getDimensionForDevice(res,
+ R.dimen.recents_layout_initial_bottom_offset_phone_port,
+ R.dimen.recents_layout_initial_bottom_offset_phone_land,
+ R.dimen.recents_layout_initial_bottom_offset_tablet,
+ R.dimen.recents_layout_initial_bottom_offset_tablet,
+ R.dimen.recents_layout_initial_bottom_offset_tablet,
+ R.dimen.recents_layout_initial_bottom_offset_tablet);
mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
}
@@ -372,52 +426,52 @@
}
/**
- * Computes the stack and task rects. The given task stack bounds is the whole bounds not
- * including the search bar.
+ * Computes the stack and task rects. The given task stack bounds already has the top/right
+ * insets and left/right padding already applied.
*/
- public void initialize(Rect taskStackBounds, StackState state) {
- RecentsConfiguration config = Recents.getConfiguration();
- int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
- int heightPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_stack_top_padding);
+ public void initialize(Rect windowRect, Rect taskStackBounds, StackState state) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
Rect lastStackRect = new Rect(mStackRect);
+ Rect displayRect = ssp.getDisplayRect();
- // The freeform height is the visible height (not including system insets) - padding above
- // freeform and below stack - gap between the freeform and stack
+ int topMargin = getScaleForExtent(windowRect, displayRect, mBaseTopMargin, mMinMargin, HEIGHT);
+ int bottomMargin = getScaleForExtent(windowRect, displayRect, mBaseBottomMargin, mMinMargin,
+ HEIGHT);
+ mInitialTopOffset = getScaleForExtent(windowRect, displayRect, mBaseInitialTopOffset,
+ mMinMargin, HEIGHT);
+ mInitialBottomOffset = mBaseInitialBottomOffset;
+
+ // Compute the stack bounds
mState = state;
- mStackTopOffset = mFocusedTopPeekHeight + heightPadding;
- mStackBottomOffset = mSystemInsets.bottom + heightPadding;
- state.computeRects(mFreeformRect, mStackRect, taskStackBounds, widthPadding, heightPadding,
- mStackBottomOffset);
- // The history button will take the full un-padded header space above the stack
- mHistoryButtonRect.set(mStackRect.left, mStackRect.top - heightPadding,
+ mStackBottomOffset = mSystemInsets.bottom + bottomMargin;
+ state.computeRects(mFreeformRect, mStackRect, taskStackBounds, topMargin,
+ mFreeformStackGap, mStackBottomOffset);
+
+ // The stack action button will take the full un-padded header space above the stack
+ mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
- // Anchor the task rect to the top-center of the non-freeform stack rect
- float aspect = (float) (taskStackBounds.width() - mSystemInsets.left - mSystemInsets.right)
- / (taskStackBounds.height() - mSystemInsets.bottom);
- int width = mStackRect.width();
- int minHeight = mStackRect.height() - mFocusedTopPeekHeight - mStackBottomOffset;
- int height = (int) Math.min(width / aspect, minHeight);
- mTaskRect.set(mStackRect.left, mStackRect.top,
- mStackRect.left + width, mStackRect.top + height);
+ // Anchor the task rect top aligned to the non-freeform stack rect
+ float aspect = (float) (windowRect.width() - (mSystemInsets.left + mSystemInsets.right)) /
+ (windowRect.height() - (mSystemInsets.top + mSystemInsets.bottom));
+ int minHeight = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
+ int height = (int) Math.min(mStackRect.width() / aspect, minHeight);
+ mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height);
// Short circuit here if the stack rects haven't changed so we don't do all the work below
- if (lastStackRect.equals(mStackRect)) {
- return;
+ if (!lastStackRect.equals(mStackRect)) {
+ // Reinitialize the focused and unfocused curves
+ mUnfocusedCurve = constructUnfocusedCurve();
+ mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
+ mFocusedCurve = constructFocusedCurve();
+ mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
+ mUnfocusedDimCurve = constructUnfocusedDimCurve();
+ mUnfocusedDimCurveInterpolator = new FreePathInterpolator(mUnfocusedDimCurve);
+ mFocusedDimCurve = constructFocusedDimCurve();
+ mFocusedDimCurveInterpolator = new FreePathInterpolator(mFocusedDimCurve);
+
+ updateFrontBackTransforms();
}
-
- // Reinitialize the focused and unfocused curves
- mUnfocusedCurve = constructUnfocusedCurve();
- mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
- mFocusedCurve = constructFocusedCurve();
- mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
- mUnfocusedDimCurve = constructUnfocusedDimCurve();
- mUnfocusedDimCurveInterpolator = new FreePathInterpolator(mUnfocusedDimCurve);
- mFocusedDimCurve = constructFocusedDimCurve();
- mFocusedDimCurveInterpolator = new FreePathInterpolator(mFocusedDimCurve);
-
- updateFrontBackTransforms();
}
/**
@@ -435,7 +489,7 @@
ArrayList<Task> tasks = stack.getStackTasks();
if (tasks.isEmpty()) {
mFrontMostTaskP = 0;
- mMinScrollP = mMaxScrollP = 0;
+ mMinScrollP = mMaxScrollP = mInitialScrollP = 0;
mNumStackTasks = mNumFreeformTasks = 0;
return;
}
@@ -492,8 +546,7 @@
// Set the max scroll to be the point where the front most task is visible with the
// stack bottom offset
int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
- float maxBottomOffsetPct = (float) maxBottomOffset / mStackRect.height();
- float maxBottomNormX = mUnfocusedCurveInterpolator.getX(maxBottomOffsetPct);
+ float maxBottomNormX = getNormalizedXFromUnfocusedY(maxBottomOffset, FROM_BOTTOM);
mUnfocusedRange.offset(0f);
mMinScrollP = 0;
mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
@@ -502,34 +555,19 @@
launchState.launchedFromAppDocked;
if (scrollToFront) {
mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+ mInitialNormX = null;
} else {
- mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
- }
+ float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
+ mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2)) -
+ Math.max(0, mUnfocusedRange.getAbsoluteX(normX)));
- // Set the initial scroll to the predefined state (which differs from the stack)
- int initialPeekOffset = mStackRect.height() - mInitialTopPeekHeight;
- float initialPeekOffsetPct = (float) initialPeekOffset / mStackRect.height();
- float initialPeekOffsetNormX = mUnfocusedCurveInterpolator.getX(initialPeekOffsetPct);
- float initialFocusedOffset = mStackRect.height() - mInitialTopPeekHeight -
- (mHeaderBarHeight * 1f) + 1;
- float initialFocusedOffsetPct = initialFocusedOffset / mStackRect.height();
- float initialFocusedNormX = mUnfocusedCurveInterpolator.getX(initialFocusedOffsetPct);
- float initialBottomOffset = mStackBottomOffset +
- (ssp.hasDockedTask()
- ? mHeaderBarHeight
- : mInitialBottomPeekHeight);
- float initialBottomOffsetPct = initialBottomOffset / mStackRect.height();
- float initialBottomNormX = mUnfocusedCurveInterpolator.getX(initialBottomOffsetPct);
- /*
- // If we want to offset the top card slightly
- mInitialNormX = scrollToFront
- ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f }
- : new float[] { initialBottomNormX, initialFocusedNormX,
- initialPeekOffsetNormX, 0f };
- */
- mInitialNormX = scrollToFront
- ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f }
- : new float[] { initialBottomNormX, 0.5f, 0f };
+ // Set the initial scroll to the predefined state (which differs from the stack)
+ mInitialNormX = new float[] {
+ getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
+ FROM_BOTTOM),
+ normX
+ };
+ }
}
}
@@ -669,7 +707,7 @@
Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
currentRange.offset(mInitialScrollP);
int taskBarHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_bar_height);
+ R.dimen.recents_task_view_header_height);
int numVisibleTasks = Math.max(mNumFreeformTasks, 1);
int numVisibleThumbnails = Math.max(mNumFreeformTasks, 1);
float prevScreenY = Integer.MAX_VALUE;
@@ -814,7 +852,7 @@
// in screen space
float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
int centerYOffset = (mStackRect.top - mTaskRect.top) +
- (mStackRect.height() - mTaskRect.height()) / 2;
+ (mStackRect.height() - mSystemInsets.bottom - mTaskRect.height()) / 2;
y = centerYOffset + getYForDeltaP(tmpP, 0);
z = mMaxTranslationZ;
dimAlpha = 0f;
@@ -869,6 +907,25 @@
}
/**
+ * Returns the original scroll progress to scroll to such that the top of the task is at the top
+ * of the stack.
+ */
+ float getStackScrollForTaskIgnoreOverrides(Task t) {
+ return (float) mTaskIndexMap.get(t.key.id, 0);
+ }
+
+ /**
+ * Returns the scroll progress to scroll to such that the top of the task at the initial top
+ * offset (which is at the task's brightest point).
+ */
+ float getStackScrollForTaskAtInitialOffset(Task t) {
+ float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
+ mUnfocusedRange.offset(0f);
+ return Utilities.clamp((float) mTaskIndexMap.get(t.key.id, 0) - Math.max(0,
+ mUnfocusedRange.getAbsoluteX(normX)), mMinScrollP, mMaxScrollP);
+ }
+
+ /**
* Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc
* length of the curve. We know the curve is mostly flat, so we just map the length of the
* screen along the arc-length proportionally (1/arclength).
@@ -890,6 +947,81 @@
}
/**
+ * Returns the task stack bounds in the current orientation. This rect takes into account the
+ * top and right system insets (but not the bottom inset) and left/right paddings, but _not_
+ * the top/bottom padding or insets.
+ */
+ public void getTaskStackBounds(Rect windowRect, int topInset, int rightInset,
+ Rect taskStackBounds) {
+ taskStackBounds.set(windowRect.left, windowRect.top + topInset,
+ windowRect.right - rightInset, windowRect.bottom);
+
+ // Ensure that the new width is at most the smaller display edge size
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ Rect displayRect = ssp.getDisplayRect();
+ int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
+ WIDTH);
+ int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
+ if (ssp.getDisplayOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+ // If we are in landscape, calculate the width of the stack in portrait and ensure that
+ // we are not larger than that size
+ Rect portraitDisplayRect = new Rect(0, 0,
+ Math.min(displayRect.width(), displayRect.height()),
+ Math.max(displayRect.width(), displayRect.height()));
+ int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect,
+ mBaseSideMargin, mMinMargin, WIDTH);
+ targetStackWidth = Math.min(targetStackWidth,
+ portraitDisplayRect.width() - 2 * portraitSideMargin);
+ }
+ taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0);
+ }
+
+ /**
+ * Retrieves resources that are constant regardless of the current configuration of the device.
+ */
+ public static int getDimensionForDevice(Resources res, int phoneResId,
+ int tabletResId, int xlargeTabletResId) {
+ return getDimensionForDevice(res, phoneResId, phoneResId, tabletResId, tabletResId,
+ xlargeTabletResId, xlargeTabletResId);
+ }
+
+ /**
+ * Retrieves resources that are constant regardless of the current configuration of the device.
+ */
+ public static int getDimensionForDevice(Resources res, int phonePortResId, int phoneLandResId,
+ int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
+ int xlargeTabletLandResId) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ boolean isLandscape = Recents.getSystemServices().getDisplayOrientation() ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ if (config.isXLargeScreen) {
+ return res.getDimensionPixelSize(isLandscape
+ ? xlargeTabletLandResId
+ : xlargeTabletPortResId);
+ } else if (config.isLargeScreen) {
+ return res.getDimensionPixelSize(isLandscape
+ ? tabletLandResId
+ : tabletPortResId);
+ } else {
+ return res.getDimensionPixelSize(isLandscape
+ ? phoneLandResId
+ : phonePortResId);
+ }
+ }
+
+ /**
+ * Returns the normalized x on the unfocused curve given an absolute Y position (relative to the
+ * stack height).
+ */
+ private float getNormalizedXFromUnfocusedY(float y, @AnchorSide int fromSide) {
+ float offset = (fromSide == FROM_TOP)
+ ? mStackRect.height() - y
+ : y;
+ float offsetPct = offset / mStackRect.height();
+ return mUnfocusedCurveInterpolator.getX(offsetPct);
+ }
+
+ /**
* Creates a new path for the focused curve.
*/
private Path constructFocusedCurve() {
@@ -897,13 +1029,12 @@
// linear pieces that goes from (0,1) through (0.5, peek height offset),
// (0.5, bottom task offsets), and (1,0).
float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
- float bottomPeekHeightPct = Math.max(
- mSystemInsets.bottom + mFocusedRange.relativeMax * mFocusedBottomTaskPeekHeight,
- mStackBottomOffset + mFocusedBottomTaskPeekHeight) / mStackRect.height();
+ float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) /
+ mStackRect.height();
Path p = new Path();
p.moveTo(0f, 1f);
p.lineTo(0.5f, 1f - topPeekHeightPct);
- p.lineTo(0.5f + (0.5f / mFocusedRange.relativeMax), bottomPeekHeightPct);
+ p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), bottomPeekHeightPct);
p.lineTo(1f, 0f);
return p;
}
@@ -919,16 +1050,16 @@
// the control point of the second bezier such that between it and a first known point,
// there is a tangent at (0.5, peek height offset).
float cpoint1X = 0.4f;
- float cpoint1Y = 1f;
- float peekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
- float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
+ float cpoint1Y = 0.975f;
+ float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
+ float slope = ((1f - topPeekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
float b = 1f - slope * cpoint1X;
float cpoint2X = 0.65f;
float cpoint2Y = slope * cpoint2X + b;
Path p = new Path();
p.moveTo(0f, 1f);
- p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - peekHeightPct);
- p.cubicTo(0.5f, 1f - peekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
+ p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - topPeekHeightPct);
+ p.cubicTo(0.5f, 1f - topPeekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
return p;
}
@@ -950,15 +1081,32 @@
* Creates a new path for the unfocused dim curve.
*/
private Path constructUnfocusedDimCurve() {
+ float focusX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
+ float cpoint2X = focusX + (1f - focusX) / 2;
Path p = new Path();
// The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
// task), then goes back to max dim towards the front of the stack
p.moveTo(0f, MAX_DIM);
- p.cubicTo(0.1f, MAX_DIM, 0.4f, 0.0f, 0.5f, 0f);
- p.cubicTo(0.6f, 0f, 0.9f, MAX_DIM / 2f, 1f, MAX_DIM / 2f);
+ p.cubicTo(focusX * 0.5f, MAX_DIM, focusX * 0.75f, MAX_DIM * 0.75f, focusX, 0f);
+ p.cubicTo(cpoint2X, 0f, cpoint2X, MED_DIM, 1f, MED_DIM);
return p;
}
+ /**
+ * Scales the given {@param value} to the scale of the {@param instance} rect relative to the
+ * {@param other} rect in the {@param extent} side.
+ */
+ private int getScaleForExtent(Rect instance, Rect other, int value, int minValue,
+ @Extent int extent) {
+ if (extent == WIDTH) {
+ float scale = Utilities.clamp01((float) instance.width() / other.width());
+ return Math.max(minValue, (int) (scale * value));
+ } else if (extent == HEIGHT) {
+ float scale = Utilities.clamp01((float) instance.height() / other.height());
+ return Math.max(minValue, (int) (scale * value));
+ }
+ return value;
+ }
/**
* Updates the current transforms that would put a TaskView at the front and back of the stack.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 0b20d21..9032871 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -22,6 +22,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -59,8 +60,7 @@
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
-import com.android.systemui.recents.events.activity.HideHistoryEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
@@ -68,8 +68,7 @@
import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
-import com.android.systemui.recents.events.activity.ShowHistoryEvent;
+import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
@@ -84,12 +83,13 @@
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -106,23 +106,36 @@
private final static String KEY_SAVED_STATE_LAYOUT_STACK_SCROLL =
"saved_instance_state_layout_stack_scroll";
- // The thresholds at which to show/hide the history button.
- private static final float SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
- private static final float HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
+ // The thresholds at which to show/hide the stack action button.
+ private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
+ private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
public static final int DEFAULT_SYNC_STACK_DURATION = 200;
private static final int DRAG_SCALE_DURATION = 175;
- private static final float DRAG_SCALE_FACTOR = 1.05f;
+ static final float DRAG_SCALE_FACTOR = 1.05f;
- private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 200;
+ private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
+ // The actions to perform when resetting to initial state,
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
+ public @interface InitialStateAction {}
+ /** Do not update the stack and layout to the initial state. */
+ private static final int INITIAL_STATE_UPDATE_NONE = 0;
+ /** Update both the stack and layout to the initial state. */
+ private static final int INITIAL_STATE_UPDATE_ALL = 1;
+ /** Update only the layout to the initial state. */
+ private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
+
LayoutInflater mInflater;
TaskStack mStack = new TaskStack();
@ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
TaskStackLayoutAlgorithm mLayoutAlgorithm;
+ // The stable layout algorithm is only used to calculate the task rect with the stable bounds
+ TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
@ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
TaskStackViewScroller mStackScroller;
@ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
@@ -151,6 +164,9 @@
@ViewDebug.ExportedProperty(category="recents")
boolean mAwaitingFirstLayout = true;
@ViewDebug.ExportedProperty(category="recents")
+ @InitialStateAction
+ int mInitialState = INITIAL_STATE_UPDATE_ALL;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInMeasureLayout = false;
@ViewDebug.ExportedProperty(category="recents")
boolean mEnterAnimationComplete = false;
@@ -165,11 +181,18 @@
// The current stack bounds are dynamic and may change as the user drags and drops
@ViewDebug.ExportedProperty(category="recents")
private Rect mStackBounds = new Rect();
+ // The current window bounds at the point we were measured
+ @ViewDebug.ExportedProperty(category="recents")
+ private Rect mStableWindowRect = new Rect();
+ // The current window bounds are dynamic and may change as the user drags and drops
+ @ViewDebug.ExportedProperty(category="recents")
+ private Rect mWindowRect = new Rect();
private Rect mTmpRect = new Rect();
private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
private List<TaskView> mTmpTaskViews = new ArrayList<>();
private TaskViewTransform mTmpTransform = new TaskViewTransform();
+ private ArrayList<TaskViewTransform> mTmpTaskTransforms = new ArrayList<>();
private int[] mTmpIntPair = new int[2];
// A convenience update listener to request updating clipping of tasks
@@ -219,6 +242,7 @@
mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
+ mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
mAnimationHelper = new TaskStackAnimationHelper(context, this);
@@ -251,41 +275,6 @@
}
}
- /**
- * Called only if we are resuming Recents.
- */
- void onResume(boolean isResumingFromVisible) {
- if (!isResumingFromVisible) {
- // Reset the focused task
- resetFocusedTask(getFocusedTask());
- }
-
- // Reset the state of each of the task views
- List<TaskView> taskViews = new ArrayList<>();
- taskViews.addAll(getTaskViews());
- taskViews.addAll(mViewPool.getViews());
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- taskViews.get(i).onResume(isResumingFromVisible);
- }
-
- // Reset the stack state
- readSystemFlags();
- mTaskViewsClipDirty = true;
- mEnterAnimationComplete = false;
- mUIDozeTrigger.stopDozing();
- if (isResumingFromVisible) {
- // Animate in the freeform workspace
- int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
- animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
- Interpolators.FAST_OUT_SLOW_IN));
- } else {
- mStackScroller.reset();
- mLayoutAlgorithm.reset();
- mAwaitingFirstLayout = true;
- requestLayout();
- }
- }
-
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -300,31 +289,54 @@
}
/**
+ * Called from RecentsActivity when it is relaunched.
+ */
+ void onReload(boolean isResumingFromVisible) {
+ if (!isResumingFromVisible) {
+ // Reset the focused task
+ resetFocusedTask(getFocusedTask());
+ }
+
+ // Reset the state of each of the task views
+ List<TaskView> taskViews = new ArrayList<>();
+ taskViews.addAll(getTaskViews());
+ taskViews.addAll(mViewPool.getViews());
+ for (int i = taskViews.size() - 1; i >= 0; i--) {
+ taskViews.get(i).onReload(isResumingFromVisible);
+ }
+
+ // Reset the stack state
+ readSystemFlags();
+ mTaskViewsClipDirty = true;
+ mEnterAnimationComplete = false;
+ mUIDozeTrigger.stopDozing();
+ if (isResumingFromVisible) {
+ // Animate in the freeform workspace
+ int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
+ animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
+ Interpolators.FAST_OUT_SLOW_IN));
+ } else {
+ mStackScroller.reset();
+ mStableLayoutAlgorithm.reset();
+ mLayoutAlgorithm.reset();
+ }
+
+ // Since we always animate to the same place in (the initial state), always reset the stack
+ // to the initial state when resuming
+ mAwaitingFirstLayout = true;
+ mInitialState = INITIAL_STATE_UPDATE_ALL;
+ requestLayout();
+ }
+
+ /**
* Sets the stack tasks of this TaskStackView from the given TaskStack.
*/
- public void setTasks(TaskStack stack, boolean notifyStackChanges, boolean relayoutTaskStack) {
+ public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
boolean isInitialized = mLayoutAlgorithm.isInitialized();
+ // Only notify if we are already initialized, otherwise, everything will pick up all the
+ // new and old tasks when we next layout
mStack.setTasks(getContext(), stack.computeAllTasksList(),
- notifyStackChanges && isInitialized);
- if (isInitialized) {
- // Only update the layout if we are notifying, otherwise, we will update it in the next
- // measure/layout pass
- updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
- updateToInitialState();
-
- if (relayoutTaskStack) {
- relayoutTaskViews(AnimationProps.IMMEDIATE);
-
- // Rebind all the task views. This will not trigger new resources to be loaded
- // unless they have actually changed
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- bindTaskView(tv, tv.getTask());
- }
- }
- }
+ allowNotifyStackChanges && isInitialized);
}
/** Returns the task stack. */
@@ -335,8 +347,10 @@
/**
* Updates this TaskStackView to the initial state.
*/
- public void updateToInitialState() {
- mStackScroller.setStackScrollToInitialState();
+ public void updateToInitialState(boolean scrollToInitialState) {
+ if (scrollToInitialState) {
+ mStackScroller.setStackScrollToInitialState();
+ }
mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
}
@@ -622,15 +636,17 @@
}
/**
- * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
- * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
- * animations that are current running on those task views, and will ensure that the children
- * {@link TaskView}s will match the set of visible tasks in the stack.
- *
- * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>)
+ * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
*/
void relayoutTaskViews(AnimationProps animation) {
- relayoutTaskViews(animation, mIgnoreTasks);
+ relayoutTaskViews(animation, mIgnoreTasks, false /* ignoreTaskOverrides */);
+ }
+
+ /**
+ * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
+ */
+ void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet) {
+ relayoutTaskViews(animation, ignoreTasksSet, false /* ignoreTaskOverrides */);
}
/**
@@ -641,13 +657,14 @@
*
* @param ignoreTasksSet the set of tasks to ignore in the relayout
*/
- void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet) {
+ void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet,
+ boolean ignoreTaskOverrides) {
// If we had a deferred animation, cancel that
mDeferredTaskViewLayoutAnimation = null;
// Synchronize the current set of TaskViews
bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet,
- false /* ignoreTaskOverrides */);
+ ignoreTaskOverrides /* ignoreTaskOverrides */);
// Animate them to their final transforms with the given animation
List<TaskView> taskViews = getTaskViews();
@@ -761,8 +778,6 @@
* Updates the clip for each of the task views from back to front.
*/
private void clipTaskViews() {
- RecentsConfiguration config = Recents.getConfiguration();
-
// Update the clip on each task child
List<TaskView> taskViews = getTaskViews();
TaskView tmpTv = null;
@@ -1059,8 +1074,10 @@
event.setContentDescription(frontMostTask.getTask().title);
}
event.setItemCount(mStack.getTaskCount());
- event.setScrollY(mStackScroller.mScroller.getCurrY());
- event.setMaxScrollY(mStackScroller.progressToScrollRange(mLayoutAlgorithm.mMaxScrollP));
+
+ int stackHeight = mLayoutAlgorithm.mStackRect.height();
+ event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
+ event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
}
@Override
@@ -1161,23 +1178,12 @@
}
/**
- * Updates the expected task stack bounds for this stack view.
+ * Updates the system insets.
*/
- public void setTaskStackBounds(Rect taskStackBounds, Rect systemInsets) {
- // We can get spurious measure passes with the old bounds when docking, and since we are
- // using the current stack bounds during drag and drop, don't overwrite them until we
- // actually get new bounds
- boolean requiresLayout = false;
- if (!taskStackBounds.equals(mStableStackBounds)) {
- mStableStackBounds.set(taskStackBounds);
- mStackBounds.set(taskStackBounds);
- requiresLayout = true;
- }
+ public void setSystemInsets(Rect systemInsets) {
if (!systemInsets.equals(mLayoutAlgorithm.mSystemInsets)) {
+ mStableLayoutAlgorithm.setSystemInsets(systemInsets);
mLayoutAlgorithm.setSystemInsets(systemInsets);
- requiresLayout = true;
- }
- if (requiresLayout) {
requestLayout();
}
}
@@ -1192,18 +1198,30 @@
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
+ // Update the stable stack bounds, but only update the current stack bounds if the stable
+ // bounds have changed. This is because we may get spurious measures while dragging where
+ // our current stack bounds reflect the target drop region.
+ mLayoutAlgorithm.getTaskStackBounds(new Rect(0, 0, width, height),
+ mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
+ if (!mTmpRect.equals(mStableStackBounds)) {
+ mStableStackBounds.set(mTmpRect);
+ mStackBounds.set(mTmpRect);
+ mStableWindowRect.set(0, 0, width, height);
+ mWindowRect.set(0, 0, width, height);
+ }
+
// Compute the rects in the stack algorithm
- mLayoutAlgorithm.initialize(mStackBounds,
+ mStableLayoutAlgorithm.initialize(mStableWindowRect, mStableStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
+ mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
// If this is the first layout, then scroll to the front of the stack, then update the
// TaskViews with the stack so that we can lay them out
- // TODO: The second check is a workaround for wacky layouts that we get while docking via
- // long pressing the recents button
- if (mAwaitingFirstLayout ||
- (mStackScroller.getStackScroll() == mLayoutAlgorithm.mInitialScrollP)) {
- updateToInitialState();
+ if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE) {
+ updateToInitialState(mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY);
+ mInitialState = INITIAL_STATE_UPDATE_NONE;
}
// Rebind all the views, including the ignore ones
@@ -1232,20 +1250,14 @@
} else {
mTmpRect.setEmpty();
}
+ Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
tv.measure(
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
+ MeasureSpec.makeMeasureSpec(taskRect.width() + mTmpRect.left + mTmpRect.right,
MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom,
+ MeasureSpec.makeMeasureSpec(taskRect.height() + mTmpRect.top + mTmpRect.bottom,
MeasureSpec.EXACTLY));
}
- /**
- * This is called with the size of the space not including the top or right insets, or the
- * search bar height in portrait (but including the search bar width in landscape, since we want
- * to draw under it.
- */
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Layout each of the TaskViews
@@ -1281,7 +1293,8 @@
} else {
mTmpRect.setEmpty();
}
- Rect taskRect = mLayoutAlgorithm.mTaskRect;
+ Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
+ tv.cancelTransformAnimation();
tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
}
@@ -1306,12 +1319,11 @@
false /* requestViewFocus */);
}
- // Update the history button visibility
- if (shouldShowHistoryButton() &&
- mStackScroller.getStackScroll() < SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
- EventBus.getDefault().send(new ShowHistoryButtonEvent(false /* translate */));
+ // Update the stack action button visibility
+ if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
+ EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
} else {
- EventBus.getDefault().send(new HideHistoryButtonEvent());
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
}
@@ -1433,9 +1445,19 @@
}
@Override
- public void onHistoryTaskRemoved(TaskStack stack, Task removedTask,
- AnimationProps animation) {
- // To be implemented
+ public void onStackTasksUpdated(TaskStack stack) {
+ // Update the layout and immediately layout
+ updateLayoutAlgorithm(false /* boundScroll */);
+ relayoutTaskViews(AnimationProps.IMMEDIATE);
+
+ // Rebind all the task views. This will not trigger new resources to be loaded
+ // unless they have actually changed
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ bindTaskView(tv, tv.getTask());
+ }
}
/**** ViewPoolConsumer Implementation ****/
@@ -1572,13 +1594,12 @@
mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
if (mEnterAnimationComplete) {
- if (shouldShowHistoryButton() &&
- prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
- curScroll <= SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
- EventBus.getDefault().send(new ShowHistoryButtonEvent(true /* translate */));
- } else if (prevScroll < HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD &&
- curScroll >= HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD) {
- EventBus.getDefault().send(new HideHistoryButtonEvent());
+ if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+ curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
+ EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
+ } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+ curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
}
}
@@ -1625,15 +1646,13 @@
cancelAllTaskViewAnimations();
final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
- if (getChildViewForTask(launchTask) == null) {
- List<TaskView> taskViews = getTaskViews();
- int lastTaskIndex = !taskViews.isEmpty()
- ? mStack.indexOfStackTask(taskViews.get(taskViews.size() - 1).getTask())
- : mStack.getTaskCount() - 1;
- int duration = LAUNCH_NEXT_SCROLL_BASE_DURATION +
- Math.abs(mStack.indexOfStackTask(launchTask) - lastTaskIndex)
- * LAUNCH_NEXT_SCROLL_INCR_DURATION;
- mStackScroller.animateScroll(mLayoutAlgorithm.getStackScrollForTask(launchTask),
+ float curScroll = mStackScroller.getStackScroll();
+ float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask);
+ float absScrollDiff = Math.abs(targetScroll - curScroll);
+ if (getChildViewForTask(launchTask) == null || absScrollDiff > 0.35f) {
+ int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
+ absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
+ mStackScroller.animateScroll(targetScroll,
duration, new Runnable() {
@Override
public void run() {
@@ -1757,32 +1776,38 @@
public final void onBusEvent(DragDropTargetChangedEvent event) {
AnimationProps animation = new AnimationProps(250, Interpolators.FAST_OUT_SLOW_IN);
+ boolean ignoreTaskOverrides = false;
if (event.dropTarget instanceof TaskStack.DockState) {
// Calculate the new task stack bounds that matches the window size that Recents will
// have after the drop
final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
mStackBounds.set(dockState.getDockedTaskStackBounds(getMeasuredWidth(),
getMeasuredHeight(), mDividerSize, mLayoutAlgorithm.mSystemInsets,
- getResources()));
- mLayoutAlgorithm.initialize(mStackBounds,
+ mLayoutAlgorithm, getResources(), mWindowRect));
+ mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
updateLayoutAlgorithm(true /* boundScroll */);
+ ignoreTaskOverrides = true;
} else {
// Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
// task view, so add it back to the ignore set after updating the layout
+ mWindowRect.set(mStableWindowRect);
mStackBounds.set(mStableStackBounds);
removeIgnoreTask(event.task);
- mLayoutAlgorithm.initialize(mStackBounds,
+ mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
updateLayoutAlgorithm(true /* boundScroll */);
addIgnoreTask(event.task);
}
- relayoutTaskViews(animation);
+ relayoutTaskViews(animation, mIgnoreTasks, ignoreTaskOverrides);
}
public final void onBusEvent(final DragEndEvent event) {
// We don't handle drops on the dock regions
if (event.dropTarget instanceof TaskStack.DockState) {
+ // However, we do need to reset the overrides, since the last state of this task stack
+ // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
+ mLayoutAlgorithm.clearUnfocusedTaskOverrides();
return;
}
@@ -1888,22 +1913,6 @@
}
}
- public final void onBusEvent(ShowHistoryEvent event) {
- ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
- postAnimTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- setVisibility(View.INVISIBLE);
- }
- });
- mAnimationHelper.startShowHistoryAnimation(postAnimTrigger);
- }
-
- public final void onBusEvent(HideHistoryEvent event) {
- setVisibility(View.VISIBLE);
- mAnimationHelper.startHideHistoryAnimation();
- }
-
public final void onBusEvent(MultiWindowStateChangedEvent event) {
if (!event.inMultiWindow) {
// Scroll the stack to the front to see the undocked task
@@ -1923,9 +1932,25 @@
}
public final void onBusEvent(ConfigurationChangedEvent event) {
+ mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
- mLayoutAlgorithm.initialize(mStackBounds,
- TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
+
+ // Notify the task views of the configuration change so they can reload their resources
+ if (!event.fromMultiWindow) {
+ mTmpTaskViews.clear();
+ mTmpTaskViews.addAll(getTaskViews());
+ mTmpTaskViews.addAll(mViewPool.getViews());
+ int taskViewCount = mTmpTaskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ mTmpTaskViews.get(i).onConfigurationChanged();
+ }
+ }
+
+ // Trigger a new layout and scroll to the initial state
+ mInitialState = event.fromMultiWindow
+ ? INITIAL_STATE_UPDATE_ALL
+ : INITIAL_STATE_UPDATE_LAYOUT_ONLY;
+ requestLayout();
}
/**
@@ -1993,13 +2018,6 @@
}
/**
- * @return whether the history button should be visible
- */
- private boolean shouldShowHistoryButton() {
- return !mStack.getHistoricalTasks().isEmpty();
- }
-
- /**
* Reads current system flags related to accessibility and screen pinning.
*/
private void readSystemFlags() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 9be3542..583fb88 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -28,8 +28,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
/* The scrolling logic for a TaskStackView */
@@ -189,7 +187,7 @@
// Finish any current scrolling animations
if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
setStackScroll(mFinalAnimatedScroll);
- mScroller.startScroll(0, progressToScrollRange(mFinalAnimatedScroll), 0, 0, 0);
+ mScroller.forceFinished(true);
}
stopScroller();
stopBoundScrollAnimation();
@@ -223,12 +221,6 @@
/**** OverScroller ****/
- // TODO: Remove
- @Deprecated
- int progressToScrollRange(float p) {
- return (int) (p * mLayoutAlgorithm.mStackRect.height());
- }
-
/** Called from the view draw, computes the next scroll. */
boolean computeScroll() {
if (mScroller.computeScrollOffset()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 8635911..aed19c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -121,7 +121,7 @@
mScrollTouchSlop = configuration.getScaledTouchSlop();
mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
mFlingAnimUtils = new FlingAnimationUtils(context, 0.2f);
- mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_stack_overscroll);
+ mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_fling_overscroll_distance);
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) {
@Override
protected float getSize(View v) {
@@ -458,12 +458,13 @@
newStackScroll = stackScroller.getBoundedStackScroll(newStackScroll);
} else if (pullStackForward) {
// Otherwise, offset the scroll by the movement of the anchor task
- float anchorTaskScroll = layoutAlgorithm.getStackScrollForTask(anchorTask);
+ float anchorTaskScroll =
+ layoutAlgorithm.getStackScrollForTaskIgnoreOverrides(anchorTask);
float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
if (layoutAlgorithm.getFocusState() != TaskStackLayoutAlgorithm.STATE_FOCUSED) {
// If we are focused, we don't want the front task to move, but otherwise, we
// allow the back task to move up, and the front task to move back
- stackScrollOffset /= 2;
+ stackScrollOffset *= 0.75f;
}
newStackScroll = stackScroller.getBoundedStackScroll(stackScroller.getStackScroll()
+ stackScrollOffset);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 7584a2e..c085d80 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -16,13 +16,16 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.app.ActivityManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Point;
@@ -59,8 +62,6 @@
import java.util.ArrayList;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
/**
* A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
* solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
@@ -129,8 +130,6 @@
@ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
- PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
- Paint mDimLayerPaint = new Paint();
float mActionButtonTranslationZ;
@ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
@@ -193,15 +192,15 @@
mCb = cb;
}
- /** Resets this TaskView for reuse. */
- void onResume(boolean isResumingFromVisible) {
+ /**
+ * Called from RecentsActivity when it is relaunched.
+ */
+ void onReload(boolean isResumingFromVisible) {
resetNoUserInteractionState();
readSystemFlags();
if (!isResumingFromVisible) {
resetViewProperties();
- setClipViewInStack(false);
}
- setCallbacks(null);
}
/** Gets the task */
@@ -239,6 +238,13 @@
mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
}
+ /**
+ * Update the task view when the configuration changes.
+ */
+ void onConfigurationChanged() {
+ mHeaderView.onConfigurationChanged();
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
@@ -559,6 +565,13 @@
postAnimationTrigger.decrementOnAnimationEnd());
}
+ @Override
+ public void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled) {
+ if (screenPinningEnabled) {
+ showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
+ }
+ }
+
/**** TaskCallbacks Implementation ****/
public void onTaskBound(Task t) {
@@ -569,9 +582,9 @@
}
@Override
- public void onTaskDataLoaded(Task task) {
+ public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
// Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode);
+ mThumbnailView.rebindToTask(mTask, thumbnailInfo, mIsDisabledInSafeMode);
mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
mTaskDataLoaded = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index b2a7d90..ddea4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -34,9 +34,11 @@
import android.os.CountDownTimer;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewDebug;
+import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -153,6 +155,8 @@
// Header drawables
@ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
+ int mHeaderBarHeight;
+ int mHeaderButtonPadding;
int mCornerRadius;
int mHighlightHeight;
@ViewDebug.ExportedProperty(category="recents")
@@ -245,6 +249,67 @@
}
mFocusTimerIndicatorStub = (ViewStub) findViewById(R.id.focus_timer_indicator_stub);
mAppOverlayViewStub = (ViewStub) findViewById(R.id.app_overlay_stub);
+
+ onConfigurationChanged();
+ }
+
+ /**
+ * Programmatically sets the layout params for a header bar layout. This is necessary because
+ * we can't get resources based on the current configuration, but instead need to get them
+ * based on the device configuration.
+ */
+ private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
+ setLayoutParams(lp);
+ lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
+ icon.setLayoutParams(lp);
+ lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
+ lp.setMarginStart(mHeaderBarHeight);
+ lp.rightMargin = mMoveTaskButton != null
+ ? 2 * mHeaderBarHeight
+ : mHeaderBarHeight;
+ title.setLayoutParams(lp);
+ if (secondaryButton != null) {
+ lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+ lp.setMarginEnd(mHeaderBarHeight);
+ secondaryButton.setLayoutParams(lp);
+ secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
+ mHeaderButtonPadding, mHeaderButtonPadding);
+ }
+ lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+ button.setLayoutParams(lp);
+ button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
+ mHeaderButtonPadding);
+ }
+
+ /**
+ * Update the header view when the configuration changes.
+ */
+ void onConfigurationChanged() {
+ // Update the dimensions of everything in the header. We do this because we need to use
+ // resources for the display, and not the current configuration.
+ Resources res = getResources();
+ mHeaderBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land);
+ mHeaderButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding_tablet_land,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding_tablet_land);
+ updateLayoutParams(mIconView, findViewById(R.id.title_container), mMoveTaskButton,
+ mDismissButton);
+ if (mAppOverlayView != null) {
+ updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
+ }
}
@Override
@@ -337,6 +402,11 @@
}
}
+ /** Only exposed for the workaround for b/27815919. */
+ public ImageView getIconView() {
+ return mIconView;
+ }
+
/** Returns the secondary color for a primary color. */
int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
@@ -350,6 +420,7 @@
public void setDimAlpha(float dimAlpha) {
if (Float.compare(mDimAlpha, dimAlpha) != 0) {
mDimAlpha = dimAlpha;
+ mTitleView.setAlpha(1f - dimAlpha);
updateBackgroundColor(mBackground.getColor(), dimAlpha);
}
}
@@ -573,6 +644,7 @@
mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
mAppInfoView.setOnClickListener(this);
mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
+ updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
}
// Update the overlay contents for the current app
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index e46708e..3eeabc7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -16,7 +16,9 @@
package com.android.systemui.recents.views;
+import android.app.ActivityManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
@@ -34,6 +36,8 @@
import android.view.ViewDebug;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
@@ -43,17 +47,24 @@
*/
public class TaskViewThumbnail extends View {
-
private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
private Task mTask;
+ private Rect mDisplayRect = new Rect();
+ private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+
// Drawing
@ViewDebug.ExportedProperty(category="recents")
+ Rect mTaskViewRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mThumbnailRect = new Rect();
@ViewDebug.ExportedProperty(category="recents")
- Rect mTaskViewRect = new Rect();
+ float mThumbnailScale;
+ float mFullscreenThumbnailScale;
+ ActivityManager.TaskThumbnailInfo mThumbnailInfo;
+
int mCornerRadius;
@ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
@@ -97,6 +108,8 @@
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
mBgFillPaint.setColor(Color.WHITE);
+ mFullscreenThumbnailScale = context.getResources().getFraction(
+ com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
}
/**
@@ -114,57 +127,75 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ mOrientation = ssp.getDisplayOrientation();
+ mDisplayRect = ssp.getDisplayRect();
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
if (mInvisible) {
return;
}
- int thumbnailHeight = (int) (((float) mTaskViewRect.width() / mThumbnailRect.width()) *
- mThumbnailRect.height());
- if (thumbnailHeight >= mTaskViewRect.height()) {
- // The thumbnail fills the full task view bounds, so just draw it
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
- mCornerRadius, mCornerRadius, mDrawPaint);
- } else {
- int count = 0;
- if (thumbnailHeight > 0) {
- // The thumbnail only covers part of the task view bounds, so fill in the
- // non-thumbnail space with the default background color. This is the equivalent of
- // the GL border texture mode.
- count = canvas.save();
+ if (mBitmapShader != null) {
+ int viewWidth = mTaskViewRect.width();
+ int viewHeight = mTaskViewRect.height();
+
+ // We are drawing the thumbnail in the same orientation, so just fit the width
+ int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale);
+ int thumbnailHeight = (int) (mThumbnailRect.height() * mThumbnailScale);
+
+ if (thumbnailWidth >= viewWidth && thumbnailHeight >= viewHeight) {
+ // Thumbnail fills the full task view bounds, so just draw it
+ canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+ mDrawPaint);
+ } else {
+ // Thumbnail does not fill the full task view bounds, so just draw it and fill the
+ // empty areas with the background color
+ int count = canvas.save();
// Since we only want the top corners to be rounded, draw slightly beyond the
// thumbnail height, but clip to the thumbnail height
- canvas.clipRect(0, 0, mTaskViewRect.width(), thumbnailHeight, Region.Op.REPLACE);
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), thumbnailHeight + mCornerRadius,
+ canvas.clipRect(0, 0, thumbnailWidth, thumbnailHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(0, 0,
+ thumbnailWidth + (thumbnailWidth < viewWidth ? mCornerRadius : 0),
+ thumbnailHeight + (thumbnailHeight < viewHeight ? mCornerRadius : 0),
mCornerRadius, mCornerRadius, mDrawPaint);
- }
- // In the remaining space, draw the background color
- canvas.clipRect(0, thumbnailHeight, mTaskViewRect.width(), mTaskViewRect.height(),
- Region.Op.REPLACE);
- canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
- mTaskViewRect.width(), mTaskViewRect.height(), mCornerRadius, mCornerRadius,
- mBgFillPaint);
+ // In the remaining space, draw the background color
+ if (thumbnailWidth < viewWidth) {
+ canvas.clipRect(thumbnailWidth, 0, viewWidth, viewHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
+ viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
+ }
+ if (thumbnailWidth > 0 && thumbnailHeight < viewHeight) {
+ canvas.clipRect(0, thumbnailHeight, viewWidth, viewHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+ viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
+ }
- if (thumbnailHeight > 0) {
canvas.restoreToCount(count);
}
}
}
/** Sets the thumbnail to a given bitmap. */
- void setThumbnail(Bitmap bm) {
+ void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
if (bm != null) {
- mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP);
+ mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mDrawPaint.setShader(mBitmapShader);
mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
+ mThumbnailInfo = thumbnailInfo;
updateThumbnailScale();
} else {
mBitmapShader = null;
mDrawPaint.setShader(null);
mThumbnailRect.setEmpty();
+ mThumbnailInfo = null;
}
}
@@ -210,20 +241,41 @@
* Updates the scale of the bitmap relative to this view.
*/
public void updateThumbnailScale() {
+ mThumbnailScale = 1f;
if (mBitmapShader != null) {
- float thumbnailScale;
- if (!mTask.isFreeformTask() || mTask.bounds == null) {
- // If this is a stack task, or a stack task moved into the freeform workspace, then
- // just scale this thumbnail to fit the width of the view
- thumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+ // We consider this a stack task if it is not freeform (ie. has no bounds) or has been
+ // dragged into the stack from the freeform workspace
+ boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null;
+ if (mTaskViewRect.isEmpty() || mThumbnailInfo == null ||
+ mThumbnailInfo.taskWidth == 0 || mThumbnailInfo.taskHeight == 0) {
+ // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
+ // and only draw the background color
+ mThumbnailScale = 0f;
+ } else if (isStackTask) {
+ float invThumbnailScale = 1f / mFullscreenThumbnailScale;
+ if (mOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ // If we are in the same orientation as the screenshot, just scale it to the
+ // width of the task view
+ mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+ } else {
+ // Scale the landscape thumbnail up to app size, then scale that to the task
+ // view size to match other portrait screenshots
+ mThumbnailScale = invThumbnailScale *
+ ((float) mTaskViewRect.width() / mDisplayRect.width());
+ }
+ } else {
+ // Otherwise, scale the screenshot to fit 1:1 in the current orientation
+ mThumbnailScale = invThumbnailScale;
+ }
} else {
// Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
// to fit the entire bitmap into the task bounds
- thumbnailScale = Math.min(
+ mThumbnailScale = Math.min(
(float) mTaskViewRect.width() / mThumbnailRect.width(),
(float) mTaskViewRect.height() / mThumbnailRect.height());
}
- mScaleMatrix.setScale(thumbnailScale, thumbnailScale);
+ mScaleMatrix.setScale(mThumbnailScale, mThumbnailScale);
mBitmapShader.setLocalMatrix(mScaleMatrix);
}
if (!mInvisible) {
@@ -261,22 +313,23 @@
}
/** Binds the thumbnail view to the task */
- void rebindToTask(Task t, boolean disabledInSafeMode) {
+ void rebindToTask(Task t, ActivityManager.TaskThumbnailInfo thumbnailInfo,
+ boolean disabledInSafeMode) {
mTask = t;
mDisabledInSafeMode = disabledInSafeMode;
if (t.thumbnail != null) {
- setThumbnail(t.thumbnail);
+ setThumbnail(t.thumbnail, thumbnailInfo);
if (t.colorBackground != 0) {
mBgFillPaint.setColor(t.colorBackground);
}
} else {
- setThumbnail(null);
+ setThumbnail(null, null);
}
}
/** Unbinds the thumbnail view from the task */
void unbindFromTask() {
mTask = null;
- setThumbnail(null);
+ setThumbnail(null, null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index dd59fac..e8cf126 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -41,6 +41,7 @@
private DockDividerVisibilityListener mDockDividerVisibilityListener;
private boolean mVisible = false;
private boolean mMinimized = false;
+ private ForcedResizableInfoActivityController mForcedResizableController;
@Override
public void start() {
@@ -52,6 +53,7 @@
mDockDividerVisibilityListener = new DockDividerVisibilityListener();
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.registerDockedStackListener(mDockDividerVisibilityListener);
+ mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
}
@Override
@@ -117,6 +119,15 @@
});
}
+ private void notifyDockedStackExistsChanged(final boolean exists) {
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ mForcedResizableController.notifyDockedStackExistsChanged(exists);
+ }
+ });
+ }
+
class DockDividerVisibilityListener extends IDockedStackListener.Stub {
@Override
@@ -126,6 +137,7 @@
@Override
public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
+ notifyDockedStackExistsChanged(exists);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 132c09f..7a933cd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -66,6 +66,8 @@
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.stackdivider.events.StartedDragingEvent;
+import com.android.systemui.stackdivider.events.StoppedDragingEvent;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
@@ -81,6 +83,7 @@
private static final String TAG = "DividerView";
private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
+ private static final boolean SWAPPING_ENABLED = false;
/**
* How much the background gets scaled when we are in the minimized dock state.
@@ -168,6 +171,13 @@
}
};
+ private final Runnable mResetBackgroundRunnable = new Runnable() {
+ @Override
+ public void run() {
+ resetBackground();
+ }
+ };
+
public DividerView(Context context) {
super(context);
}
@@ -211,12 +221,14 @@
mGestureDetector = new GestureDetector(mContext, new SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
- updateDockSide();
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (mDockSide != WindowManager.DOCKED_INVALID
- && !ssp.isRecentsTopMost(ssp.getTopMostTask(), null /* isTopHome */)) {
- mWindowManagerProxy.swapTasks();
- return true;
+ if (SWAPPING_ENABLED) {
+ updateDockSide();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (mDockSide != WindowManager.DOCKED_INVALID
+ && !ssp.isRecentsTopMost(ssp.getTopMostTask(), null /* isTopHome */)) {
+ mWindowManagerProxy.swapTasks();
+ return true;
+ }
}
return false;
}
@@ -281,6 +293,7 @@
mWindowManager.setSlippery(false);
liftBackground();
}
+ EventBus.getDefault().send(new StartedDragingEvent());
return mDockSide != WindowManager.DOCKED_INVALID;
}
@@ -380,13 +393,6 @@
x = (int) event.getRawX();
y = (int) event.getRawY();
- if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
- int position = calculatePosition(x, y);
- SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position,
- 0 /* velocity */, false /* hardDismiss */);
- resizeStack(calculatePosition(x, y), snapTarget.position, snapTarget);
- }
-
mVelocityTracker.computeCurrentVelocity(1000);
int position = calculatePosition(x, y);
stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
@@ -439,6 +445,7 @@
mDockSide = WindowManager.DOCKED_INVALID;
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
+ EventBus.getDefault().send(new StoppedDragingEvent());
}
});
mCurrentAnimator = anim;
@@ -519,15 +526,17 @@
public void setMinimizedDockStack(boolean minimized) {
updateDockSide();
mHandle.setAlpha(minimized ? 0f : 1f);
- if (mDockSide == WindowManager.DOCKED_TOP) {
+ if (!minimized) {
+ resetBackground();
+ } else if (mDockSide == WindowManager.DOCKED_TOP) {
mBackground.setPivotY(0);
- mBackground.setScaleY(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ mBackground.setScaleY(MINIMIZE_DOCK_SCALE);
} else if (mDockSide == WindowManager.DOCKED_LEFT
|| mDockSide == WindowManager.DOCKED_RIGHT) {
mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
? 0
: mBackground.getWidth());
- mBackground.setScaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ mBackground.setScaleX(MINIMIZE_DOCK_SCALE);
}
}
@@ -550,12 +559,22 @@
mBackground.animate()
.scaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
}
+ if (!minimized) {
+ mBackground.animate().withEndAction(mResetBackgroundRunnable);
+ }
mBackground.animate()
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(animDuration)
.start();
}
+ private void resetBackground() {
+ mBackground.setPivotX(mBackground.getWidth() / 2);
+ mBackground.setPivotY(mBackground.getHeight() / 2);
+ mBackground.setScaleX(1f);
+ mBackground.setScaleY(1f);
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -730,21 +749,22 @@
mSnapAlgorithm.calculateDismissingFraction(position)));
SnapTarget dismissTarget = null;
SnapTarget splitTarget = null;
- if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_START
- || snapTarget == mSnapAlgorithm.getFirstSplitTarget())
+ int start = 0;
+ if (position <= mSnapAlgorithm.getLastSplitTarget().position
&& dockSideTopLeft(dockSide)) {
dismissTarget = mSnapAlgorithm.getDismissStartTarget();
splitTarget = mSnapAlgorithm.getFirstSplitTarget();
- } else if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_END
- || snapTarget == mSnapAlgorithm.getLastSplitTarget())
+ start = taskPosition;
+ } else if (position >= mSnapAlgorithm.getLastSplitTarget().position
&& dockSideBottomRight(dockSide)) {
dismissTarget = mSnapAlgorithm.getDismissEndTarget();
splitTarget = mSnapAlgorithm.getLastSplitTarget();
+ start = splitTarget.position;
}
if (dismissTarget != null && fraction > 0f
&& isDismissing(splitTarget, position, dockSide)) {
fraction = calculateParallaxDismissingFraction(fraction, dockSide);
- int offsetPosition = (int) (taskPosition +
+ int offsetPosition = (int) (start +
fraction * (dismissTarget.position - splitTarget.position));
int width = taskRect.width();
int height = taskRect.height();
@@ -792,11 +812,12 @@
}
private int getStackIdForDismissTarget(SnapTarget dismissTarget) {
- if (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START &&
- (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP)) {
+ if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
+ || (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
+ && dockSideBottomRight(mDockSide))) {
return StackId.DOCKED_STACK_ID;
} else {
- return StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+ return StackId.HOME_STACK_ID;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
new file mode 100644
index 0000000..f728dab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
@@ -0,0 +1,74 @@
+/*
+ * 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.stackdivider;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.systemui.R;
+
+/**
+ * Translucent activity that gets started on top of a task in multi-window to inform the user that
+ * we forced the activity below to be resizable.
+ */
+public class ForcedResizableInfoActivity extends Activity implements OnTouchListener {
+
+ private static final long DISMISS_DELAY = 2500;
+
+ private final Runnable mFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ };
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.forced_resizable_activity);
+ getWindow().getDecorView().setOnTouchListener(this);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ getWindow().getDecorView().postDelayed(mFinishRunnable, DISMISS_DELAY);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ finish();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ finish();
+ return true;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ finish();
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
new file mode 100644
index 0000000..9b56037
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -0,0 +1,118 @@
+/*
+ * 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.stackdivider;
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
+import com.android.systemui.recents.events.activity.ForcedResizableEvent;
+import com.android.systemui.stackdivider.events.StartedDragingEvent;
+import com.android.systemui.stackdivider.events.StoppedDragingEvent;
+
+/**
+ * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
+ */
+public class ForcedResizableInfoActivityController {
+
+ private static final String SELF_PACKAGE_NAME = "com.android.systemui";
+
+ private static final int TIMEOUT = 1000;
+ private final Context mContext;
+ private final Handler mHandler = new Handler();
+ private final ArraySet<Integer> mPendingTaskIds = new ArraySet<>();
+ private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
+ private boolean mDividerDraging;
+
+ private final Runnable mTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ showPending();
+ }
+ };
+
+ public ForcedResizableInfoActivityController(Context context) {
+ mContext = context;
+ EventBus.getDefault().register(this);
+ }
+
+ public void notifyDockedStackExistsChanged(boolean exists) {
+ if (!exists) {
+ mPackagesShownInSession.clear();
+ }
+ }
+
+ public final void onBusEvent(ForcedResizableEvent forcedResizableEvent) {
+ if (debounce(forcedResizableEvent.packageName)) {
+ return;
+ }
+ mPendingTaskIds.add(forcedResizableEvent.taskId);
+ postTimeout();
+ }
+
+ public final void onBusEvent(AppTransitionFinishedEvent event) {
+ if (!mDividerDraging) {
+ showPending();
+ }
+ }
+
+ public final void onBusEvent(StartedDragingEvent event) {
+ mDividerDraging = true;
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ }
+
+ public final void onBusEvent(StoppedDragingEvent event) {
+ mDividerDraging = false;
+ showPending();
+ }
+
+ private void showPending() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) {
+ Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(mPendingTaskIds.valueAt(i));
+ mContext.startActivity(intent, options.toBundle());
+ }
+ mPendingTaskIds.clear();
+ }
+
+ private void postTimeout() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ mHandler.postDelayed(mTimeoutRunnable, TIMEOUT);
+ }
+
+ private boolean debounce(String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+
+ // We launch ForcedResizableInfoActivity into a task that was forced resizable, so that
+ // triggers another notification. So ignore our own activity.
+ if (SELF_PACKAGE_NAME.equals(packageName)) {
+ return true;
+ }
+ boolean debounce = mPackagesShownInSession.contains(packageName);
+ mPackagesShownInSession.add(packageName);
+ return debounce;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
similarity index 74%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
copy to packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
index 98c0a69..5d19851 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
@@ -11,16 +11,15 @@
* 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
*/
-package com.android.systemui.recents.events.activity;
+package com.android.systemui.stackdivider.events;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the history is to be cleared
+ * Sent when the divider is being draged either manually or by an animation.
*/
-public class ClearHistoryEvent extends EventBus.AnimatedEvent {
- // Simple event
+public class StartedDragingEvent extends EventBus.Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
similarity index 74%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
copy to packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
index 98c0a69..c50d6d6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
@@ -11,16 +11,15 @@
* 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
*/
-package com.android.systemui.recents.events.activity;
+package com.android.systemui.stackdivider.events;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the history is to be cleared
+ * Sent when the divider isn't draging anymore.
*/
-public class ClearHistoryEvent extends EventBus.AnimatedEvent {
- // Simple event
+public class StoppedDragingEvent extends EventBus.Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2d2a08a..1c5d28a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1206,10 +1206,10 @@
}
@Override
- public void toggleKeyboardShortcutsMenu() {
+ public void toggleKeyboardShortcutsMenu(int deviceId) {
int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
+ mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
}
/** Jumps to the next affiliated task in the group. */
@@ -1295,8 +1295,8 @@
}
}
- protected void toggleKeyboardShortcuts() {
- getKeyboardShortcuts().toggleKeyboardShortcuts();
+ protected void toggleKeyboardShortcuts(int deviceId) {
+ getKeyboardShortcuts().toggleKeyboardShortcuts(deviceId);
}
protected void cancelPreloadingRecents() {
@@ -1469,7 +1469,7 @@
showRecentsPreviousAffiliatedTask();
break;
case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
- toggleKeyboardShortcuts();
+ toggleKeyboardShortcuts(m.arg1);
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6a98488..99b6397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -73,6 +73,7 @@
private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT;
private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT;
private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT;
+ private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -91,36 +92,37 @@
* These methods are called back on the main thread.
*/
public interface Callbacks {
- public void setIcon(String slot, StatusBarIcon icon);
- public void removeIcon(String slot);
- public void disable(int state1, int state2, boolean animate);
- public void animateExpandNotificationsPanel();
- public void animateCollapsePanels(int flags);
- public void animateExpandSettingsPanel(String obj);
- public void setSystemUiVisibility(int vis, int fullscreenStackVis,
+ void setIcon(String slot, StatusBarIcon icon);
+ void removeIcon(String slot);
+ void disable(int state1, int state2, boolean animate);
+ void animateExpandNotificationsPanel();
+ void animateCollapsePanels(int flags);
+ void animateExpandSettingsPanel(String obj);
+ void setSystemUiVisibility(int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds);
- public void topAppWindowChanged(boolean visible);
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+ void topAppWindowChanged(boolean visible);
+ void setImeWindowStatus(IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
- public void showRecentApps(boolean triggeredFromAltTab);
- public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
- public void toggleRecentApps();
- public void toggleSplitScreen();
- public void preloadRecentApps();
- public void toggleKeyboardShortcutsMenu();
- public void cancelPreloadRecentApps();
- public void setWindowState(int window, int state);
- public void buzzBeepBlinked();
- public void notificationLightOff();
- public void notificationLightPulse(int argb, int onMillis, int offMillis);
- public void showScreenPinningRequest();
- public void appTransitionPending();
- public void appTransitionCancelled();
- public void appTransitionStarting(long startTime, long duration);
- public void showAssistDisclosure();
- public void startAssist(Bundle args);
- public void onCameraLaunchGestureDetected(int source);
- public void requestTvPictureInPicture();
+ void showRecentApps(boolean triggeredFromAltTab);
+ void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+ void toggleRecentApps();
+ void toggleSplitScreen();
+ void preloadRecentApps();
+ void toggleKeyboardShortcutsMenu(int deviceId);
+ void cancelPreloadRecentApps();
+ void setWindowState(int window, int state);
+ void buzzBeepBlinked();
+ void notificationLightOff();
+ void notificationLightPulse(int argb, int onMillis, int offMillis);
+ void showScreenPinningRequest();
+ void appTransitionPending();
+ void appTransitionCancelled();
+ void appTransitionStarting(long startTime, long duration);
+ void appTransitionFinished();
+ void showAssistDisclosure();
+ void startAssist(Bundle args);
+ void onCameraLaunchGestureDetected(int source);
+ void requestTvPictureInPicture();
void addQsTile(ComponentName tile);
void remQsTile(ComponentName tile);
@@ -254,10 +256,10 @@
}
@Override
- public void toggleKeyboardShortcutsMenu() {
+ public void toggleKeyboardShortcutsMenu(int deviceId) {
synchronized (mLock) {
mHandler.removeMessages(MSG_TOGGLE_KEYBOARD_SHORTCUTS);
- mHandler.obtainMessage(MSG_TOGGLE_KEYBOARD_SHORTCUTS).sendToTarget();
+ mHandler.obtainMessage(MSG_TOGGLE_KEYBOARD_SHORTCUTS, deviceId, 0).sendToTarget();
}
}
@@ -324,6 +326,14 @@
}
}
+ @Override
+ public void appTransitionFinished() {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_APP_TRANSITION_FINISHED);
+ mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
+ }
+ }
+
public void showAssistDisclosure() {
synchronized (mLock) {
mHandler.removeMessages(MSG_ASSIST_DISCLOSURE);
@@ -425,7 +435,7 @@
mCallbacks.cancelPreloadRecentApps();
break;
case MSG_TOGGLE_KEYBOARD_SHORTCUTS:
- mCallbacks.toggleKeyboardShortcutsMenu();
+ mCallbacks.toggleKeyboardShortcutsMenu(msg.arg1);
break;
case MSG_SET_WINDOW_STATE:
mCallbacks.setWindowState(msg.arg1, msg.arg2);
@@ -452,6 +462,9 @@
Pair<Long, Long> data = (Pair<Long, Long>) msg.obj;
mCallbacks.appTransitionStarting(data.first, data.second);
break;
+ case MSG_APP_TRANSITION_FINISHED:
+ mCallbacks.appTransitionFinished();
+ break;
case MSG_ASSIST_DISCLOSURE:
mCallbacks.showAssistDisclosure();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 60c2fa6..8fe60a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -21,9 +21,14 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
+import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
+import android.util.SparseArray;
import android.view.ContextThemeWrapper;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -42,16 +47,163 @@
import java.util.List;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
-import static android.view.Gravity.TOP;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
/**
* Contains functionality for handling keyboard shortcuts.
*/
public class KeyboardShortcuts {
- private static final char SYSTEM_HOME_BASE_CHARACTER = '\u2386';
- private static final char SYSTEM_BACK_BASE_CHARACTER = '\u007F';
- private static final char SYSTEM_RECENTS_BASE_CHARACTER = '\u0009';
+ private static final String TAG = KeyboardShortcuts.class.getSimpleName();
+
+ private static final SparseArray<String> SPECIAL_CHARACTER_NAMES = new SparseArray<>();
+ private static final SparseArray<String> MODIFIER_NAMES = new SparseArray<>();
+
+ private static void loadSpecialCharacterNames(Context context) {
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_BACK, context.getString(R.string.keyboard_key_back));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_DPAD_UP, context.getString(R.string.keyboard_key_dpad_up));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_DPAD_DOWN, context.getString(R.string.keyboard_key_dpad_down));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_DPAD_LEFT, context.getString(R.string.keyboard_key_dpad_left));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_DPAD_RIGHT, context.getString(R.string.keyboard_key_dpad_right));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_DPAD_CENTER, context.getString(R.string.keyboard_key_dpad_center));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_PERIOD, ".");
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_TAB, context.getString(R.string.keyboard_key_tab));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_SPACE, context.getString(R.string.keyboard_key_space));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_ENTER, context.getString(R.string.keyboard_key_enter));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_DEL, context.getString(R.string.keyboard_key_backspace));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
+ context.getString(R.string.keyboard_key_media_play_pause));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_MEDIA_STOP, context.getString(R.string.keyboard_key_media_stop));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_MEDIA_NEXT, context.getString(R.string.keyboard_key_media_next));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS,
+ context.getString(R.string.keyboard_key_media_previous));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_REWIND,
+ context.getString(R.string.keyboard_key_media_rewind));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
+ context.getString(R.string.keyboard_key_media_fast_forward));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_PAGE_UP, context.getString(R.string.keyboard_key_page_up));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_PAGE_DOWN, context.getString(R.string.keyboard_key_page_down));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_A,
+ context.getString(R.string.keyboard_key_button_template, "A"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_B,
+ context.getString(R.string.keyboard_key_button_template, "B"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_C,
+ context.getString(R.string.keyboard_key_button_template, "C"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_X,
+ context.getString(R.string.keyboard_key_button_template, "X"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_Y,
+ context.getString(R.string.keyboard_key_button_template, "Y"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_Z,
+ context.getString(R.string.keyboard_key_button_template, "Z"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_L1,
+ context.getString(R.string.keyboard_key_button_template, "L1"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_R1,
+ context.getString(R.string.keyboard_key_button_template, "R1"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_L2,
+ context.getString(R.string.keyboard_key_button_template, "L2"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_R2,
+ context.getString(R.string.keyboard_key_button_template, "R2"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_START,
+ context.getString(R.string.keyboard_key_button_template, "Start"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_SELECT,
+ context.getString(R.string.keyboard_key_button_template, "Select"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_MODE,
+ context.getString(R.string.keyboard_key_button_template, "Mode"));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_FORWARD_DEL, context.getString(R.string.keyboard_key_forward_del));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_ESCAPE, "Esc");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_SYSRQ, "SysRq");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BREAK, "Break");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_SCROLL_LOCK, "Scroll Lock");
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_MOVE_HOME, context.getString(R.string.keyboard_key_move_home));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_MOVE_END, context.getString(R.string.keyboard_key_move_end));
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_INSERT, context.getString(R.string.keyboard_key_insert));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F1, "F1");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F2, "F2");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F3, "F3");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F4, "F4");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F5, "F5");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F6, "F6");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F7, "F7");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F8, "F8");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F9, "F9");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F10, "F10");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F11, "F11");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F12, "F12");
+ SPECIAL_CHARACTER_NAMES.put(
+ KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_0,
+ context.getString(R.string.keyboard_key_numpad_template, "0"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_1,
+ context.getString(R.string.keyboard_key_numpad_template, "1"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_2,
+ context.getString(R.string.keyboard_key_numpad_template, "2"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_3,
+ context.getString(R.string.keyboard_key_numpad_template, "3"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_4,
+ context.getString(R.string.keyboard_key_numpad_template, "4"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_5,
+ context.getString(R.string.keyboard_key_numpad_template, "5"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_6,
+ context.getString(R.string.keyboard_key_numpad_template, "6"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_7,
+ context.getString(R.string.keyboard_key_numpad_template, "7"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_8,
+ context.getString(R.string.keyboard_key_numpad_template, "8"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_9,
+ context.getString(R.string.keyboard_key_numpad_template, "9"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE,
+ context.getString(R.string.keyboard_key_numpad_template, "/"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
+ context.getString(R.string.keyboard_key_numpad_template, "*"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
+ context.getString(R.string.keyboard_key_numpad_template, "-"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_ADD,
+ context.getString(R.string.keyboard_key_numpad_template, "+"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_DOT,
+ context.getString(R.string.keyboard_key_numpad_template, "."));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_COMMA,
+ context.getString(R.string.keyboard_key_numpad_template, ","));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_ENTER,
+ context.getString(R.string.keyboard_key_numpad_template,
+ context.getString(R.string.keyboard_key_enter)));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_EQUALS,
+ context.getString(R.string.keyboard_key_numpad_template, "="));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN,
+ context.getString(R.string.keyboard_key_numpad_template, "("));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN,
+ context.getString(R.string.keyboard_key_numpad_template, ")"));
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_ZENKAKU_HANKAKU, "半角/全角");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_EISU, "英数");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_HENKAN, "変換");
+ SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
+
+ MODIFIER_NAMES.put(KeyEvent.META_META_ON, "Meta");
+ MODIFIER_NAMES.put(KeyEvent.META_CTRL_ON, "Ctrl");
+ MODIFIER_NAMES.put(KeyEvent.META_ALT_ON, "Alt");
+ MODIFIER_NAMES.put(KeyEvent.META_SHIFT_ON, "Shift");
+ MODIFIER_NAMES.put(KeyEvent.META_SYM_ON, "Sym");
+ MODIFIER_NAMES.put(KeyEvent.META_FUNCTION_ON, "Fn");
+ }
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Context mContext;
@@ -62,12 +214,20 @@
};
private Dialog mKeyboardShortcutsDialog;
+ private KeyCharacterMap mKeyCharacterMap;
public KeyboardShortcuts(Context context) {
this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
+ if (SPECIAL_CHARACTER_NAMES.size() == 0) {
+ loadSpecialCharacterNames(context);
+ }
}
- public void toggleKeyboardShortcuts() {
+ public void toggleKeyboardShortcuts(int deviceId) {
+ InputDevice inputDevice = InputManager.getInstance().getInputDevice(deviceId);
+ if (inputDevice != null) {
+ mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+ }
if (mKeyboardShortcutsDialog == null) {
Recents.getSystemServices().requestKeyboardShortcuts(mContext,
new KeyboardShortcutsReceiver() {
@@ -75,20 +235,20 @@
public void onKeyboardShortcutsReceived(
final List<KeyboardShortcutGroup> result) {
KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
- mContext.getString(R.string.keyboard_shortcut_group_system), true);
+ mContext.getString(R.string.keyboard_shortcut_group_system), true);
systemGroup.addItem(new KeyboardShortcutInfo(
- mContext.getString(R.string.keyboard_shortcut_group_system_home),
- SYSTEM_HOME_BASE_CHARACTER, KeyEvent.META_META_ON));
+ mContext.getString(R.string.keyboard_shortcut_group_system_home),
+ KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON));
systemGroup.addItem(new KeyboardShortcutInfo(
- mContext.getString(R.string.keyboard_shortcut_group_system_back),
- SYSTEM_BACK_BASE_CHARACTER, KeyEvent.META_META_ON));
+ mContext.getString(R.string.keyboard_shortcut_group_system_back),
+ KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON));
systemGroup.addItem(new KeyboardShortcutInfo(
- mContext.getString(R.string.keyboard_shortcut_group_system_recents),
- SYSTEM_RECENTS_BASE_CHARACTER, KeyEvent.META_ALT_ON));
+ mContext.getString(R.string.keyboard_shortcut_group_system_recents),
+ KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
result.add(systemGroup);
showKeyboardShortcutsDialog(result);
}
- });
+ }, deviceId);
} else {
dismissKeyboardShortcutsDialog();
}
@@ -148,6 +308,18 @@
final int itemsSize = group.getItems().size();
for (int j = 0; j < itemsSize; j++) {
KeyboardShortcutInfo info = group.getItems().get(j);
+ if (info.getKeycode() != KeyEvent.KEYCODE_UNKNOWN
+ && !KeyCharacterMap.deviceHasKey(info.getKeycode())) {
+ // The user can't achieve this shortcut, so skipping.
+ Log.w(TAG, "Keyboard Shortcut contains key not on device, skipping.");
+ continue;
+ }
+ List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
+ if (shortcutKeys == null) {
+ // Ignore shortcuts we can't display keys for.
+ Log.w(TAG, "Keyboard Shortcut contains unsupported keys, skipping.");
+ continue;
+ }
View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item,
shortcutContainer, false);
TextView textView = (TextView) shortcutView
@@ -156,7 +328,6 @@
ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView
.findViewById(R.id.keyboard_shortcuts_item_container);
- List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
final int shortcutKeysSize = shortcutKeys.size();
for (int k = 0; k < shortcutKeysSize; k++) {
String shortcutKey = shortcutKeys.get(k);
@@ -178,11 +349,46 @@
}
private List<String> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
- // TODO: fix the shortcuts. Find or build an util which can produce human readable
- // names of the baseCharacter and the modifiers.
- List<String> shortcutKeys = new ArrayList<>();
- shortcutKeys.add(KeyEvent.metaStateToString(info.getModifiers()).toUpperCase());
- shortcutKeys.add(Character.getName(info.getBaseCharacter()).toUpperCase());
+ List<String> shortcutKeys = getHumanReadableModifiers(info);
+ if (shortcutKeys == null) {
+ return null;
+ }
+ String displayLabelString;
+ if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
+ displayLabelString = String.valueOf(info.getBaseCharacter());
+ } else if (SPECIAL_CHARACTER_NAMES.get(info.getKeycode()) != null) {
+ displayLabelString = SPECIAL_CHARACTER_NAMES.get(info.getKeycode());
+ } else {
+ // TODO: Have a generic map for when we don't have the device's.
+ char displayLabel = mKeyCharacterMap == null
+ ? 0 : mKeyCharacterMap.getDisplayLabel(info.getKeycode());
+ if (displayLabel != 0) {
+ displayLabelString = String.valueOf(displayLabel);
+ } else {
+ return null;
+ }
+ }
+ shortcutKeys.add(displayLabelString.toUpperCase());
+ return shortcutKeys;
+ }
+
+ private List<String> getHumanReadableModifiers(KeyboardShortcutInfo info) {
+ final List<String> shortcutKeys = new ArrayList<>();
+ int modifiers = info.getModifiers();
+ if (modifiers == 0) {
+ return shortcutKeys;
+ }
+ for(int i = 0; i < MODIFIER_NAMES.size(); ++i) {
+ final int supportedModifier = MODIFIER_NAMES.keyAt(i);
+ if ((modifiers & supportedModifier) != 0) {
+ shortcutKeys.add(MODIFIER_NAMES.get(supportedModifier).toUpperCase());
+ modifiers &= ~supportedModifier;
+ }
+ }
+ if (modifiers != 0) {
+ // Remaining unsupported modifiers, don't show anything.
+ return null;
+ }
return shortcutKeys;
}
}
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 c32ef0e..4add3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.car;
import android.app.ActivityManager;
-import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -25,6 +24,7 @@
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
@@ -33,6 +33,7 @@
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;
@@ -40,9 +41,7 @@
* A status bar (and navigation bar) tailored for the automotive use case.
*/
public class CarStatusBar extends PhoneStatusBar {
- private SystemServicesProxy mSystemServicesProxy;
private TaskStackListenerImpl mTaskStackListener;
- private Handler mHandler;
private CarNavigationBarView mCarNavigationBar;
private CarNavigationBarController mController;
@@ -51,10 +50,8 @@
@Override
public void start() {
super.start();
- mHandler = new Handler();
- mTaskStackListener = new TaskStackListenerImpl(mHandler);
- mSystemServicesProxy = new SystemServicesProxy(mContext);
- mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
+ mTaskStackListener = new TaskStackListenerImpl();
+ SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
registerPackageChangeReceivers();
}
@@ -114,47 +111,16 @@
}
/**
- * An implementation of ITaskStackListener, that listens for changes in the system task
+ * An implementation of TaskStackListener, that listens for changes in the system task
* stack and notifies the navigation bar.
*/
- private class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
- private Handler mHandler;
-
- public TaskStackListenerImpl(Handler handler) {
- this.mHandler = handler;
- }
-
- @Override
- public void onActivityPinned() {
- }
-
- @Override
- public void onPinnedActivityRestartAttempt() {
- }
-
- @Override
- public void onPinnedStackAnimationEnded() {
- }
-
+ private class TaskStackListenerImpl extends TaskStackListener {
@Override
public void onTaskStackChanged() {
- mHandler.removeCallbacks(this);
- mHandler.post(this);
- }
-
- @Override
- public void run() {
- ensureMainThread();
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
mController.taskChanged(runningTaskInfo.baseActivity.getPackageName());
}
-
- private void ensureMainThread() {
- if (!Looper.getMainLooper().isCurrentThread()) {
- throw new RuntimeException("Must be called on the UI thread");
- }
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index e5e3caf..7486519 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -136,7 +136,7 @@
private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionFromOverscroll;
private float mLastOverscroll;
- private boolean mQsExpansionEnabled = true;
+ protected boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
@@ -202,6 +202,7 @@
notifyBarPanelExpansionChanged();
}
};
+ private NotificationGroupManager mGroupManager;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -415,6 +416,11 @@
if (!(child instanceof ExpandableNotificationRow)) {
continue;
}
+ boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
+ ((ExpandableNotificationRow) child).getStatusBarNotification());
+ if (suppressedSummary) {
+ continue;
+ }
availableSpace -= child.getMinHeight() + notificationPadding;
if (availableSpace >= 0 && count < maximum) {
count++;
@@ -1731,7 +1737,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mNavigationBarBottomHeight = insets.getSystemWindowInsetBottom();
+ mNavigationBarBottomHeight = insets.getStableInsetBottom();
updateMaxHeadsUpTranslation();
return insets;
}
@@ -2298,4 +2304,8 @@
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
}
+
+ public void setGroupManager(NotificationGroupManager groupManager) {
+ mGroupManager = groupManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index f0df706..960515b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -75,7 +75,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
+ setPadding(0, 0, 0, insets.getStableInsetBottom());
return insets;
}
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 86031e1..7ae87e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -120,6 +120,7 @@
import com.android.systemui.qs.QSPanel;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
@@ -708,6 +709,7 @@
mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
+ mNotificationPanel.setGroupManager(mGroupManager);
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
@@ -4309,6 +4311,7 @@
@Override
public void appTransitionCancelled() {
mIconController.appTransitionCancelled();
+ EventBus.getDefault().send(new AppTransitionFinishedEvent());
}
@Override
@@ -4325,6 +4328,11 @@
}
@Override
+ public void appTransitionFinished() {
+ EventBus.getDefault().send(new AppTransitionFinishedEvent());
+ }
+
+ @Override
public void onCameraLaunchGestureDetected(int source) {
mLastCameraLaunchSource = source;
if (mStartedGoingToSleep) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index ada7450..b271380 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -138,11 +138,7 @@
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
- if (state.remoteInputActive) {
- mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
- } else {
- mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
- }
+ mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
private void applyHeight(State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 3a0336b..1f4ef4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -46,7 +46,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.stack.LongPressCancelable;
+import com.android.systemui.statusbar.stack.ScrollContainer;
/**
* Host for the remote input.
@@ -67,7 +67,9 @@
private RemoteInputController mController;
private NotificationData.Entry mEntry;
- private LongPressCancelable mLongPressCancelable;
+
+ private ScrollContainer mScrollContainer;
+ private View mScrollContainerChild;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -106,7 +108,7 @@
mEditText.setOnClickListener(this);
mEditText.addTextChangedListener(this);
mEditText.setInnerFocusable(false);
- mEditText.mDefocusListener = this;
+ mEditText.mRemoteInputView = this;
}
private void sendRemoteInput() {
@@ -237,23 +239,34 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if (mLongPressCancelable == null) {
- ViewParent p = getParent();
- while (p != null) {
- if (p instanceof LongPressCancelable) {
- mLongPressCancelable = (LongPressCancelable) p;
- break;
- }
- p = p.getParent();
- }
- }
- if (mLongPressCancelable != null) {
- mLongPressCancelable.requestDisallowLongPress();
+ findScrollContainer();
+ if (mScrollContainer != null) {
+ mScrollContainer.requestDisallowLongPress();
}
}
return super.onInterceptTouchEvent(ev);
}
+ public boolean requestScrollTo() {
+ findScrollContainer();
+ mScrollContainer.scrollTo(mScrollContainerChild);
+ return true;
+ }
+
+ private void findScrollContainer() {
+ if (mScrollContainer == null) {
+ ViewParent p = this;
+ while (p != null) {
+ if (p.getParent() instanceof ScrollContainer) {
+ mScrollContainer = (ScrollContainer) p.getParent();
+ mScrollContainerChild = (View) p;
+ break;
+ }
+ p = p.getParent();
+ }
+ }
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -261,7 +274,7 @@
public static class RemoteEditText extends EditText {
private final Drawable mBackground;
- private RemoteInputView mDefocusListener;
+ private RemoteInputView mRemoteInputView;
boolean mShowImeOnInputConnection;
public RemoteEditText(Context context, AttributeSet attrs) {
@@ -270,13 +283,13 @@
}
private void defocusIfNeeded() {
- if (mDefocusListener.mEntry.row.isChangingPosition()) {
+ if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()) {
return;
}
if (isFocusable() && isEnabled()) {
setInnerFocusable(false);
- if (mDefocusListener != null) {
- mDefocusListener.onDefocus();
+ if (mRemoteInputView != null) {
+ mRemoteInputView.onDefocus();
}
mShowImeOnInputConnection = false;
}
@@ -300,13 +313,6 @@
}
@Override
- public boolean requestRectangleOnScreen(Rect r) {
- r.top = mScrollY;
- r.bottom = mScrollY + (mBottom - mTop);
- return super.requestRectangleOnScreen(r);
- }
-
- @Override
public void getFocusedRect(Rect r) {
super.getFocusedRect(r);
r.top = mScrollY;
@@ -314,6 +320,11 @@
}
@Override
+ public boolean requestRectangleOnScreen(Rect rectangle) {
+ return mRemoteInputView.requestScrollTo();
+ }
+
+ @Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
defocusIfNeeded();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index bc276b7..b5030e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -43,6 +43,7 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.OverScroller;
@@ -83,7 +84,7 @@
public class NotificationStackScrollLayout extends ViewGroup
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
- SettingsIconRowListener, LongPressCancelable {
+ SettingsIconRowListener, ScrollContainer {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -134,7 +135,7 @@
private int mPaddingBetweenElements;
private int mIncreasedPaddingBetweenElements;
private int mTopPadding;
- private int mCollapseSecondCardPadding;
+ private int mBottomInset = 0;
/**
* The algorithm which calculates the properties for our children
@@ -206,6 +207,7 @@
private float mStackTranslation;
private float mTopPaddingOverflow;
private boolean mDontReportNextOverScroll;
+ private boolean mDontClampNextScroll;
private boolean mRequestViewResizeAnimationOnLayout;
private boolean mNeedViewResizeAnimation;
private View mExpandedGroupView;
@@ -919,6 +921,45 @@
mScrollingEnabled = enable;
}
+ public void scrollTo(View v) {
+ ExpandableView expandableView = (ExpandableView) v;
+ int positionInLinearLayout = getPositionInLinearLayout(v);
+
+ int targetScroll = positionInLinearLayout + expandableView.getActualHeight() +
+ mBottomInset - getHeight() + getTopPadding();
+ if (mOwnScrollY < targetScroll) {
+ mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
+ mDontReportNextOverScroll = true;
+ postInvalidateOnAnimation();
+ }
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mBottomInset = Math.max(0, insets.getSystemWindowInsetBottom()
+ - (getRootView().getHeight() - getHeight()));
+
+ int range = getScrollRange();
+ if (mOwnScrollY > range) {
+ // HACK: We're repeatedly getting staggered insets here while the IME is
+ // animating away. To work around that we'll wait until things have settled.
+ removeCallbacks(mReclamp);
+ postDelayed(mReclamp, 50);
+ }
+ return insets;
+ }
+
+ private Runnable mReclamp = new Runnable() {
+ @Override
+ public void run() {
+ int range = getScrollRange();
+ mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
+ mDontReportNextOverScroll = true;
+ mDontClampNextScroll = true;
+ postInvalidateOnAnimation();
+ }
+ };
+
public void setExpandingEnabled(boolean enable) {
mExpandHelper.setEnabled(enable);
}
@@ -1256,7 +1297,7 @@
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
- final int range = getScrollRange();
+ int range = getScrollRange();
if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
float currVelocity = mScroller.getCurrVelocity();
if (currVelocity >= mMinimumVelocity) {
@@ -1264,6 +1305,9 @@
}
}
+ if (mDontClampNextScroll) {
+ range = Math.max(range, oldY);
+ }
overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
0, (int) (mMaxOverScroll), false);
onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
@@ -1271,6 +1315,8 @@
// Keep on drawing until the animation has finished.
postInvalidateOnAnimation();
+ } else {
+ mDontClampNextScroll = false;
}
}
@@ -1474,7 +1520,9 @@
- firstChild.getMinHeight());
}
}
- return scrollRange;
+ int imeOverlap = Math.max(0,
+ getContentHeight() - (getHeight() - mBottomInset));
+ return scrollRange + imeOverlap;
}
/**
@@ -2015,6 +2063,14 @@
}
}
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ if (disallowIntercept) {
+ mSwipeHelper.removeLongPressCallback();
+ }
+ }
+
private void onViewRemovedInternal(View child) {
if (mChangePositionInProgress) {
// This is only a position change, don't do anything special
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java
index 05f0c07..a35465e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java
@@ -16,13 +16,21 @@
package com.android.systemui.statusbar.stack;
+import android.view.View;
+
/**
- * Interface for container layouts that listen for long presses. A child that
- * wants to handle long press can use this to cancel the parents long press logic.
+ * Interface for container layouts that scroll and listen for long presses. A child that
+ * wants to handle long press can use this to cancel the parents long press logic or request
+ * to be made visible by scrolling to it.
*/
-public interface LongPressCancelable {
+public interface ScrollContainer {
/**
* Request that the view does not perform long press for the current touch.
*/
void requestDisallowLongPress();
+
+ /**
+ * Request that the view is made visible by scrolling to it.
+ */
+ void scrollTo(View v);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2524e1a..f9bb5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,6 +171,10 @@
}
@Override
+ public void appTransitionFinished() {
+ }
+
+ @Override
public void onCameraLaunchGestureDetected(int source) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
new file mode 100644
index 0000000..0e64dcd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -0,0 +1,251 @@
+/*
+ * 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.tv.pip;
+
+import android.app.Activity;
+import android.content.Context;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.LinearLayout;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+
+import static android.media.session.PlaybackState.ACTION_PAUSE;
+import static android.media.session.PlaybackState.ACTION_PLAY;
+
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
+
+
+/**
+ * A view containing PIP controls including fullscreen, close, and media controls.
+ */
+public class PipControlsView extends LinearLayout implements PipManager.Listener {
+ /**
+ * An interface to listen user action.
+ */
+ public interface Listener {
+ /**
+ * Called when an user clicks close PIP button.
+ */
+ void onClosed();
+ }
+
+ private final PipManager mPipManager = PipManager.getInstance();
+ private MediaController mMediaController;
+ private Listener mListener;
+
+ private View mFullButtonView;
+ private View mFullDescriptionView;
+ private View mPlayPauseView;
+ private ImageView mPlayPauseButtonImageView;
+ private TextView mPlayPauseDescriptionTextView;
+ private View mCloseButtonView;
+ private View mCloseDescriptionView;
+
+ private boolean mHasFocus;
+ private OnFocusChangeListener mOnChildFocusChangeListener;
+
+ private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ updatePlayPauseView();
+ }
+ };
+
+ public PipControlsView(Context context) {
+ this(context, null, 0, 0);
+ }
+
+ public PipControlsView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0, 0);
+ }
+
+ public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+
+ mFullButtonView = findViewById(R.id.full_button);
+ mFullDescriptionView = findViewById(R.id.full_desc);
+ mFullButtonView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPipManager.movePipToFullscreen();
+ }
+ });
+ mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ onChildViewFocusChanged();
+ }
+ });
+
+ mPlayPauseView = findViewById(R.id.play_pause);
+ mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
+ mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
+ mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+ return;
+ }
+ long actions = mMediaController.getPlaybackState().getActions();
+ int state = mMediaController.getPlaybackState().getState();
+ if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PAUSED) {
+ mMediaController.getTransportControls().play();
+ } else if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PLAYING) {
+ mMediaController.getTransportControls().pause();
+ }
+ // View will be updated later in {@link mMediaControllerCallback}
+ }
+ });
+ mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPlayPauseDescriptionTextView.setVisibility(
+ hasFocus ? View.VISIBLE : View.INVISIBLE);
+ onChildViewFocusChanged();
+ }
+ });
+
+ mCloseButtonView = findViewById(R.id.close_button);
+ mCloseDescriptionView = findViewById(R.id.close_desc);
+ mCloseButtonView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPipManager.closePip();
+ mListener.onClosed();
+ }
+ });
+ mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ onChildViewFocusChanged();
+ }
+ });
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ updateMediaController();
+ mPipManager.addListener(this);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPipManager.removeListener(this);
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
+ }
+
+ private void updateMediaController() {
+ MediaController newController = mPipManager.getMediaController();
+ if (mMediaController == newController) {
+ return;
+ }
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
+ mMediaController = newController;
+ if (mMediaController != null) {
+ mMediaController.registerCallback(mMediaControllerCallback);
+ }
+ updatePlayPauseView();
+ }
+
+ private void updatePlayPauseView() {
+ int state = mPipManager.getPlaybackState();
+ if (state == PLAYBACK_STATE_UNAVAILABLE) {
+ mPlayPauseView.setVisibility(View.GONE);
+ } else {
+ mPlayPauseView.setVisibility(View.VISIBLE);
+ if (state == PLAYBACK_STATE_PLAYING) {
+ mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
+ mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
+ } else {
+ mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
+ mPlayPauseDescriptionTextView.setText(R.string.pip_play);
+ }
+ }
+ }
+
+ /**
+ * Sets a listener to be invoked when {@link android.view.View.hasFocus()} is changed.
+ */
+ public void setOnChildFocusChangeListener(OnFocusChangeListener listener) {
+ mOnChildFocusChangeListener = listener;
+ }
+
+ private void onChildViewFocusChanged() {
+ // At this moment, hasFocus() returns true although there's no focused child.
+ boolean hasFocus = (mFullButtonView != null && mFullButtonView.isFocused())
+ || (mPlayPauseButtonImageView != null && mPlayPauseButtonImageView.isFocused())
+ || (mCloseButtonView != null && mCloseButtonView.isFocused());
+ if (mHasFocus != hasFocus) {
+ mHasFocus = hasFocus;
+ if (mOnChildFocusChangeListener != null) {
+ mOnChildFocusChangeListener.onFocusChange(getFocusedChild(), mHasFocus);
+ }
+ }
+ }
+
+ /**
+ * Sets the {@link Listener} to listen user actions.
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onPipEntered() { }
+
+ @Override
+ public void onPipActivityClosed() { }
+
+ @Override
+ public void onShowPipMenu() { }
+
+ @Override
+ public void onMoveToFullscreen() { }
+
+ @Override
+ public void onMediaControllerChanged() {
+ updateMediaController();
+ }
+
+ @Override
+ public void onPipResizeAboutToStart() { }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index 95cee4c..68e0883 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -21,7 +21,6 @@
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.IActivityManager;
-import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -31,6 +30,7 @@
import android.graphics.Rect;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.os.Debug;
import android.os.Handler;
import android.os.RemoteException;
@@ -38,6 +38,9 @@
import android.util.Log;
import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import java.util.ArrayList;
import java.util.List;
@@ -69,12 +72,23 @@
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
+ /**
+ * PIPed activity is playing a media and it can be paused.
+ */
+ static final int PLAYBACK_STATE_PLAYING = 0;
+ /**
+ * PIPed activity has a paused media and it can be played.
+ */
+ static final int PLAYBACK_STATE_PAUSED = 1;
+ /**
+ * Users are unable to control PIPed activity's media playback.
+ */
+ static final int PLAYBACK_STATE_UNAVAILABLE = 2;
+
private static final int CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS = 3000;
private int mSuspendPipResizingReason;
- private static final float SCALE_FACTOR = 1.1f;
-
private Context mContext;
private IActivityManager mActivityManager;
private MediaSessionManager mMediaSessionManager;
@@ -86,6 +100,7 @@
private Rect mMenuModePipBounds;
private Rect mRecentsPipBounds;
private Rect mRecentsFocusedPipBounds;
+ private int mRecentsFocusChangedAnimationDurationMs;
private boolean mInitialized;
private int mPipTaskId = TASK_ID_NO_PIP;
private ComponentName mPipComponentName;
@@ -95,89 +110,6 @@
private boolean mIsRecentsShown;
private boolean mIsPipFocusedInRecent;
- private final Runnable mOnActivityPinnedRunnable = new Runnable() {
- @Override
- public void run() {
- StackInfo stackInfo = null;
- try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (stackInfo == null) {
- Log.w(TAG, "Cannot find pinned stack");
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "getStackInfo failed", e);
- return;
- }
- if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
- mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
- mPipComponentName = ComponentName.unflattenFromString(
- stackInfo.taskNames[stackInfo.taskNames.length - 1]);
- // Set state to overlay so we show it when the pinned stack animation ends.
- mState = STATE_PIP_OVERLAY;
- mCurrentPipBounds = mPipBounds;
- launchPipOnboardingActivityIfNeeded();
- mMediaSessionManager.addOnActiveSessionsChangedListener(
- mActiveMediaSessionListener, null);
- updateMediaController(mMediaSessionManager.getActiveSessions(null));
- if (mIsRecentsShown) {
- // If an activity becomes PIPed again after the fullscreen, the Recents is shown
- // behind so we need to resize the pinned stack and show the correct overlay.
- resizePinnedStack(STATE_PIP_OVERLAY);
- }
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onPipEntered();
- }
- }
- };
- private final Runnable mOnTaskStackChanged = new Runnable() {
- @Override
- public void run() {
- if (mState != STATE_NO_PIP) {
- StackInfo stackInfo = null;
- try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (stackInfo == null) {
- Log.w(TAG, "There is no pinned stack");
- closePipInternal(false);
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "getStackInfo failed", e);
- return;
- }
- for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
- if (stackInfo.taskIds[i] == mPipTaskId) {
- // PIP task is still alive.
- return;
- }
- }
- // PIP task doesn't exist anymore in PINNED_STACK.
- closePipInternal(true);
- }
- }
- };
- private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
- @Override
- public void run() {
- // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
- movePipToFullscreen();
- }
- };
- private final Runnable mOnPinnedStackAnimationEnded = new Runnable() {
- @Override
- public void run() {
- switch (mState) {
- case STATE_PIP_OVERLAY:
- showPipOverlay();
- break;
- case STATE_PIP_MENU:
- showPipMenu();
- break;
- }
- }
- };
-
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@Override
public void run() {
@@ -230,24 +162,16 @@
mPipBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
mMenuModePipBounds = Rect.unflattenFromString(res.getString(
- com.android.internal.R.string.config_centeredPictureInPictureBounds));
+ R.string.pip_menu_bounds));
mRecentsPipBounds = Rect.unflattenFromString(res.getString(
- com.android.internal.R.string.config_pictureInPictureBoundsInRecents));
- float scaleBy = (SCALE_FACTOR - 1.0f) / 2;
- mRecentsFocusedPipBounds = new Rect(
- (int) (mRecentsPipBounds.left - scaleBy * mRecentsPipBounds.width()),
- (int) (mRecentsPipBounds.top - scaleBy * mRecentsPipBounds.height()),
- (int) (mRecentsPipBounds.right + scaleBy * mRecentsPipBounds.width()),
- (int) (mRecentsPipBounds.bottom + scaleBy * mRecentsPipBounds.height()));
+ R.string.pip_recents_bounds));
+ mRecentsFocusedPipBounds = Rect.unflattenFromString(res.getString(
+ R.string.pip_recents_focused_bounds));
+ mRecentsFocusChangedAnimationDurationMs = res.getInteger(
+ R.integer.recents_tv_pip_focus_anim_duration);
mActivityManager = ActivityManagerNative.getDefault();
- TaskStackListener taskStackListener = new TaskStackListener();
- IActivityManager iam = ActivityManagerNative.getDefault();
- try {
- iam.registerTaskStackListener(taskStackListener);
- } catch (RemoteException e) {
- Log.e(TAG, "registerTaskStackListener failed", e);
- }
+ SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
@@ -367,6 +291,7 @@
mSuspendPipResizingReason);
return;
}
+ int animationDurationMs = -1;
switch (mState) {
case STATE_NO_PIP:
mCurrentPipBounds = null;
@@ -376,6 +301,10 @@
break;
case STATE_PIP_OVERLAY:
if (mIsRecentsShown) {
+ if (mCurrentPipBounds == mRecentsFocusedPipBounds
+ || mCurrentPipBounds == mRecentsFocusedPipBounds) {
+ animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
+ }
if (mIsPipFocusedInRecent) {
mCurrentPipBounds = mRecentsFocusedPipBounds;
} else {
@@ -390,7 +319,8 @@
break;
}
try {
- mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true, -1);
+ mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
+ true, true, true, animationDurationMs);
} catch (RemoteException e) {
Log.e(TAG, "resizeStack failed", e);
}
@@ -452,9 +382,17 @@
}
/**
+ * Returns {@code true} if the PIP view in
+ * {@link com.android.systemui.recents.tv.RecentsTvActivity} is focused in Recents.
+ * This API is valid only when {@link isRecentsShown()} returns {@code true}.
+ */
+ boolean isPipViewFocusdInRecents() {
+ return mIsPipFocusedInRecent;
+ }
+
+ /**
* Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
- * stack to the centered PIP bound {@link com.android.internal.R.string
- * .config_centeredPictureInPictureBounds}.
+ * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
*/
private void showPipMenu() {
if (DEBUG) Log.d(TAG, "showPipMenu()");
@@ -566,33 +504,114 @@
return mPipMediaController;
}
- private class TaskStackListener extends ITaskStackListener.Stub {
+ /**
+ * Returns the PIPed activity's playback state.
+ * This returns one of {@link PLAYBACK_STATE_PLAYING}, {@link PLAYBACK_STATE_PAUSED},
+ * or {@link PLAYBACK_STATE_UNAVAILABLE}.
+ */
+ int getPlaybackState() {
+ if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
+ return PLAYBACK_STATE_UNAVAILABLE;
+ }
+ int state = mPipMediaController.getPlaybackState().getState();
+ boolean isPlaying = (state == PlaybackState.STATE_BUFFERING
+ || state == PlaybackState.STATE_CONNECTING
+ || state == PlaybackState.STATE_PLAYING
+ || state == PlaybackState.STATE_FAST_FORWARDING
+ || state == PlaybackState.STATE_REWINDING
+ || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
+ || state == PlaybackState.STATE_SKIPPING_TO_NEXT);
+ long actions = mPipMediaController.getPlaybackState().getActions();
+ if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
+ return PLAYBACK_STATE_PAUSED;
+ } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
+ return PLAYBACK_STATE_PLAYING;
+ }
+ return PLAYBACK_STATE_UNAVAILABLE;
+ }
+
+ TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
- public void onTaskStackChanged() throws RemoteException {
- // Post the message back to the UI thread.
- mHandler.post(mOnTaskStackChanged);
+ public void onTaskStackChanged() {
+ if (mState != STATE_NO_PIP) {
+ StackInfo stackInfo = null;
+ try {
+ stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (stackInfo == null) {
+ Log.w(TAG, "There is no pinned stack");
+ closePipInternal(false);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getStackInfo failed", e);
+ return;
+ }
+ for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
+ if (stackInfo.taskIds[i] == mPipTaskId) {
+ // PIP task is still alive.
+ return;
+ }
+ }
+ // PIP task doesn't exist anymore in PINNED_STACK.
+ closePipInternal(true);
+ }
}
@Override
- public void onActivityPinned() throws RemoteException {
- // Post the message back to the UI thread.
+ public void onActivityPinned() {
if (DEBUG) Log.d(TAG, "onActivityPinned()");
- mHandler.post(mOnActivityPinnedRunnable);
+ StackInfo stackInfo = null;
+ try {
+ stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (stackInfo == null) {
+ Log.w(TAG, "Cannot find pinned stack");
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getStackInfo failed", e);
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
+ mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
+ mPipComponentName = ComponentName.unflattenFromString(
+ stackInfo.taskNames[stackInfo.taskNames.length - 1]);
+ // Set state to overlay so we show it when the pinned stack animation ends.
+ mState = STATE_PIP_OVERLAY;
+ mCurrentPipBounds = mPipBounds;
+ launchPipOnboardingActivityIfNeeded();
+ mMediaSessionManager.addOnActiveSessionsChangedListener(
+ mActiveMediaSessionListener, null);
+ updateMediaController(mMediaSessionManager.getActiveSessions(null));
+ if (mIsRecentsShown) {
+ // If an activity becomes PIPed again after the fullscreen, the Recents is shown
+ // behind so we need to resize the pinned stack and show the correct overlay.
+ resizePinnedStack(STATE_PIP_OVERLAY);
+ }
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onPipEntered();
+ }
}
@Override
public void onPinnedActivityRestartAttempt() {
- // Post the message back to the UI thread.
if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
- mHandler.post(mOnPinnedActivityRestartAttempt);
+ // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
+ movePipToFullscreen();
}
@Override
public void onPinnedStackAnimationEnded() {
if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
- mHandler.post(mOnPinnedStackAnimationEnded);
+ switch (mState) {
+ case STATE_PIP_OVERLAY:
+ showPipOverlay();
+ break;
+ case STATE_PIP_MENU:
+ showPipMenu();
+ break;
+ }
}
- }
+ };
/**
* A listener interface to receive notification on changes in PIP.
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index b8b837a..ea9275f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -17,12 +17,7 @@
package com.android.systemui.tv.pip;
import android.app.Activity;
-import android.media.session.MediaController;
-import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
@@ -31,8 +26,6 @@
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-import static android.media.session.PlaybackState.ACTION_PAUSE;
-import static android.media.session.PlaybackState.ACTION_PLAY;
/**
* Activity to show the PIP menu to control PIP.
@@ -41,131 +34,17 @@
private static final String TAG = "PipMenuActivity";
private final PipManager mPipManager = PipManager.getInstance();
- private MediaController mMediaController;
- private View mFullButtonView;
- private View mFullDescriptionView;
- private View mPlayPauseView;
- private ImageView mPlayPauseButtonImageView;
- private TextView mPlayPauseDescriptionTextView;
- private View mCloseButtonView;
- private View mCloseDescriptionView;
+ private PipControlsView mPipControlsView;
private boolean mPipMovedToFullscreen;
- private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- updatePlayPauseView(state);
- }
- };
-
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.tv_pip_menu);
mPipManager.addListener(this);
- mFullButtonView = findViewById(R.id.full_button);
- mFullDescriptionView = findViewById(R.id.full_desc);
- mFullButtonView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.movePipToFullscreen();
- mPipMovedToFullscreen = true;
- finish();
- }
- });
- mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
- }
- });
- mPlayPauseView = findViewById(R.id.play_pause);
- mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
- mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
- mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mMediaController == null || mMediaController.getPlaybackState() == null) {
- return;
- }
- long actions = mMediaController.getPlaybackState().getActions();
- int state = mMediaController.getPlaybackState().getState();
- if (((actions & ACTION_PLAY) != 0) && !isPlaying(state)) {
- mMediaController.getTransportControls().play();
- } else if ((actions & ACTION_PAUSE) != 0 && isPlaying(state)) {
- mMediaController.getTransportControls().pause();
- }
- // View will be updated later in {@link mMediaControllerCallback}
- }
- });
- mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mPlayPauseDescriptionTextView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
- }
- });
-
- mCloseButtonView = findViewById(R.id.close_button);
- mCloseDescriptionView = findViewById(R.id.close_desc);
- mCloseButtonView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.closePip();
- finish();
- }
- });
- mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
- }
- });
- updateMediaController();
- }
-
- private void updateMediaController() {
- MediaController newController = mPipManager.getMediaController();
- if (mMediaController == newController) {
- return;
- }
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
- }
- mMediaController = newController;
- if (mMediaController != null) {
- mMediaController.registerCallback(mMediaControllerCallback);
- updatePlayPauseView(mMediaController.getPlaybackState());
- } else {
- updatePlayPauseView(null);
- }
- }
-
- private void updatePlayPauseView(PlaybackState playbackState) {
- if (playbackState != null
- && (playbackState.getActions() & (ACTION_PLAY | ACTION_PAUSE)) != 0) {
- mPlayPauseView.setVisibility(View.VISIBLE);
- if (isPlaying(playbackState.getState())) {
- mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
- mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
- } else {
- mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
- mPlayPauseDescriptionTextView.setText(R.string.pip_play);
- }
- } else {
- mPlayPauseView.setVisibility(View.GONE);
- }
- }
-
- private boolean isPlaying(int state) {
- return state == PlaybackState.STATE_BUFFERING
- || state == PlaybackState.STATE_CONNECTING
- || state == PlaybackState.STATE_PLAYING
- || state == PlaybackState.STATE_FAST_FORWARDING
- || state == PlaybackState.STATE_REWINDING
- || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
- || state == PlaybackState.STATE_SKIPPING_TO_NEXT;
+ mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
}
private void restorePipAndFinish() {
@@ -184,9 +63,6 @@
@Override
protected void onDestroy() {
super.onDestroy();
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
- }
mPipManager.removeListener(this);
mPipManager.resumePipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
@@ -210,13 +86,12 @@
@Override
public void onMoveToFullscreen() {
+ mPipMovedToFullscreen = true;
finish();
}
@Override
- public void onMediaControllerChanged() {
- updateMediaController();
- }
+ public void onMediaControllerChanged() { }
@Override
public void onPipResizeAboutToStart() {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index 1de321d..12cb4cd 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
+import android.widget.ImageView;
import com.android.systemui.R;
@@ -46,9 +47,10 @@
private final Handler mHandler = new Handler();
private View mGuideOverlayView;
private View mGuideButtonsView;
+ private ImageView mGuideButtonPlayPauseImageView;
private final Runnable mHideGuideOverlayRunnable = new Runnable() {
public void run() {
- mGuideOverlayView.setVisibility(View.INVISIBLE);
+ mGuideOverlayView.setVisibility(View.GONE);
}
};
@@ -71,6 +73,7 @@
setContentView(R.layout.tv_pip_overlay);
mGuideOverlayView = findViewById(R.id.guide_overlay);
mGuideButtonsView = findViewById(R.id.guide_buttons);
+ mGuideButtonPlayPauseImageView = (ImageView) findViewById(R.id.guide_button_play_pause);
mPipManager.addListener(this);
sPipOverlayActivity = this;
@@ -80,12 +83,17 @@
protected void onResume() {
super.onResume();
// TODO: Implement animation for this
- if (!mPipManager.isRecentsShown()) {
- mGuideOverlayView.setVisibility(View.VISIBLE);
- mGuideButtonsView.setVisibility(View.INVISIBLE);
+ if (mPipManager.isRecentsShown()) {
+ mGuideOverlayView.setVisibility(View.GONE);
+ if (mPipManager.isPipViewFocusdInRecents()) {
+ mGuideButtonsView.setVisibility(View.GONE);
+ } else {
+ mGuideButtonsView.setVisibility(View.VISIBLE);
+ updateGuideButtonsView();
+ }
} else {
- mGuideOverlayView.setVisibility(View.INVISIBLE);
- mGuideButtonsView.setVisibility(View.VISIBLE);
+ mGuideOverlayView.setVisibility(View.VISIBLE);
+ mGuideButtonsView.setVisibility(View.GONE);
}
mHandler.removeCallbacks(mHideGuideOverlayRunnable);
mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
@@ -134,11 +142,30 @@
}
@Override
- public void onMediaControllerChanged() { }
+ public void onMediaControllerChanged() {
+ updateGuideButtonsView();
+ }
@Override
public void finish() {
sPipOverlayActivity = null;
super.finish();
}
+
+ private void updateGuideButtonsView() {
+ switch (mPipManager.getPlaybackState()) {
+ case PipManager.PLAYBACK_STATE_PLAYING:
+ mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
+ mGuideButtonPlayPauseImageView.setImageResource(R.drawable.ic_pause_white_24dp);
+ break;
+ case PipManager.PLAYBACK_STATE_PAUSED:
+ mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
+ mGuideButtonPlayPauseImageView.setImageResource(
+ R.drawable.ic_play_arrow_white_24dp);
+ break;
+ case PipManager.PLAYBACK_STATE_UNAVAILABLE:
+ mGuideButtonPlayPauseImageView.setVisibility(View.GONE);
+ break;
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e710ded..e3dac28 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1812,6 +1812,87 @@
return mKeyEventDispatcher;
}
+ /**
+ * Enables accessibility service specified by {@param componentName} for the {@param userId}.
+ */
+ public void enableAccessibilityService(ComponentName componentName, int userId) {
+ synchronized(mLock) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("only SYSTEM can call enableAccessibilityService.");
+ }
+
+ SettingsStringHelper settingsHelper = new SettingsStringHelper(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+ settingsHelper.addService(componentName);
+ settingsHelper.writeToSettings();
+
+ UserState userState = getUserStateLocked(userId);
+ if (userState.mEnabledServices.add(componentName)) {
+ onUserStateChangedLocked(userState);
+ }
+ }
+ }
+
+ /**
+ * Disables accessibility service specified by {@param componentName} for the {@param userId}.
+ */
+ public void disableAccessibilityService(ComponentName componentName, int userId) {
+ synchronized(mLock) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("only SYSTEM can call disableAccessibility");
+ }
+
+ SettingsStringHelper settingsHelper = new SettingsStringHelper(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+ settingsHelper.deleteService(componentName);
+ settingsHelper.writeToSettings();
+
+ UserState userState = getUserStateLocked(userId);
+ if (userState.mEnabledServices.remove(componentName)) {
+ onUserStateChangedLocked(userState);
+ }
+ }
+ }
+
+ private class SettingsStringHelper {
+ private static final String SETTINGS_DELIMITER = ":";
+ private ContentResolver mContentResolver;
+ private final String mSettingsName;
+ private Set<String> mServices;
+ private final int mUserId;
+
+ public SettingsStringHelper(String name, int userId) {
+ mUserId = userId;
+ mSettingsName = name;
+ mContentResolver = mContext.getContentResolver();
+ String servicesString = Settings.Secure.getStringForUser(
+ mContentResolver, mSettingsName, userId);
+ mServices = new HashSet();
+ if (!TextUtils.isEmpty(servicesString)) {
+ final TextUtils.SimpleStringSplitter colonSplitter =
+ new TextUtils.SimpleStringSplitter(SETTINGS_DELIMITER.charAt(0));
+ colonSplitter.setString(servicesString);
+ while (colonSplitter.hasNext()) {
+ final String serviceName = colonSplitter.next();
+ mServices.add(serviceName);
+ }
+ }
+ }
+
+ public void addService(ComponentName component) {
+ mServices.add(component.flattenToString());
+ }
+
+ public void deleteService(ComponentName component) {
+ mServices.remove(component.flattenToString());
+ }
+
+ public void writeToSettings() {
+ Settings.Secure.putStringForUser(mContentResolver, mSettingsName,
+ TextUtils.join(SETTINGS_DELIMITER, mServices), mUserId);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5b01062..95d3cc3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -71,6 +71,7 @@
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
+import android.net.metrics.ConnectivityServiceChangeEvent;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -125,7 +126,6 @@
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.PermissionMonitor;
-import com.android.server.connectivity.ApfFilter;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -354,13 +354,6 @@
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
- /**
- * used to push APF program to NetworkAgent
- * replyTo = NetworkAgent message handler
- * obj = byte[] of APF program
- */
- private static final int EVENT_PUSH_APF_PROGRAM_TO_NETWORK = 32;
-
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -630,7 +623,7 @@
mDefaultRequest = createInternetRequestForTransport(-1);
NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest,
- new Binder(), NetworkRequestInfo.REQUEST);
+ new Binder(), NetworkRequestType.REQUEST);
mNetworkRequests.put(mDefaultRequest, defaultNRI);
mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
@@ -806,7 +799,7 @@
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- null, mDefaultMobileDataRequest, new Binder(), NetworkRequestInfo.REQUEST));
+ null, mDefaultMobileDataRequest, new Binder(), NetworkRequestType.REQUEST));
} else {
handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID);
}
@@ -1802,20 +1795,6 @@
}
}
- private void dumpApf(IndentingPrintWriter pw) {
- pw.println("APF filters:");
- pw.increaseIndent();
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (nai.apfFilter != null) {
- pw.println(nai.name() + ":");
- pw.increaseIndent();
- nai.apfFilter.dump(pw);
- pw.decreaseIndent();
- }
- }
- pw.decreaseIndent();
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1833,11 +1812,6 @@
return;
}
- if (argsContain(args, "apf")) {
- dumpApf(pw);
- return;
- }
-
pw.print("NetworkFactories for:");
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
pw.print(" " + nfi.name);
@@ -1901,7 +1875,6 @@
mKeepaliveTracker.dump(pw);
pw.println();
- dumpApf(pw);
if (mInetLog != null && mInetLog.size() > 0) {
pw.println();
@@ -1945,7 +1918,7 @@
}
private boolean isRequest(NetworkRequest request) {
- return mNetworkRequests.get(request).isRequest;
+ return mNetworkRequests.get(request).isRequest();
}
// must be stateless - things change under us.
@@ -2164,7 +2137,7 @@
if (VDBG) log("NetworkFactory connected");
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.isRequest == false) continue;
+ if (!nri.isRequest()) continue;
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);
@@ -2223,7 +2196,6 @@
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
- if (nai.apfFilter != null) nai.apfFilter.shutdown();
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
synchronized (mNetworkForNetId) {
@@ -2301,7 +2273,7 @@
private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
- if (!nri.isRequest) {
+ if (!nri.isRequest()) {
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
if (nri.request.networkCapabilities.hasSignalStrength() &&
network.satisfiesImmutableCapabilitiesOf(nri.request)) {
@@ -2310,7 +2282,7 @@
}
}
rematchAllNetworksAndRequests(null, 0);
- if (nri.isRequest && mNetworkForRequestId.get(nri.request.requestId) == null) {
+ if (nri.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) {
sendUpdatedScoreToFactories(nri.request, 0);
}
}
@@ -2331,7 +2303,7 @@
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
// If this Network is already the highest scoring Network for a request, or if
// there is hope for it to become one if it validated, then it is needed.
- if (nri.isRequest && nai.satisfies(nri.request) &&
+ if (nri.isRequest() && nai.satisfies(nri.request) &&
(nai.networkRequests.get(nri.request.requestId) != null ||
// Note that this catches two important cases:
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
@@ -2359,7 +2331,7 @@
nri.unlinkDeathRecipient();
mNetworkRequests.remove(request);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
- if (nri.isRequest) {
+ if (nri.isRequest()) {
// Find all networks that are satisfying this request and remove the request
// from their request lists.
// TODO - it's my understanding that for a request there is only a single
@@ -2438,13 +2410,6 @@
accept ? 1 : 0, always ? 1: 0, network));
}
- public void pushApfProgramToNetwork(NetworkAgentInfo nai, byte[] program) {
- enforceConnectivityInternalPermission();
- Message msg = mHandler.obtainMessage(EVENT_PUSH_APF_PROGRAM_TO_NETWORK, program);
- msg.replyTo = nai.messenger;
- mHandler.sendMessage(msg);
- }
-
private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
if (DBG) log("handleSetAcceptUnvalidated network=" + network +
" accept=" + accept + " always=" + always);
@@ -2594,16 +2559,6 @@
handleMobileDataAlwaysOn();
break;
}
- case EVENT_PUSH_APF_PROGRAM_TO_NETWORK: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_PUSH_APF_PROGRAM_TO_NETWORK from unknown NetworkAgent");
- } else {
- nai.asyncChannel.sendMessage(NetworkAgent.CMD_PUSH_APF_PROGRAM,
- (byte[]) msg.obj);
- }
- break;
- }
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
@@ -3736,13 +3691,36 @@
}
/**
+ * A NetworkRequest as registered by an application can be one of three
+ * types:
+ *
+ * - "listen", for which the framework will issue callbacks about any
+ * and all networks that match the specified NetworkCapabilities,
+ *
+ * - "request", capable of causing a specific network to be created
+ * first (e.g. a telephony DUN request), the framework will issue
+ * callbacks about the single, highest scoring current network
+ * (if any) that matches the specified NetworkCapabilities, or
+ *
+ * - "track the default network", a hybrid of the two designed such
+ * that the framework will issue callbacks for the single, highest
+ * scoring current network (if any) that matches the capabilities of
+ * the default Internet request (mDefaultRequest), but which cannot
+ * cause the framework to either create or retain the existence of
+ * any specific network.
+ *
+ */
+ private static enum NetworkRequestType {
+ LISTEN,
+ TRACK_DEFAULT,
+ REQUEST
+ };
+
+ /**
* Tracks info about the requester.
* Also used to notice when the calling process dies so we can self-expire
*/
private class NetworkRequestInfo implements IBinder.DeathRecipient {
- static final boolean REQUEST = true;
- static final boolean LISTEN = false;
-
final NetworkRequest request;
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
@@ -3750,26 +3728,26 @@
final int mPid;
final int mUid;
final Messenger messenger;
- final boolean isRequest;
+ private final NetworkRequestType mType;
- NetworkRequestInfo(NetworkRequest r, PendingIntent pi, boolean isRequest) {
+ NetworkRequestInfo(NetworkRequest r, PendingIntent pi, NetworkRequestType type) {
request = r;
mPendingIntent = pi;
messenger = null;
mBinder = null;
mPid = getCallingPid();
mUid = getCallingUid();
- this.isRequest = isRequest;
+ mType = type;
}
- NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) {
+ NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, NetworkRequestType type) {
super();
messenger = m;
request = r;
mBinder = binder;
mPid = getCallingPid();
mUid = getCallingUid();
- this.isRequest = isRequest;
+ mType = type;
mPendingIntent = null;
try {
@@ -3779,6 +3757,16 @@
}
}
+ private String typeString() {
+ switch (mType) {
+ case LISTEN: return "Listen";
+ case REQUEST: return "Request";
+ case TRACK_DEFAULT: return "Track default";
+ default:
+ return "unknown type";
+ }
+ }
+
void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
@@ -3791,8 +3779,27 @@
releaseNetworkRequest(request);
}
+ /**
+ * Returns true iff. the contained NetworkRequest is one that:
+ *
+ * - should be associated with at most one satisfying network
+ * at a time;
+ *
+ * - should cause a network to be kept up if it is the only network
+ * which can satisfy the NetworkReqeust.
+ *
+ * For full detail of how isRequest() is used for pairing Networks with
+ * NetworkRequests read rematchNetworkAndRequests().
+ *
+ * TODO: Rename to something more properly descriptive.
+ */
+ public boolean isRequest() {
+ return (mType == NetworkRequestType.TRACK_DEFAULT) ||
+ (mType == NetworkRequestType.REQUEST);
+ }
+
public String toString() {
- return (isRequest ? "Request" : "Listen") +
+ return typeString() +
" from uid/pid:" + mUid + "/" + mPid +
" for " + request +
(mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
@@ -3845,10 +3852,13 @@
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
+ final NetworkRequestType type = (networkCapabilities == null)
+ ? NetworkRequestType.TRACK_DEFAULT
+ : NetworkRequestType.REQUEST;
// If the requested networkCapabilities is null, take them instead from
// the default network request. This allows callers to keep track of
// the system default network.
- if (networkCapabilities == null) {
+ if (type == NetworkRequestType.TRACK_DEFAULT) {
networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities);
enforceAccessPermission();
} else {
@@ -3870,8 +3880,7 @@
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId());
- NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
- NetworkRequestInfo.REQUEST);
+ NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, type);
if (DBG) log("requestNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
@@ -3936,7 +3945,7 @@
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
- NetworkRequestInfo.REQUEST);
+ NetworkRequestType.REQUEST);
if (DBG) log("pendingRequest for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
nri));
@@ -3988,7 +3997,7 @@
NetworkRequest networkRequest = new NetworkRequest(
new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
- NetworkRequestInfo.LISTEN);
+ NetworkRequestType.LISTEN);
if (DBG) log("listenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -4006,7 +4015,7 @@
NetworkRequest networkRequest = new NetworkRequest(
new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
- NetworkRequestInfo.LISTEN);
+ NetworkRequestType.LISTEN);
if (DBG) log("pendingListenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -4131,9 +4140,6 @@
if (networkAgent.clatd != null) {
networkAgent.clatd.fixupLinkProperties(oldLp);
}
- if (networkAgent.apfFilter != null) {
- networkAgent.apfFilter.updateFilter();
- }
updateInterfaces(newLp, oldLp, netId);
updateMtu(newLp, oldLp);
@@ -4457,6 +4463,7 @@
private void makeDefault(NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
+ ConnectivityServiceChangeEvent.logEvent(newNetwork.network.netId);
setupDataActivityTracking(newNetwork);
try {
mNetd.setDefaultNetId(newNetwork.network.netId);
@@ -4523,7 +4530,7 @@
// check if it satisfies the NetworkCapabilities
if (VDBG) log(" checking if request is satisfied: " + nri.request);
if (satisfies) {
- if (!nri.isRequest) {
+ if (!nri.isRequest()) {
// This is not a request, it's a callback listener.
// Add it to newNetwork regardless of score.
if (newNetwork.addRequest(nri.request)) addedRequests.add(nri);
@@ -4583,7 +4590,7 @@
mNetworkForRequestId.remove(nri.request.requestId);
sendUpdatedScoreToFactories(nri.request, 0);
} else {
- if (nri.isRequest == true) {
+ if (nri.isRequest()) {
Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
newNetwork.name() +
" without updating mNetworkForRequestId or factories!");
@@ -5087,5 +5094,4 @@
NetworkAgentInfo nai, NetworkRequest defaultRequest) {
return new NetworkMonitor(context, handler, nai, defaultRequest);
}
-
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index b57e14e..4749417 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -684,13 +684,18 @@
public long SMS_TEMP_APP_WHITELIST_DURATION;
private final ContentResolver mResolver;
+ private final boolean mHasWatch;
private final KeyValueListParser mParser = new KeyValueListParser(',');
public Constants(Handler handler, ContentResolver resolver) {
super(handler);
mResolver = resolver;
- mResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS), false, this);
+ mHasWatch = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
+ mResolver.registerContentObserver(Settings.Global.getUriFor(
+ mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
+ : Settings.Global.DEVICE_IDLE_CONSTANTS),
+ false, this);
updateConstants();
}
@@ -703,7 +708,8 @@
synchronized (DeviceIdleController.this) {
try {
mParser.setString(Settings.Global.getString(mResolver,
- Settings.Global.DEVICE_IDLE_CONSTANTS));
+ mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
+ : Settings.Global.DEVICE_IDLE_CONSTANTS));
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
@@ -724,8 +730,9 @@
MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(
KEY_MIN_DEEP_MAINTENANCE_TIME,
!COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
+ long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;
INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
- !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
+ !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
!DEBUG ? 4 * 60 * 1000L : 60 * 1000L);
LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT,
@@ -733,8 +740,10 @@
LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
+ long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L;
IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
- !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
+ !COMPRESS_TIME ? idleAfterInactiveTimeout
+ : (idleAfterInactiveTimeout / 10));
IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_IDLE_PENDING_TIMEOUT,
!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
MAX_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_PENDING_TIMEOUT,
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 044bb04..e29515f 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -51,17 +51,15 @@
* 2) ASHMEM_SIZE (for scratch space used during dumping)
* 3) ASHMEM_SIZE * HISTORY_SIZE
*
- * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 20. Assuming
- * the system then also has 10 active rendering processes in the worst case
- * this would end up using under 14KiB (12KiB for the buffers, plus some overhead
- * for userId, pid, package name, and a couple other objects)
+ * This is currently under 16KiB total memory in the worst case of
+ * 20 processes in history + 10 unique active processes.
*
* @hide */
public class GraphicsStatsService extends IGraphicsStats.Stub {
public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
private static final String TAG = "GraphicsStatsService";
- private static final int ASHMEM_SIZE = 256;
+ private static final int ASHMEM_SIZE = 296;
private static final int HISTORY_SIZE = 20;
private final Context mContext;
diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index 575d99e..23cf64a 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -23,6 +23,8 @@
import android.os.CpuUsageInfo;
import android.os.IHardwarePropertiesManager;
import android.os.Process;
+import android.os.UserHandle;
+import com.android.server.vr.VrManagerInternal;
import java.util.Arrays;
@@ -78,14 +80,15 @@
*
* @param callingPackage The calling package name.
*
- * @throws SecurityException if a non profile or device owner or system tries to retrieve
- * information provided by the service.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
private void enforceHardwarePropertiesRetrievalAllowed(String callingPackage)
throws SecurityException {
final PackageManager pm = mContext.getPackageManager();
+ int uid = 0;
try {
- final int uid = pm.getPackageUid(callingPackage, 0);
+ uid = pm.getPackageUid(callingPackage, 0);
if (Binder.getCallingUid() != uid) {
throw new SecurityException("The caller has faked the package name.");
}
@@ -93,10 +96,13 @@
throw new SecurityException("The caller has faked the package name.");
}
+ final int userId = UserHandle.getUserId(uid);
+ final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (!dpm.isDeviceOwnerApp(callingPackage) && !dpm.isProfileOwnerApp(callingPackage)
- && Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("The caller is not a device or profile owner or system.");
+ && !vrService.isCurrentVrListener(callingPackage, userId)) {
+ throw new SecurityException("The caller is not a device or profile owner or bound "
+ + "VrListenerService.");
}
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 898d5b73..e042483 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -215,7 +215,7 @@
// Ongoing notification
private NotificationManager mNotificationManager;
private KeyguardManager mKeyguardManager;
- private StatusBarManagerService mStatusBar;
+ private @Nullable StatusBarManagerService mStatusBar;
private Notification.Builder mImeSwitcherNotification;
private PendingIntent mImeSwitchPendingIntent;
private boolean mShowOngoingImeSwitcherForPhones;
@@ -1070,7 +1070,9 @@
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mStatusBar = statusBar;
- statusBar.setIconVisibility(mSlotIme, false);
+ if (mStatusBar != null) {
+ mStatusBar.setIconVisibility(mSlotIme, false);
+ }
updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
com.android.internal.R.bool.show_ongoing_ime_switcher);
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 43d10c7..3fdcceb 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -779,11 +779,11 @@
}
}
- if (hasNonDefaults) {
+ if (debug && hasNonDefaults) {
if (dest.size() == 0) {
- Slog.w(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
+ Slog.v(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
} else if (dest.size() > 1) {
- Slog.w(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
+ Slog.v(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
}
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 548b662..c6d536d 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -209,9 +209,12 @@
/** Set of interfaces with active alerts. */
@GuardedBy("mQuotaLock")
private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
- /** Set of UIDs with active reject rules. */
+ /** Set of UIDs blacklisted on metered networks. */
@GuardedBy("mQuotaLock")
- private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+ private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray();
+ /** Set of UIDs whitelisted on metered networks. */
+ @GuardedBy("mQuotaLock")
+ private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray();
/** Set of UIDs with cleartext penalties. */
@GuardedBy("mQuotaLock")
private SparseIntArray mUidCleartextPolicy = new SparseIntArray();
@@ -240,6 +243,9 @@
@GuardedBy("mQuotaLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ @GuardedBy("mQuotaLock")
+ private boolean mDataSaverMode;
+
private Object mIdleTimerLock = new Object();
/** Set of interfaces with active idle timers. */
private static class IdleTimerParams {
@@ -513,6 +519,28 @@
}
}
+ // Sync the state of the given chain with the native daemon.
+ private void syncFirewallChainLocked(int chain, SparseIntArray uidFirewallRules, String name) {
+ int size = uidFirewallRules.size();
+ if (size > 0) {
+ // Make a copy of the current rules, and then clear them. This is because
+ // setFirewallUidRuleInternal only pushes down rules to the native daemon if they are
+ // different from the current rules stored in the mUidFirewall*Rules array for the
+ // specified chain. If we don't clear the rules, setFirewallUidRuleInternal will do
+ // nothing.
+ final SparseIntArray rules = uidFirewallRules.clone();
+ uidFirewallRules.clear();
+
+ // Now push the rules. setFirewallUidRuleInternal will push each of these down to the
+ // native daemon, and also add them to the mUidFirewall*Rules array for the specified
+ // chain.
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall " + name + "UID rules");
+ for (int i = 0; i < rules.size(); i++) {
+ setFirewallUidRuleInternal(chain, rules.keyAt(i), rules.valueAt(i));
+ }
+ }
+ }
+
/**
* Prepare native daemon once connected, enabling modules and pushing any
* existing in-memory rules.
@@ -561,6 +589,9 @@
// push any existing quota or UID rules
synchronized (mQuotaLock) {
+
+ setDataSaverModeEnabled(mDataSaverMode);
+
int size = mActiveQuotas.size();
if (size > 0) {
if (DBG) Slog.d(TAG, "Pushing " + size + " active quota rules");
@@ -581,13 +612,25 @@
}
}
- size = mUidRejectOnQuota.size();
+ size = mUidRejectOnMetered.size();
if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active UID rules");
- final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
- mUidRejectOnQuota = new SparseBooleanArray();
+ if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered whitelist rules");
+ final SparseBooleanArray uidRejectOnQuota = mUidRejectOnMetered;
+ mUidRejectOnMetered = new SparseBooleanArray();
for (int i = 0; i < uidRejectOnQuota.size(); i++) {
- setUidNetworkRules(uidRejectOnQuota.keyAt(i), uidRejectOnQuota.valueAt(i));
+ setUidMeteredNetworkBlacklist(uidRejectOnQuota.keyAt(i),
+ uidRejectOnQuota.valueAt(i));
+ }
+ }
+
+ size = mUidAllowOnMetered.size();
+ if (size > 0) {
+ if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered blacklist rules");
+ final SparseBooleanArray uidAcceptOnQuota = mUidAllowOnMetered;
+ mUidAllowOnMetered = new SparseBooleanArray();
+ for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
+ setUidMeteredNetworkWhitelist(uidAcceptOnQuota.keyAt(i),
+ uidAcceptOnQuota.valueAt(i));
}
}
@@ -603,55 +646,18 @@
setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
- size = mUidFirewallRules.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallRules;
- mUidFirewallRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_NONE, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
+ syncFirewallChainLocked(FIREWALL_CHAIN_NONE, mUidFirewallRules, "");
+ syncFirewallChainLocked(FIREWALL_CHAIN_STANDBY, mUidFirewallStandbyRules, "standby ");
+ syncFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mUidFirewallDozableRules, "dozable ");
+ syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, mUidFirewallPowerSaveRules,
+ "powersave ");
- size = mUidFirewallStandbyRules.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall standby UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallStandbyRules;
- mUidFirewallStandbyRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_STANDBY, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)) {
setFirewallChainEnabled(FIREWALL_CHAIN_STANDBY, true);
}
-
- size = mUidFirewallDozableRules.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall dozable UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallDozableRules;
- mUidFirewallDozableRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_DOZABLE, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) {
setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true);
}
-
- size = mUidFirewallPowerSaveRules.size();
- if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall powersave UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallPowerSaveRules;
- mUidFirewallPowerSaveRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_POWERSAVE, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)) {
setFirewallChainEnabled(FIREWALL_CHAIN_POWERSAVE, true);
}
@@ -738,6 +744,7 @@
private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
@Override
public void onDaemonConnected() {
+ Slog.i(TAG, "onDaemonConnected()");
// event is dispatched from internal NDC thread, so we prepare the
// daemon back on main thread.
if (mConnectedSignal != null) {
@@ -1698,28 +1705,30 @@
}
}
- @Override
- public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+ private void setUidOnMeteredNetworkList(SparseBooleanArray quotaList, int uid,
+ boolean blacklist, boolean enable) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
if (!mBandwidthControlEnabled) return;
+ final String chain = blacklist ? "naughtyapps" : "niceapps";
+ final String suffix = enable ? "add" : "remove";
+
synchronized (mQuotaLock) {
- final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
- if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
+ final boolean oldEnable = quotaList.get(uid, false);
+ if (oldEnable == enable) {
// TODO: eventually consider throwing
return;
}
try {
- mConnector.execute("bandwidth",
- rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
- if (rejectOnQuotaInterfaces) {
- mUidRejectOnQuota.put(uid, true);
+ mConnector.execute("bandwidth", suffix + chain, uid);
+ if (enable) {
+ quotaList.put(uid, true);
} else {
- mUidRejectOnQuota.delete(uid);
+ quotaList.delete(uid);
}
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
@@ -1728,6 +1737,39 @@
}
@Override
+ public void setUidMeteredNetworkBlacklist(int uid, boolean enable) {
+ setUidOnMeteredNetworkList(mUidRejectOnMetered, uid, true, enable);
+ }
+
+ @Override
+ public void setUidMeteredNetworkWhitelist(int uid, boolean enable) {
+ setUidOnMeteredNetworkList(mUidAllowOnMetered, uid, false, enable);
+ }
+
+ @Override
+ public boolean setDataSaverModeEnabled(boolean enable) {
+ if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
+ synchronized (mQuotaLock) {
+ if (mDataSaverMode == enable) {
+ Log.w(TAG, "setDataSaverMode(): already " + mDataSaverMode);
+ return true;
+ }
+ try {
+ final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
+ if (changed) {
+ mDataSaverMode = enable;
+ } else {
+ Log.w(TAG, "setDataSaverMode(" + enable + "): netd command silently failed");
+ }
+ return changed;
+ } catch (RemoteException e) {
+ Log.w(TAG, "setDataSaverMode(" + enable + "): netd command failed", e);
+ return false;
+ }
+ }
+ }
+
+ @Override
public void setUidCleartextNetworkPolicy(int uid, int policy) {
if (Binder.getCallingUid() != uid) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -2221,29 +2263,22 @@
synchronized (mQuotaLock) {
pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
- }
-
- synchronized (mUidRejectOnQuota) {
- pw.print("UID reject on quota ifaces: [");
- final int size = mUidRejectOnQuota.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidRejectOnQuota.keyAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ pw.print("Data saver mode: "); pw.println(mDataSaverMode);
+ dumpUidRuleOnQuotaLocked(pw, "blacklist", mUidRejectOnMetered);
+ dumpUidRuleOnQuotaLocked(pw, "whitelist", mUidAllowOnMetered);
}
synchronized (mUidFirewallRules) {
dumpUidFirewallRule(pw, "", mUidFirewallRules);
}
- pw.println("UID firewall standby chain enabled: " +
+ pw.print("UID firewall standby chain enabled: "); pw.println(
mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY));
synchronized (mUidFirewallStandbyRules) {
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules);
}
- pw.println("UID firewall dozable chain enabled: " +
+ pw.print("UID firewall dozable chain enabled: "); pw.println(
mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE));
synchronized (mUidFirewallDozableRules) {
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules);
@@ -2267,6 +2302,29 @@
}
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
+ pw.print("Netd service status: " );
+ if (mNetdService == null) {
+ pw.println("disconnected");
+ } else {
+ try {
+ final boolean alive = mNetdService.isAlive();
+ pw.println(alive ? "alive": "dead");
+ } catch (RemoteException e) {
+ pw.println("unreachable");
+ }
+ }
+ }
+
+ private void dumpUidRuleOnQuotaLocked(PrintWriter pw, String name, SparseBooleanArray list) {
+ pw.print("UID bandwith control ");
+ pw.print(name);
+ pw.print(" rule: [");
+ final int size = list.size();
+ for (int i = 0; i < size; i++) {
+ pw.print(list.keyAt(i));
+ if (i < size - 1) pw.print(",");
+ }
+ pw.println("]");
}
private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c1ec71c..3d13715 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -55,7 +55,6 @@
import com.android.server.pm.Installer;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
-import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
import org.xmlpull.v1.XmlPullParser;
@@ -103,7 +102,6 @@
import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
-import android.app.admin.IDevicePolicyManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.IBackupManager;
@@ -355,6 +353,7 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH;
+import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
@@ -526,6 +525,7 @@
// Determines whether to take full screen screenshots
static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
+ public static final float FULLSCREEN_SCREENSHOT_SCALE = 0.6f;
private static native int nativeMigrateToBoost();
private static native int nativeMigrateFromBoost();
@@ -1470,6 +1470,7 @@
static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66;
+ static final int NOTIFY_FORCED_RESIZABLE_MSG = 67;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1491,6 +1492,7 @@
/** The dimensions of the thumbnails in the Recents UI. */
int mThumbnailWidth;
int mThumbnailHeight;
+ float mFullscreenThumbnailScale;
final ServiceThread mHandlerThread;
final MainHandler mHandler;
@@ -2017,6 +2019,21 @@
}
break;
}
+ case NOTIFY_FORCED_RESIZABLE_MSG: {
+ synchronized (ActivityManagerService.this) {
+ for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+ try {
+ // Make a one-way callback to the listener
+ mTaskStackListeners.getBroadcastItem(i).onActivityForcedResizable(
+ (String) msg.obj, msg.arg1);
+ } catch (RemoteException e){
+ // Handled by the RemoteCallbackList
+ }
+ }
+ mTaskStackListeners.finishBroadcast();
+ }
+ break;
+ }
case NOTIFY_CLEARTEXT_NETWORK_MSG: {
final int uid = msg.arg1;
final byte[] firstPacket = (byte[]) msg.obj;
@@ -3013,7 +3030,7 @@
if (!app.killed) {
Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
Process.killProcessQuiet(app.pid);
- killProcessGroup(app.info.uid, app.pid);
+ killProcessGroup(app.uid, app.pid);
}
if (lrui <= mLruProcessActivityStart) {
mLruProcessActivityStart--;
@@ -3386,7 +3403,7 @@
// clean it up now.
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
checkTime(startTime, "startProcess: bad proc running, killing");
- killProcessGroup(app.info.uid, app.pid);
+ killProcessGroup(app.uid, app.pid);
handleAppDiedLocked(app, true, true);
checkTime(startTime, "startProcess: done killing old proc");
}
@@ -4477,63 +4494,14 @@
}
final long origId = Binder.clearCallingIdentity();
try {
- return startActivityFromRecentsInner(taskId, bOptions);
+ synchronized (this) {
+ return mStackSupervisor.startActivityFromRecentsInner(taskId, bOptions);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
}
- final int startActivityFromRecentsInner(int taskId, Bundle bOptions) {
- final TaskRecord task;
- final int callingUid;
- final String callingPackage;
- final Intent intent;
- final int userId;
- synchronized (this) {
- final ActivityOptions activityOptions = (bOptions != null)
- ? new ActivityOptions(bOptions) : null;
- final int launchStackId = (activityOptions != null)
- ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
-
- if (launchStackId == HOME_STACK_ID) {
- throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
- + taskId + " can't be launch in the home stack.");
- }
- task = mStackSupervisor.anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
- if (task == null) {
- throw new IllegalArgumentException(
- "startActivityFromRecentsInner: Task " + taskId + " not found.");
- }
-
- if (launchStackId != INVALID_STACK_ID) {
- if (launchStackId == DOCKED_STACK_ID && activityOptions != null) {
- mWindowManager.setDockedStackCreateState(
- activityOptions.getDockCreateMode(), null /* initialBounds */);
- }
- if (task.stack.mStackId != launchStackId) {
- mStackSupervisor.moveTaskToStackLocked(
- taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
- ANIMATE);
- }
- }
-
- // If the user must confirm credentials (e.g. when first launching a work app and the
- // Work Challenge is present) let startActivityInPackage handle the intercepting.
- if (!mUserController.shouldConfirmCredentials(task.userId)
- && task.getRootActivity() != null) {
- moveTaskToFrontLocked(task.taskId, 0, bOptions);
- return ActivityManager.START_TASK_TO_FRONT;
- }
- callingUid = task.mCallingUid;
- callingPackage = task.mCallingPackage;
- intent = task.intent;
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
- userId = task.userId;
- }
- return startActivityInPackage(callingUid, callingPackage, intent, null, null, null, 0, 0,
- bOptions, userId, null, task);
- }
-
final int startActivityInPackage(int uid, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId,
@@ -5021,7 +4989,7 @@
if (!fromBinderDied) {
Process.killProcessQuiet(pid);
}
- killProcessGroup(app.info.uid, pid);
+ killProcessGroup(app.uid, pid);
app.killed = true;
}
@@ -11422,18 +11390,18 @@
try {
final int currentUserId = mUserController.getCurrentUserIdLocked();
// Get the focused task before launching launcher.
- final int taskId = (mFocusedActivity == null)
- ? -1 : mFocusedActivity.task.taskId;
- startHomeActivityLocked(currentUserId, "notifyLockedProfile");
+
if (mUserController.isLockScreenDisabled(currentUserId)) {
- // If there is no device lock, we first go to launcher and then resume the
- // original task. Work challenge will be shown because we intercepted
- // startActivityFromRecentsInner and the reason why we switch to home stack
- // first is to prevent pressing back button brings user back to the work
- // app.
- if (taskId != -1) {
- startActivityFromRecentsInner(taskId, null);
+
+ // If there is no device lock, we will show the profile's credential page.
+ // startActivityFromRecentsInner is intercepted and will forward user to it.
+ if (mFocusedActivity != null) {
+ mStackSupervisor.startActivityFromRecentsInner(
+ mFocusedActivity.task.taskId, null);
}
+ } else {
+ // Showing launcher to avoid user entering credential twice.
+ startHomeActivityLocked(currentUserId, "notifyLockedProfile");
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -12592,6 +12560,8 @@
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
+ mFullscreenThumbnailScale = res.getFraction(
+ com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
@@ -21031,6 +21001,20 @@
mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason);
}
}
+
+ @Override
+ public void notifyAppTransitionFinished() {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.notifyAppTransitionDone();
+ }
+ }
+
+ @Override
+ public void notifyAppTransitionCancelled() {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.notifyAppTransitionDone();
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
@@ -21119,7 +21103,9 @@
// Will bring task to front if it already has a root activity.
final long origId = Binder.clearCallingIdentity();
try {
- startActivityFromRecentsInner(mTaskId, null);
+ synchronized (this) {
+ mStackSupervisor.startActivityFromRecentsInner(mTaskId, null);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 26eaa8a..ee406da 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -258,6 +258,12 @@
// Current bounds of the stack or null if fullscreen.
Rect mBounds = null;
+ boolean mUpdateBoundsDeferred;
+ boolean mUpdateBoundsDeferredCalled;
+ final Rect mDeferredBounds = new Rect();
+ final Rect mDeferredTaskBounds = new Rect();
+ final Rect mDeferredTaskInsetBounds = new Rect();
+
long mLaunchStartTime = 0;
long mFullyDrawnStartTime = 0;
@@ -427,6 +433,57 @@
mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
}
+ /**
+ * Defers updating the bounds of the stack. If the stack was resized/repositioned while
+ * deferring, the bounds will update in {@link #continueUpdateBounds()}.
+ */
+ void deferUpdateBounds() {
+ if (!mUpdateBoundsDeferred) {
+ mUpdateBoundsDeferred = true;
+ mUpdateBoundsDeferredCalled = false;
+ }
+ }
+
+ /**
+ * Continues updating bounds after updates have been deferred. If there was a resize attempt
+ * between {@link #deferUpdateBounds()} and {@link #continueUpdateBounds()}, the stack will
+ * be resized to that bounds.
+ */
+ void continueUpdateBounds() {
+ final boolean wasDeferred = mUpdateBoundsDeferred;
+ mUpdateBoundsDeferred = false;
+ if (wasDeferred && mUpdateBoundsDeferredCalled) {
+ mStackSupervisor.resizeStackUncheckedLocked(this,
+ mDeferredBounds.isEmpty() ? null : mDeferredBounds,
+ mDeferredTaskBounds.isEmpty() ? null : mDeferredTaskBounds,
+ mDeferredTaskInsetBounds.isEmpty() ? null : mDeferredTaskInsetBounds);
+ }
+ }
+
+ boolean updateBoundsAllowed(Rect bounds, Rect tempTaskBounds,
+ Rect tempTaskInsetBounds) {
+ if (!mUpdateBoundsDeferred) {
+ return true;
+ }
+ if (bounds != null) {
+ mDeferredBounds.set(bounds);
+ } else {
+ mDeferredBounds.setEmpty();
+ }
+ if (tempTaskBounds != null) {
+ mDeferredTaskBounds.set(tempTaskBounds);
+ } else {
+ mDeferredTaskBounds.setEmpty();
+ }
+ if (tempTaskInsetBounds != null) {
+ mDeferredTaskInsetBounds.set(tempTaskInsetBounds);
+ } else {
+ mDeferredTaskInsetBounds.setEmpty();
+ }
+ mUpdateBoundsDeferredCalled = true;
+ return false;
+ }
+
void setBounds(Rect bounds) {
mBounds = mFullscreen ? null : new Rect(bounds);
if (mTaskPositioner != null) {
@@ -929,7 +986,7 @@
// use within SystemUI while keeping memory usage low.
if (ActivityManagerService.TAKE_FULLSCREEN_SCREENSHOTS) {
w = h = -1;
- scale = 0.5f;
+ scale = mService.mFullscreenThumbnailScale;
}
return mWindowManager.screenshotApplications(who.appToken, Display.DEFAULT_DISPLAY,
w, h, scale);
@@ -1980,7 +2037,9 @@
if (next == null) {
// There are no more activities!
final String reason = "noMoreActivities";
- if (!mFullscreen && adjustFocusToNextFocusableStackLocked(reason)) {
+ final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack()
+ ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+ if (!mFullscreen && adjustFocusToNextFocusableStackLocked(returnTaskType, reason)) {
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen.
return mStackSupervisor.resumeFocusedStackTopActivityLocked(
@@ -1993,8 +2052,6 @@
"resumeTopActivityLocked: No more activities go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
// Only resume home if on home display
- final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
- HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
return isOnHomeDisplay() &&
mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
}
@@ -3004,16 +3061,18 @@
} else {
final TaskRecord task = r.task;
if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
+ final int taskToReturnTo = task.getTaskToReturnTo();
+
// For non-fullscreen stack, we want to move the focus to the next visible
// stack to prevent the home screen from moving to the top and obscuring
// other visible stacks.
- if (!mFullscreen && adjustFocusToNextFocusableStackLocked(myReason)) {
+ if (!mFullscreen
+ && adjustFocusToNextFocusableStackLocked(taskToReturnTo, myReason)) {
return;
}
// Move the home stack to the top if this stack is fullscreen or there is no
// other visible stack.
- if (mStackSupervisor.moveHomeStackTaskToTop(
- task.getTaskToReturnTo(), myReason)) {
+ if (mStackSupervisor.moveHomeStackTaskToTop(taskToReturnTo, myReason)) {
// Activity focus was already adjusted. Nothing else to do...
return;
}
@@ -3024,12 +3083,16 @@
mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked(), myReason);
}
- private boolean adjustFocusToNextFocusableStackLocked(String reason) {
+ private boolean adjustFocusToNextFocusableStackLocked(int taskToReturnTo, String reason) {
final ActivityStack stack = getNextFocusableStackLocked();
final String myReason = reason + " adjustFocusToNextFocusableStack";
if (stack == null) {
return false;
}
+
+ if (stack.isHomeStack()) {
+ return mStackSupervisor.moveHomeStackTaskToTop(taskToReturnTo, reason);
+ }
return mService.setFocusedActivityLocked(stack.topRunningActivityLocked(), myReason);
}
@@ -4843,7 +4906,9 @@
// We only need to adjust focused stack if this stack is in focus.
if (isOnHomeDisplay() && mStackSupervisor.isFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
- if (mFullscreen || !adjustFocusToNextFocusableStackLocked(myReason)) {
+ if (mFullscreen
+ || !adjustFocusToNextFocusableStackLocked(
+ task.getTaskToReturnTo(), myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7def1bd..0e6d174 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -52,6 +52,7 @@
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -101,7 +102,9 @@
import java.util.Objects;
import java.util.Set;
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
@@ -120,7 +123,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -151,6 +153,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityManagerService.NOTIFY_FORCED_RESIZABLE_MSG;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
@@ -167,6 +170,7 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
public final class ActivityStackSupervisor implements DisplayListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
@@ -423,6 +427,11 @@
boolean mAppVisibilitiesChangedSinceLastPause;
/**
+ * Set of tasks that are in resizing mode during an app transition to fill the "void".
+ */
+ private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
+
+ /**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
*/
@@ -1304,7 +1313,7 @@
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
- ActivityRecord resultRecord, ActivityStack resultStack) {
+ ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED) {
@@ -1358,6 +1367,19 @@
Slog.w(TAG, message);
return false;
}
+ if (options != null && options.getLaunchTaskId() != -1) {
+ final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
+ callingPid, callingUid);
+ if (startInTaskPerm != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with launchTaskId="
+ + options.getLaunchTaskId();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
return true;
}
@@ -1788,6 +1810,8 @@
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
+
+ showNonResizeableDockToastIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId);
}
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
@@ -1924,10 +1948,39 @@
}
}
- private void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+ void deferUpdateBounds(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack != null) {
+ stack.deferUpdateBounds();
+ }
+ }
+
+ void continueUpdateBounds(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack != null) {
+ stack.continueUpdateBounds();
+ }
+ }
+
+ void notifyAppTransitionDone() {
+ continueUpdateBounds(HOME_STACK_ID);
+ for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
+ final int taskId = mResizingTasksDuringAnimation.valueAt(i);
+ if (anyTaskForIdLocked(taskId) != null) {
+ mWindowManager.setTaskDockedResizing(taskId, false);
+ }
+ }
+ mResizingTasksDuringAnimation.clear();
+ }
+
+ void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
Rect tempTaskInsetBounds) {
bounds = TaskRecord.validateBounds(bounds);
+ if (!stack.updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
+ return;
+ }
+
mTmpBounds.clear();
mTmpConfigs.clear();
mTmpInsetBounds.clear();
@@ -3309,10 +3362,14 @@
return;
}
- if (!task.canGoInDockedStack() || task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
- // Display warning toast if we tried to put a non-dockable task in the docked stack or
- // the task was forced to be resizable by the system.
+ if (!task.canGoInDockedStack()) {
+ // Display a warning toast that we tried to put a non-dockable task in the docked stack.
mWindowManager.scheduleShowNonResizeableDockToast(task.taskId);
+ } else if (task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
+ String packageName = task.getTopActivity() != null
+ ? task.getTopActivity().appInfo.packageName : null;
+ mService.mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, task.taskId, 0,
+ packageName).sendToTarget();
}
}
@@ -4129,4 +4186,80 @@
throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+ " in=" + stacks);
}
+
+ /**
+ * Puts a task into resizing mode during the next app transition.
+ *
+ * @param taskId the id of the task to put into resizing mode
+ */
+ private void setResizingDuringAnimation(int taskId) {
+ mResizingTasksDuringAnimation.add(taskId);
+ mWindowManager.setTaskDockedResizing(taskId, true);
+ }
+
+ final int startActivityFromRecentsInner(int taskId, Bundle bOptions) {
+ final TaskRecord task;
+ final int callingUid;
+ final String callingPackage;
+ final Intent intent;
+ final int userId;
+ final ActivityOptions activityOptions = (bOptions != null)
+ ? new ActivityOptions(bOptions) : null;
+ final int launchStackId = (activityOptions != null)
+ ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
+
+ if (launchStackId == HOME_STACK_ID) {
+ throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+ + taskId + " can't be launch in the home stack.");
+ }
+ task = anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
+ if (task == null) {
+ throw new IllegalArgumentException(
+ "startActivityFromRecentsInner: Task " + taskId + " not found.");
+ }
+
+ if (launchStackId != INVALID_STACK_ID) {
+ if (launchStackId == DOCKED_STACK_ID) {
+ mWindowManager.setDockedStackCreateState(
+ activityOptions.getDockCreateMode(), null /* initialBounds */);
+
+ // Defer updating the stack in which recents is until the app transition is done, to
+ // not run into issues where we still need to draw the task in recents but the
+ // docked stack is already created.
+ deferUpdateBounds(HOME_STACK_ID);
+ mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+ }
+ if (task.stack.mStackId != launchStackId) {
+ moveTaskToStackLocked(
+ taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
+ ANIMATE);
+ }
+ }
+
+ // If the user must confirm credentials (e.g. when first launching a work app and the
+ // Work Challenge is present) let startActivityInPackage handle the intercepting.
+ if (!mService.mUserController.shouldConfirmCredentials(task.userId)
+ && task.getRootActivity() != null) {
+ mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
+
+ // If we are launching the task in the docked stack, put it into resizing mode so
+ // the window renders full-screen with the background filling the void. Also only
+ // call this at the end to make sure that tasks exists on the window manager side.
+ if (launchStackId == DOCKED_STACK_ID) {
+ setResizingDuringAnimation(taskId);
+ }
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
+ callingUid = task.mCallingUid;
+ callingPackage = task.mCallingPackage;
+ intent = task.intent;
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ userId = task.userId;
+ int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
+ null, null, 0, 0, bOptions, userId, null, task);
+ if (launchStackId == DOCKED_STACK_ID) {
+ setResizingDuringAnimation(task.taskId);
+ }
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 13d90e3..76dfd01 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -26,6 +26,7 @@
import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import android.app.KeyguardManager;
@@ -192,14 +193,14 @@
Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
new String[]{ resolvedType },
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
- final int flags = intent.getFlags();
final KeyguardManager km = (KeyguardManager) mService.mContext
.getSystemService(KEYGUARD_SERVICE);
final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
if (newIntent == null) {
return null;
}
- newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+ FLAG_ACTIVITY_TASK_ON_HOME);
newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
return newIntent;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index af69c93..e3ca3ea 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -362,7 +362,7 @@
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
- resultRecord, resultStack);
+ resultRecord, resultStack, options);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
@@ -547,12 +547,18 @@
}
if (startedActivityStackId == DOCKED_STACK_ID && prevFocusedStackId == HOME_STACK_ID) {
- // We launch an activity while being in home stack, which means either launcher or
- // recents into docked stack. We don't want the launched activity to be alone in a
- // docked stack, so we want to immediately launch recents too.
- if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
- mWindowManager.showRecentApps();
- return;
+ final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID);
+ final ActivityRecord topActivityHomeStack = homeStack != null
+ ? homeStack.topRunningActivityLocked() : null;
+ if (topActivityHomeStack == null
+ || topActivityHomeStack.mActivityType != RECENTS_ACTIVITY_TYPE) {
+ // We launch an activity while being in home stack, which means either launcher or
+ // recents into docked stack. We don't want the launched activity to be alone in a
+ // docked stack, so we want to immediately launch recents too.
+ if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+ mWindowManager.showRecentApps();
+ return;
+ }
}
if (startedActivityStackId == PINNED_STACK_ID
@@ -879,6 +885,9 @@
ActivityRecord intentActivity = getReusableIntentActivity();
+ final int preferredLaunchStackId =
+ (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
+
if (intentActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
@@ -938,6 +947,8 @@
// We didn't do anything... but it was needed (a.k.a., client don't use that
// intent!) And for paranoia, make sure we have correctly resumed the top activity.
resumeTargetStackIfNeeded();
+ mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task,
+ preferredLaunchStackId, mTargetStack.mStackId);
return START_TASK_TO_FRONT;
}
}
@@ -977,6 +988,8 @@
}
top.deliverNewIntentLocked(
mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+ mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task,
+ preferredLaunchStackId, mTargetStack.mStackId);
return START_DELIVERED_TO_TOP;
}
@@ -1063,8 +1076,6 @@
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
- final int preferredLaunchStackId =
- (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
mSupervisor.showNonResizeableDockToastIfNeeded(
mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId);
@@ -1297,7 +1308,10 @@
// same component, then instead of launching bring that one to the front.
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
- if (putIntoExistingTask) {
+ if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
+ final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+ intentActivity = task != null ? task.getTopActivity() : null;
+ } else if (putIntoExistingTask) {
// See if there is a task to bring to the front. If this is a SINGLE_INSTANCE
// activity, there can be one and only one instance of it in the history, and it is
// always in its own unique task, so we do a special search.
@@ -1349,6 +1363,15 @@
intentActivity.task, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
mMovedToFront = true;
+ } else if ((launchStack.mStackId == DOCKED_STACK_ID
+ || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID)
+ && (mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+ // If we want to launch adjacent and mTargetStack is not the computed
+ // launch stack - move task to top of computed stack.
+ mSupervisor.moveTaskToStackLocked(intentActivity.task.taskId,
+ launchStack.mStackId, ON_TOP, FORCE_FOCUS, "launchToSide",
+ ANIMATE);
+ mMovedToFront = true;
}
mOptions = null;
}
@@ -1756,26 +1779,41 @@
if (!launchToSideAllowed || (launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0) {
return null;
}
+ // Otherwise handle adjacent launch.
// The parent activity doesn't want to launch the activity on top of itself, but
// instead tries to put it onto other side in side-by-side mode.
final ActivityStack parentStack = task != null ? task.stack
: r.mInitialActivityContainer != null ? r.mInitialActivityContainer.mStack
: mSupervisor.mFocusedStack;
- if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
- // If parent was in docked stack, the natural place to launch another activity
- // will be fullscreen, so it can appear alongside the docked window.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+
+ if (parentStack != mSupervisor.mFocusedStack) {
+ // If task's parent stack is not focused - use it during adjacent launch.
+ return parentStack;
} else {
- // If the parent is not in the docked stack, we check if there is docked window
- // and if yes, we will launch into that stack. If not, we just put the new
- // activity into parent's stack, because we can't find a better place.
- final ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
- if (stack != null && stack.getStackVisibilityLocked(r) == STACK_INVISIBLE) {
- // There is a docked stack, but it isn't visible, so we can't launch into that.
- return null;
+ if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
+ // If task is already on top of focused stack - use it. We don't want to move the
+ // existing focused task to adjacent stack, just deliver new intent in this case.
+ return mSupervisor.mFocusedStack;
+ }
+
+ if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
+ // If parent was in docked stack, the natural place to launch another activity
+ // will be fullscreen, so it can appear alongside the docked window.
+ return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED,
+ ON_TOP);
} else {
- return stack;
+ // If the parent is not in the docked stack, we check if there is docked window
+ // and if yes, we will launch into that stack. If not, we just put the new
+ // activity into parent's stack, because we can't find a better place.
+ final ActivityStack dockedStack = mSupervisor.getStack(DOCKED_STACK_ID);
+ if (dockedStack != null
+ && dockedStack.getStackVisibilityLocked(r) == STACK_INVISIBLE) {
+ // There is a docked stack, but it isn't visible, so we can't launch into that.
+ return null;
+ } else {
+ return dockedStack;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 71a1f97..ffa3b5b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1170,7 +1170,8 @@
}
if (useCheckinFormat) {
- List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
+ List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ALL);
if (isRealCheckin) {
// For a real checkin, first we want to prefer to use the last complete checkin
// file if there is one.
diff --git a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
index 39c6ce6..9fb51c1 100644
--- a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
+++ b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
@@ -122,7 +122,8 @@
// Battery Stats stores the GPS sensors with a bogus key in this API. Pull it out
// as a separate metric here so as to not expose that in the API.
if (sensorId == BatteryStats.Uid.Sensor.GPS) {
- addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR, sensors.valueAt(i).getSensorTime());
+ addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR,
+ sensors.valueAt(i).getSensorTime());
} else {
addTimers(uidWriter, UidHealthStats.TIMERS_SENSORS, Integer.toString(sensorId),
sensors.valueAt(i).getSensorTime());
@@ -131,7 +132,7 @@
// STATS_PIDS
pids = uid.getPidStats();
- N = sensors.size();
+ N = pids.size();
for (int i=0; i<N; i++) {
final HealthStatsWriter writer = new HealthStatsWriter(PidHealthStats.CONSTANTS);
writePid(writer, pids.valueAt(i));
@@ -241,7 +242,8 @@
addTimer(uidWriter, UidHealthStats.TIMER_CAMERA, uid.getCameraTurnedOnTimer());
// TIMER_FOREGROUND_ACTIVITY
- addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY, uid.getForegroundActivityTimer());
+ addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY,
+ uid.getForegroundActivityTimer());
// TIMER_BLUETOOTH_SCAN
addTimer(uidWriter, UidHealthStats.TIMER_BLUETOOTH_SCAN, uid.getBluetoothScanTimer());
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b4aa4cf..5407d28 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -558,7 +558,7 @@
}
EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
Process.killProcessQuiet(pid);
- Process.killProcessGroup(info.uid, pid);
+ Process.killProcessGroup(uid, pid);
if (!persistent) {
killed = true;
killedByAm = true;
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
index f6dc9b9..7cac227 100644
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
@@ -60,7 +60,7 @@
private long mLastSentEventTimeMillis = System.currentTimeMillis();
private final void enforceConnectivityInternalPermission() {
- getContext().enforceCallingPermission(
+ getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_INTERNAL,
"MetricsLoggerService");
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b4c71c1..c5d38cb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -32,7 +32,6 @@
import com.android.internal.util.AsyncChannel;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkMonitor;
-import com.android.server.connectivity.ApfFilter;
import java.util.ArrayList;
import java.util.Comparator;
@@ -164,8 +163,6 @@
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
- public ApfFilter apfFilter;
-
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
@@ -178,7 +175,6 @@
currentScore = score;
networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
- apfFilter.maybeInstall(connService, this);
}
/**
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bce7733..bbb162e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -34,6 +34,8 @@
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.metrics.CaptivePortalCheckResultEvent;
+import android.net.metrics.CaptivePortalStateChangeEvent;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
@@ -297,9 +299,13 @@
transitionTo(mLingeringState);
return HANDLED;
case CMD_NETWORK_CONNECTED:
+ CaptivePortalStateChangeEvent.logEvent(
+ CaptivePortalStateChangeEvent.NETWORK_MONITOR_CONNECTED);
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_NETWORK_DISCONNECTED:
+ CaptivePortalStateChangeEvent.logEvent(
+ CaptivePortalStateChangeEvent.NETWORK_MONITOR_DISCONNECTED);
if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -349,6 +355,8 @@
private class ValidatedState extends State {
@Override
public void enter() {
+ CaptivePortalStateChangeEvent.logEvent(
+ CaptivePortalStateChangeEvent.NETWORK_MONITOR_VALIDATED);
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo));
}
@@ -457,6 +465,8 @@
// will be unresponsive. isCaptivePortal() could be executed on another Thread
// if this is found to cause problems.
int httpResponseCode = isCaptivePortal();
+ CaptivePortalCheckResultEvent.logEvent(mNetworkAgentInfo.network.netId,
+ httpResponseCode);
if (httpResponseCode == 204) {
transitionTo(mValidatedState);
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4527f1f..715d2d8 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -17,12 +17,12 @@
package com.android.server.display;
import android.content.res.Resources;
-import android.os.Build;
import com.android.server.LocalServices;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -31,7 +31,6 @@
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.Surface;
@@ -388,7 +387,7 @@
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
- || (Build.HARDWARE.contains("goldfish")
+ || (Build.IS_EMULATOR
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 5b1cedc..fc412e3 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -20,6 +20,7 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
+import static android.provider.Settings.ACTION_VPN_SETTINGS;
import android.app.Notification;
import android.app.NotificationManager;
@@ -66,9 +67,6 @@
private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
- private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
- private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
-
private static final int ROOT_UID = 0;
private final Context mContext;
@@ -101,7 +99,6 @@
mProfile = Preconditions.checkNotNull(profile);
final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
- configIntent.putExtra(EXTRA_PICK_LOCKDOWN, true);
mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 49ae293..9b92e4f 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2803,7 +2803,7 @@
private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
try {
- mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ mNetworkManager.setUidMeteredNetworkBlacklist(uid, rejectOnQuotaInterfaces);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting uid rules", e);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java
index 2f55562..6f781b3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java
@@ -52,7 +52,7 @@
*/
class NetworkStatsObservers {
private static final String TAG = "NetworkStatsObservers";
- private static final boolean LOGV = true;
+ private static final boolean LOGV = false;
private static final long MIN_THRESHOLD_BYTES = 2 * MB_IN_BYTES;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 0eacd13..eae2eaa 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -24,7 +24,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.util.ArraySet;
import android.util.Log;
@@ -51,8 +50,6 @@
final AtomicBoolean mIdleTime = new AtomicBoolean(false);
- private boolean useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
-
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
@@ -93,8 +90,8 @@
// skip previously failing package
continue;
}
- if (!pm.performDexOpt(pkg, /* instruction set */ null, useJitProfiles,
- /* extractOnly */ false, /* force */ false)) {
+ if (!pm.performDexOpt(pkg, /* instruction set */ null, /* checkProfiles */ true,
+ PackageManagerService.REASON_BACKGROUND_DEXOPT, /* force */ false)) {
// there was a problem running dexopt,
// remember this so we do not keep retrying.
sFailedPackageNames.add(pkg);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 206a143..7e25632 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.Build;
-import android.os.storage.StorageManager;
import android.util.Slog;
import com.android.internal.os.InstallerConnection;
@@ -37,17 +36,17 @@
* frameworks/native/cmds/installd/installd.h
* **************************************************************************/
/** Application should be visible to everyone */
- public static final int DEXOPT_PUBLIC = 1 << 1;
+ public static final int DEXOPT_PUBLIC = 1 << 1;
/** Application wants to run in VM safe mode */
- public static final int DEXOPT_SAFEMODE = 1 << 2;
+ public static final int DEXOPT_SAFEMODE = 1 << 2;
/** Application wants to allow debugging of its code */
- public static final int DEXOPT_DEBUGGABLE = 1 << 3;
+ public static final int DEXOPT_DEBUGGABLE = 1 << 3;
/** The system boot has finished */
- public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
- /** Do not compile, only extract bytecode into an OAT file */
- public static final int DEXOPT_EXTRACTONLY = 1 << 5;
+ public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
+ /** Hint that the dexopt type is profile-guided. */
+ public static final int DEXOPT_PROFILE_GUIDED = 1 << 5;
/** This is an OTA update dexopt */
- public static final int DEXOPT_OTA = 1 << 6;
+ public static final int DEXOPT_OTA = 1 << 6;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
@@ -137,19 +136,23 @@
}
public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
- int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException {
+ int dexFlags, String compilerFilter, String volumeUuid) throws InstallerException {
assertValidInstructionSet(instructionSet);
mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags,
- volumeUuid, useProfiles);
+ compilerFilter, volumeUuid);
}
public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
- String volumeUuid, boolean useProfiles)
+ String compilerFilter, String volumeUuid)
throws InstallerException {
assertValidInstructionSet(instructionSet);
mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
- outputPath, dexFlags, volumeUuid, useProfiles);
+ outputPath, dexFlags, compilerFilter, volumeUuid);
+ }
+
+ public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+ return mInstaller.mergeProfiles(uid, pkgName);
}
public void idmap(String targetApkPath, String overlayApkPath, int uid)
@@ -166,8 +169,12 @@
mInstaller.execute("rmpackagedir", packageDir);
}
- public void rmProfiles(String pkgName) throws InstallerException {
- mInstaller.execute("rmprofiles", pkgName);
+ public void clearAppProfiles(String pkgName) throws InstallerException {
+ mInstaller.execute("clear_app_profiles", pkgName);
+ }
+
+ public void destroyAppProfiles(String pkgName) throws InstallerException {
+ mInstaller.execute("destroy_app_profiles", pkgName);
}
public void createUserConfig(int userid) throws InstallerException {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 2f0532a..c303ceb 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -42,6 +42,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
@@ -54,6 +55,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -102,6 +104,8 @@
private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ private final Handler mCallbackHandler;
+
public LauncherAppsImpl(Context context) {
mContext = context;
mPm = mContext.getPackageManager();
@@ -109,6 +113,7 @@
mShortcutServiceInternal = Preconditions.checkNotNull(
LocalServices.getService(ShortcutServiceInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
+ mCallbackHandler = BackgroundThread.getHandler();
}
@VisibleForTesting
@@ -165,7 +170,7 @@
* Register a receiver to watch for package broadcasts
*/
private void startWatchingPackageBroadcasts() {
- mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+ mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
}
/**
@@ -550,8 +555,9 @@
}
}
+ @VisibleForTesting
void postToPackageMonitorHandler(Runnable r) {
- mPackageMonitor.getRegisteredHandler().post(r);
+ mCallbackHandler.post(r);
}
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 67aeed1..c3a9226 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -19,11 +19,11 @@
import static com.android.server.pm.Installer.DEXOPT_OTA;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import android.content.Context;
import android.content.pm.IOtaDexopt;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -130,6 +130,7 @@
// TODO: If apps are not installed in the internal /data partition, we should compare
// against that storage's free capacity.
File dataDir = Environment.getDataDirectory();
+ @SuppressWarnings("deprecation")
long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
if (lowThreshold == 0) {
throw new IllegalStateException("Invalid low memory threshold");
@@ -142,7 +143,7 @@
}
mPackageDexOptimizer.performDexOpt(nextPackage, null /* ISAs */, false /* useProfiles */,
- false /* extractOnly */);
+ getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
}
private void moveAbArtifacts(Installer installer) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 561682c..5ceb65f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -20,13 +20,10 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
import android.os.Environment;
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.os.storage.StorageManager;
-import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -34,18 +31,18 @@
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
import dalvik.system.DexFile;
import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
-import static com.android.server.pm.Installer.DEXOPT_EXTRACTONLY;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
/**
* Helper class for running dexopt command on packages.
@@ -59,8 +56,6 @@
static final int DEX_OPT_DEFERRED = 2;
static final int DEX_OPT_FAILED = -1;
- private static final boolean DEBUG_DEXOPT = PackageManagerService.DEBUG_DEXOPT;
-
private final Installer mInstaller;
private final Object mInstallLock;
@@ -94,8 +89,8 @@
* <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
* synchronized on {@link #mInstallLock}.
*/
- int performDexOpt(PackageParser.Package pkg, String[] instructionSets, boolean useProfiles,
- boolean extractOnly) {
+ int performDexOpt(PackageParser.Package pkg, String[] instructionSets, boolean checkProfiles,
+ String targetCompilationFilter) {
synchronized (mInstallLock) {
final boolean useLock = mSystemReady;
if (useLock) {
@@ -103,7 +98,8 @@
mDexoptWakeLock.acquire();
}
try {
- return performDexOptLI(pkg, instructionSets, useProfiles, extractOnly);
+ return performDexOptLI(pkg, instructionSets, checkProfiles,
+ targetCompilationFilter);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -128,7 +124,7 @@
}
private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
- boolean useProfiles, boolean extractOnly) {
+ boolean checkProfiles, String targetCompilerFilter) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
@@ -136,36 +132,51 @@
return DEX_OPT_SKIPPED;
}
+ final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+
+ boolean isProfileGuidedFilter = DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter);
+ // If any part of the app is used by other apps, we cannot use profile-guided
+ // compilation.
+ // TODO: This needs to be refactored to be also checked when the target mode is
+ // profile-guided.
+ if (isProfileGuidedFilter) {
+ for (String path : paths) {
+ if (isUsedByOtherApps(path)) {
+ checkProfiles = false;
+
+ // TODO: Should we only upgrade to the non-profile-guided version? That is,
+ // given verify-profile, should we move to interpret-only?
+ targetCompilerFilter = getFullCompilerFilter();
+ isProfileGuidedFilter = false;
+
+ break;
+ }
+ }
+ }
+
+ // If we're asked to take profile updates into account, check now.
+ boolean newProfile = false;
+ if (checkProfiles && isProfileGuidedFilter) {
+ // Merge profiles, see if we need to do anything.
+ try {
+ newProfile = mInstaller.mergeProfiles(sharedGid, pkg.packageName);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to merge profiles", e);
+ }
+ }
+
final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
boolean performedDexOpt = false;
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (String path : paths) {
- if (useProfiles && isUsedByOtherApps(path)) {
- // We cannot use profile guided compilation if the apk was used by another app.
- useProfiles = false;
- }
int dexoptNeeded;
-
try {
- int compilationTypeMask = 0;
- if (extractOnly) {
- // For extract only, any type of compilation is good.
- compilationTypeMask = DexFile.COMPILATION_TYPE_FULL
- | DexFile.COMPILATION_TYPE_PROFILE_GUIDE
- | DexFile.COMPILATION_TYPE_EXTRACT_ONLY;
- } else {
- // Branch taken for profile guide and full compilation.
- // Profile guide compilation should only recompile a previous
- // profile compiled/extract only file and should not be attempted if the
- // apk is already fully compiled. So test against a full compilation type.
- compilationTypeMask = DexFile.COMPILATION_TYPE_FULL;
- }
dexoptNeeded = DexFile.getDexOptNeeded(path,
- dexCodeInstructionSet, compilationTypeMask);
+ dexCodeInstructionSet, targetCompilerFilter, newProfile);
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;
@@ -194,20 +205,20 @@
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
- + " extractOnly=" + extractOnly + " oatDir = " + oatDir);
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ + " target-filter=" + targetCompilerFilter + " oatDir = " + oatDir);
// Profile guide compiled oat files should not be public.
- final boolean isPublic = !pkg.isForwardLocked() && !useProfiles;
+ final boolean isPublic = !pkg.isForwardLocked() && !isProfileGuidedFilter;
+ final int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
final int dexFlags = adjustDexoptFlags(
( isPublic ? DEXOPT_PUBLIC : 0)
| (vmSafeMode ? DEXOPT_SAFEMODE : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
- | (extractOnly ? DEXOPT_EXTRACTONLY : 0)
+ | profileFlag
| DEXOPT_BOOTCOMPLETE);
try {
mInstaller.dexopt(path, sharedGid, pkg.packageName, dexCodeInstructionSet,
- dexoptNeeded, oatDir, dexFlags, pkg.volumeUuid, useProfiles);
+ dexoptNeeded, oatDir, dexFlags, targetCompilerFilter, pkg.volumeUuid);
performedDexOpt = true;
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9335116..64f8c98 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,8 @@
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
@@ -441,6 +443,17 @@
sBrowserIntent.setData(Uri.parse("http:"));
}
+ // Compilation reasons.
+ public static final int REASON_BOOT = 0;
+ public static final int REASON_INSTALL = 1;
+ public static final int REASON_BACKGROUND_DEXOPT = 2;
+ public static final int REASON_AB_OTA = 3;
+ public static final int REASON_NON_SYSTEM_LIBRARY = 4;
+ public static final int REASON_SHARED_APK = 5;
+ public static final int REASON_FORCED_DEXOPT = 6;
+
+ public static final int REASON_LAST = REASON_FORCED_DEXOPT;
+
final ServiceThread mHandlerThread;
final PackageHandler mHandler;
@@ -1956,6 +1969,9 @@
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
+ // Self-check for initial settings.
+ PackageManagerServiceCompilerMapping.checkProperties();
+
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
@@ -2168,12 +2184,13 @@
// AOT compilation (if needed).
int dexoptNeeded = DexFile.getDexOptNeeded(
lib, dexCodeInstructionSet,
- DexFile.COMPILATION_TYPE_FULL);
+ getCompilerFilterForReason(REASON_SHARED_APK),
+ false /* newProfile */);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
- StorageManager.UUID_PRIVATE_INTERNAL,
- false /*useProfiles*/);
+ getCompilerFilterForReason(REASON_SHARED_APK),
+ StorageManager.UUID_PRIVATE_INTERNAL);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
@@ -6928,7 +6945,7 @@
// and would have to be patched (would be SELF_PATCHOAT, which is deprecated).
// Instead, force the extraction in this case.
performDexOpt(pkg.packageName, null /* instructionSet */,
- false /* useProfiles */, true /* extractOnly */, prunedCache);
+ false /* checkProfiles */, REASON_BOOT, prunedCache);
}
}
}
@@ -6947,29 +6964,37 @@
// TODO: this is not used nor needed. Delete it.
@Override
public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- return performDexOptTraced(packageName, instructionSet, false /* useProfiles */,
- false /* extractOnly */, false /* force */);
+ return performDexOptTraced(packageName, instructionSet, false /* checkProfiles */,
+ getFullCompilerFilter(), false /* force */);
}
@Override
- public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles,
- boolean extractOnly, boolean force) {
- return performDexOptTraced(packageName, instructionSet, useProfiles, extractOnly, force);
+ public boolean performDexOpt(String packageName, String instructionSet,
+ boolean checkProfiles, int compileReason, boolean force) {
+ return performDexOptTraced(packageName, instructionSet, checkProfiles,
+ getCompilerFilterForReason(compileReason), force);
+ }
+
+ @Override
+ public boolean performDexOptMode(String packageName, String instructionSet,
+ boolean checkProfiles, String targetCompilerFilter, boolean force) {
+ return performDexOptTraced(packageName, instructionSet, checkProfiles,
+ targetCompilerFilter, force);
}
private boolean performDexOptTraced(String packageName, String instructionSet,
- boolean useProfiles, boolean extractOnly, boolean force) {
+ boolean checkProfiles, String targetCompilerFilter, boolean force) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
- return performDexOptInternal(packageName, instructionSet, useProfiles, extractOnly,
- force);
+ return performDexOptInternal(packageName, instructionSet, checkProfiles,
+ targetCompilerFilter, force);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private boolean performDexOptInternal(String packageName, String instructionSet,
- boolean useProfiles, boolean extractOnly, boolean force) {
+ boolean checkProfiles, String targetCompilerFilter, boolean force) {
PackageParser.Package p;
final String targetInstructionSet;
synchronized (mPackages) {
@@ -6987,7 +7012,7 @@
synchronized (mInstallLock) {
final String[] instructionSets = new String[] { targetInstructionSet };
int result = performDexOptInternalWithDependenciesLI(p, instructionSets,
- useProfiles, extractOnly, force);
+ checkProfiles, targetCompilerFilter, force);
return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
}
} finally {
@@ -7008,7 +7033,8 @@
}
private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
- String instructionSets[], boolean useProfiles, boolean extractOnly, boolean force) {
+ String instructionSets[], boolean checkProfiles, String targetCompilerFilter,
+ boolean force) {
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
@@ -7022,13 +7048,13 @@
if (!deps.isEmpty()) {
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
- // Currently this will do a full compilation of the library.
- pdo.performDexOpt(depPackage, instructionSets, false /* useProfiles */,
- false /* extractOnly */);
+ // Currently this will do a full compilation of the library by default.
+ pdo.performDexOpt(depPackage, instructionSets, false /* checkProfiles */,
+ getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY));
}
}
- return pdo.performDexOpt(p, instructionSets, useProfiles, extractOnly);
+ return pdo.performDexOpt(p, instructionSets, checkProfiles, targetCompilerFilter);
}
Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
@@ -7106,7 +7132,8 @@
// Whoever is calling forceDexOpt wants a fully compiled package.
// Don't use profiles since that may cause compilation to be skipped.
final int res = performDexOptInternalWithDependenciesLI(pkg, instructionSets,
- false /* useProfiles */, false /* extractOnly */, true /* force */);
+ false /* checkProfiles */, getCompilerFilterForReason(REASON_FORCED_DEXOPT),
+ true /* force */);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -7176,6 +7203,30 @@
}
}
+ private void deleteProfilesLI(String packageName, boolean destroy) {
+ final PackageParser.Package pkg;
+ synchronized (mPackages) {
+ pkg = mPackages.get(packageName);
+ }
+ if (pkg == null) {
+ Slog.w(TAG, "Failed to delete profiles. No package: " + packageName);
+ return;
+ }
+ deleteProfilesLI(pkg, destroy);
+ }
+
+ private void deleteProfilesLI(PackageParser.Package pkg, boolean destroy) {
+ try {
+ if (destroy) {
+ mInstaller.clearAppProfiles(pkg.packageName);
+ } else {
+ mInstaller.destroyAppProfiles(pkg.packageName);
+ }
+ } catch (InstallerException ex) {
+ Log.e(TAG, "Could not delete profiles for package " + pkg.packageName);
+ }
+ }
+
private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
final PackageParser.Package pkg;
synchronized (mPackages) {
@@ -13208,6 +13259,7 @@
}
deleteCodeCacheDirsLI(pkg);
+ deleteProfilesLI(pkg, /*destroy*/ false);
try {
final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
@@ -13342,6 +13394,7 @@
// Successfully disabled the old package. Now proceed with re-installation
deleteCodeCacheDirsLI(pkg);
+ deleteProfilesLI(pkg, /*destroy*/ false);
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
@@ -13973,7 +14026,7 @@
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` is not in `mPackages` yet.
int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instructionSets */,
- false /* useProfiles */, true /* extractOnly */);
+ false /* checkProfiles */, getCompilerFilterForReason(REASON_INSTALL));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
String msg = "Extracking package failed for " + pkgName;
@@ -14414,6 +14467,7 @@
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
res = deletePackageLI(packageName, removeForUser, true, allUsers,
flags | REMOVE_CHATTY, info, true, null);
+ deleteProfilesLI(packageName, /*destroy*/ true);
synchronized (mPackages) {
if (res) {
mEphemeralApplicationRegistry.onPackageUninstalledLPw(uninstalledPs.pkg);
@@ -15181,7 +15235,7 @@
public void clearApplicationProfileData(String packageName) {
enforceSystemOrRoot("Only the system can clear all profile data");
try {
- mInstaller.rmProfiles(packageName);
+ mInstaller.clearAppProfiles(packageName);
} catch (InstallerException ex) {
Log.e(TAG, "Could not clear profile data of package " + packageName);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
new file mode 100644
index 0000000..238e410
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -0,0 +1,128 @@
+/*
+ * 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.server.pm;
+
+import android.os.SystemProperties;
+
+import dalvik.system.DexFile;
+
+/**
+ * Manage (retrieve) mappings from compilation reason to compilation filter.
+ */
+class PackageManagerServiceCompilerMapping {
+ // Names for compilation reasons.
+ static final String REASON_STRINGS[] = {
+ "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk", "forced-dexopt"
+ };
+
+ // Static block to ensure the strings array is of the right length.
+ static {
+ if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
+ throw new IllegalStateException("REASON_STRINGS not correct");
+ }
+ }
+
+ private static String getSystemPropertyName(int reason) {
+ if (reason < 0 || reason >= REASON_STRINGS.length) {
+ throw new IllegalArgumentException("reason " + reason + " invalid");
+ }
+
+ return "pm.dexopt." + REASON_STRINGS[reason];
+ }
+
+ // Load the property for the given reason and check for validity. This will throw an
+ // exception in case the reason or value are invalid.
+ private static String getAndCheckValidity(int reason) {
+ String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
+ if (sysPropValue == null || sysPropValue.isEmpty() ||
+ !DexFile.isValidCompilerFilter(sysPropValue)) {
+ throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ + "(reason " + REASON_STRINGS[reason] + ")");
+ }
+
+ // Ensure that some reasons are not mapped to profile-guided filters.
+ switch (reason) {
+ case PackageManagerService.REASON_SHARED_APK:
+ case PackageManagerService.REASON_FORCED_DEXOPT:
+ if (DexFile.isProfileGuidedCompilerFilter(sysPropValue)) {
+ throw new IllegalStateException("\"" + sysPropValue + "\" is profile-guided, "
+ + "but not allowed for " + REASON_STRINGS[reason]);
+ }
+ break;
+ }
+
+ return sysPropValue;
+ }
+
+ // Check that the properties are set and valid.
+ // Note: this is done in a separate method so this class can be statically initialized.
+ static void checkProperties() {
+ // We're gonna check all properties and collect the exceptions, so we can give a general
+ // overview. Store the exceptions here.
+ RuntimeException toThrow = null;
+
+ for (int reason = 0; reason <= PackageManagerService.REASON_LAST; reason++) {
+ try {
+ // Check that the system property name is legal.
+ String sysPropName = getSystemPropertyName(reason);
+ if (sysPropName == null ||
+ sysPropName.isEmpty() ||
+ sysPropName.length() > SystemProperties.PROP_NAME_MAX) {
+ throw new IllegalStateException("Reason system property name \"" +
+ sysPropName +"\" for reason " + REASON_STRINGS[reason]);
+ }
+
+ // Check validity, ignore result.
+ getAndCheckValidity(reason);
+ } catch (Exception exc) {
+ if (toThrow == null) {
+ toThrow = new IllegalStateException("PMS compiler filter settings are bad.");
+ }
+ toThrow.addSuppressed(exc);
+ }
+ }
+
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ }
+
+ public static String getCompilerFilterForReason(int reason) {
+ return getAndCheckValidity(reason);
+ }
+
+ /**
+ * Return the compiler filter for "full" compilation.
+ *
+ * We derive that from the traditional "dalvik.vm.dex2oat-filter" property and just make
+ * sure this isn't profile-guided. Returns "speed" in case of invalid (or missing) values.
+ */
+ public static String getFullCompilerFilter() {
+ String value = SystemProperties.get("dalvik.vm.dex2oat-filter");
+ if (value == null || value.isEmpty()) {
+ return "speed";
+ }
+
+ if (!DexFile.isValidCompilerFilter(value) ||
+ DexFile.isProfileGuidedCompilerFilter(value)) {
+ return "speed";
+ }
+
+ return value;
+ }
+
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 319fc37..bf44b0f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -44,12 +44,13 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ShellCommand;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.PrintWriterPrinter;
import com.android.internal.util.SizedInputStream;
+import dalvik.system.DexFile;
+
import libcore.io.IoUtils;
import java.io.File;
@@ -249,11 +250,38 @@
private int runCompile() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
boolean useJitProfiles = false;
- boolean extractOnly = false;
boolean forceCompilation = false;
boolean allPackages = false;
boolean clearProfileData = false;
- String compilationMode = "default";
+ String compilerFilter = null;
+ String compilationReason = null;
+
+ if (peekNextArg() == null) {
+ // No arguments, show help.
+ pw.println("Usage: cmd package compile [-c] [-f] [--reset] [-m mode] " +
+ "[-r reason] [-a|pkg]");
+ pw.println();
+ pw.println(" -c Clear profile data");
+ pw.println(" -f Force compilation");
+ pw.println(" --reset Reset package");
+ pw.println(" -m mode Compilation mode, one of the dex2oat compiler filters");
+ pw.println(" verify-none");
+ pw.println(" verify-at-runtime");
+ pw.println(" verify-profile");
+ pw.println(" interpret-only");
+ pw.println(" space-profile");
+ pw.println(" space");
+ pw.println(" speed-profile");
+ pw.println(" speed");
+ pw.println(" everything");
+ pw.println(" -r reason Compiler reason, one of the package manager reasons");
+ for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
+ pw.println(" " +
+ PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
+ }
+ pw.println(" -a Apply to all packages");
+ return 1;
+ }
String opt;
while ((opt = getNextOption()) != null) {
@@ -268,12 +296,15 @@
forceCompilation = true;
break;
case "-m":
- compilationMode = getNextArgRequired();
+ compilerFilter = getNextArgRequired();
+ break;
+ case "-r":
+ compilationReason = getNextArgRequired();
break;
case "--reset":
forceCompilation = true;
clearProfileData = true;
- compilationMode = "extract";
+ compilerFilter = "reset";
break;
default:
pw.println("Error: Unknown option: " + opt);
@@ -281,27 +312,55 @@
}
}
- switch (compilationMode) {
- case "default":
- useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
- extractOnly = false;
- break;
- case "full":
- useJitProfiles = false;
- extractOnly = false;
- break;
- case "profile":
- useJitProfiles = true;
- extractOnly = false;
- break;
- case "extract":
- useJitProfiles = false;
- extractOnly = true;
- break;
- default:
- pw.println("Error: Unknown compilation mode: " + compilationMode);
- return 1;
+ if (compilerFilter != null && compilationReason != null) {
+ pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " +
+ "at the same time");
+ return 1;
}
+ if (compilerFilter == null && compilationReason == null) {
+ pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " +
+ "reason (\"-r\") at the same time");
+ return 1;
+ }
+
+ String targetCompilerFilter;
+ if (compilerFilter != null) {
+ // Specially recognize default and reset. Otherwise, only accept valid modes.
+ if ("default".equals(compilerFilter)) {
+ // Use the default mode for background dexopt.
+ targetCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_BACKGROUND_DEXOPT);
+ } else if ("reset".equals(compilerFilter)) {
+ // Use the default mode for install.
+ targetCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_INSTALL);
+ } else {
+ if (!DexFile.isValidCompilerFilter(compilerFilter)) {
+ pw.println("Error: \"" + compilerFilter +
+ "\" is not a valid compilation filter.");
+ return 1;
+ }
+ targetCompilerFilter = compilerFilter;
+ }
+ } else {
+ int reason = -1;
+ for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
+ if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals(
+ compilationReason)) {
+ reason = i;
+ break;
+ }
+ }
+ if (reason == -1) {
+ pw.println("Error: Unknown compilation reason: " + compilationReason);
+ return 1;
+ }
+ targetCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason);
+ }
+
List<String> packageNames = null;
if (allPackages) {
@@ -321,8 +380,8 @@
mInterface.clearApplicationProfileData(packageName);
}
- boolean result = mInterface.performDexOpt(packageName, null /* instructionSet */,
- useJitProfiles, extractOnly, forceCompilation);
+ boolean result = mInterface.performDexOptMode(packageName, null /* instructionSet */,
+ useJitProfiles, targetCompilerFilter, forceCompilation);
if (!result) {
failedPackages.add(packageName);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e66ec3c..fa0eb46 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4456,7 +4456,8 @@
}
}
- if ((permissionNames != null || dumpAll) && ps.pkg.requestedPermissions != null
+ if ((permissionNames != null || dumpAll) && ps.pkg != null
+ && ps.pkg.requestedPermissions != null
&& ps.pkg.requestedPermissions.size() > 0) {
final ArrayList<String> perms = ps.pkg.requestedPermissions;
pw.print(prefix); pw.println(" requested permissions:");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 715f1e5..ac19e24 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -458,7 +458,7 @@
continue;
}
if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
- users.add(ui);
+ users.add(userWithName(ui));
}
}
return users;
@@ -500,7 +500,7 @@
if (mRemovingUserIds.get(profile.id)) {
continue;
}
- users.add(profile);
+ users.add(userWithName(profile));
}
return users;
}
@@ -653,7 +653,21 @@
public UserInfo getUserInfo(int userId) {
checkManageUsersPermission("query user");
synchronized (mUsersLock) {
- return getUserInfoLU(userId);
+ return userWithName(getUserInfoLU(userId));
+ }
+ }
+
+ /**
+ * Returns a UserInfo object with the name filled in, for Owner, or the original
+ * if the name is already set.
+ */
+ private UserInfo userWithName(UserInfo orig) {
+ if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) {
+ UserInfo withName = new UserInfo(orig);
+ withName.name = getOwnerName();
+ return withName;
+ } else {
+ return orig;
}
}
@@ -1459,9 +1473,7 @@
flags |= UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY;
}
// Create the system user
- UserInfo system = new UserInfo(UserHandle.USER_SYSTEM,
- mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
- flags);
+ UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags);
UserData userData = new UserData();
userData.info = system;
synchronized (mUsersLock) {
@@ -1482,6 +1494,10 @@
writeUserLP(userData);
}
+ private String getOwnerName() {
+ return mContext.getResources().getString(com.android.internal.R.string.owner_name);
+ }
+
private void scheduleWriteUser(UserData UserData) {
if (DBG) {
debug("scheduleWriteUser");
@@ -1551,9 +1567,11 @@
serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType);
}
}
- serializer.startTag(null, TAG_NAME);
- serializer.text(userInfo.name);
- serializer.endTag(null, TAG_NAME);
+ if (userInfo.name != null) {
+ serializer.startTag(null, TAG_NAME);
+ serializer.text(userInfo.name);
+ serializer.endTag(null, TAG_NAME);
+ }
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.writeRestrictions(serializer,
mBaseUserRestrictions.get(userInfo.id), TAG_RESTRICTIONS);
diff --git a/services/core/java/com/android/server/policy/EnableAccessibilityController.java b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
index da9c001..6b203a9 100644
--- a/services/core/java/com/android/server/policy/EnableAccessibilityController.java
+++ b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
@@ -16,7 +16,9 @@
package com.android.server.policy;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -32,19 +34,25 @@
import android.os.UserManager;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
+import android.util.Log;
import android.util.MathUtils;
import android.view.IWindowManager;
import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import com.android.internal.R;
+import com.android.server.LocalServices;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class EnableAccessibilityController {
+ private static final String TAG = "EnableAccessibilityController";
private static final int SPEAK_WARNING_DELAY_MILLIS = 2000;
private static final int ENABLE_ACCESSIBILITY_DELAY_MILLIS = 6000;
@@ -75,9 +83,6 @@
}
};
- private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
- ServiceManager.getService("window"));
-
private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager
.Stub.asInterface(ServiceManager.getService("accessibility"));
@@ -132,7 +137,7 @@
&& !getInstalledSpeakingAccessibilityServices(context).isEmpty();
}
- private static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
+ public static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
Context context) {
List<AccessibilityServiceInfo> services = new ArrayList<AccessibilityServiceInfo>();
services.addAll(AccessibilityManager.getInstance(context)
@@ -213,71 +218,74 @@
}
private void enableAccessibility() {
- List<AccessibilityServiceInfo> services = getInstalledSpeakingAccessibilityServices(
- mContext);
- if (services.isEmpty()) {
+ if (enableAccessibility(mContext)) {
+ mOnAccessibilityEnabledCallback.run();
+ }
+ }
+
+ public static boolean enableAccessibility(Context context) {
+ final IAccessibilityManager accessibilityManager = IAccessibilityManager
+ .Stub.asInterface(ServiceManager.getService("accessibility"));
+ final WindowManagerInternal windowManager = LocalServices.getService(
+ WindowManagerInternal.class);
+ final UserManager userManager = (UserManager) context.getSystemService(
+ Context.USER_SERVICE);
+ ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
+ if (componentName == null) {
+ return false;
+ }
+
+ boolean keyguardLocked = windowManager.isKeyguardLocked();
+ final boolean hasMoreThanOneUser = userManager.getUsers().size() > 1;
+ try {
+ if (!keyguardLocked || !hasMoreThanOneUser) {
+ final int userId = ActivityManager.getCurrentUser();
+ accessibilityManager.enableAccessibilityService(componentName, userId);
+ } else if (keyguardLocked) {
+ accessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
+ componentName, true /* enableTouchExploration */);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot enable accessibilty: " + e);
+ }
+
+ return true;
+ }
+
+ public static void disableAccessibility(Context context) {
+ final IAccessibilityManager accessibilityManager = IAccessibilityManager
+ .Stub.asInterface(ServiceManager.getService("accessibility"));
+ ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
+ if (componentName == null) {
return;
}
- boolean keyguardLocked = false;
+
+ final int userId = ActivityManager.getCurrentUser();
try {
- keyguardLocked = mWindowManager.isKeyguardLocked();
- } catch (RemoteException re) {
- /* ignore */
+ accessibilityManager.disableAccessibilityService(componentName, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot disable accessibility " + e);
+ }
+ }
+
+ public static boolean isAccessibilityEnabled(Context context) {
+ final AccessibilityManager accessibilityManager =
+ context.getSystemService(AccessibilityManager.class);
+ List enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_SPOKEN);
+ return enabledServices != null && !enabledServices.isEmpty();
+ }
+
+ @Nullable
+ public static ComponentName getInstalledSpeakingAccessibilityServiceComponent(
+ Context context) {
+ List<AccessibilityServiceInfo> services =
+ getInstalledSpeakingAccessibilityServices(context);
+ if (services.isEmpty()) {
+ return null;
}
- final boolean hasMoreThanOneUser = mUserManager.getUsers().size() > 1;
-
- AccessibilityServiceInfo service = services.get(0);
- boolean enableTouchExploration = (service.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
- // Try to find a service supporting explore by touch.
- if (!enableTouchExploration) {
- final int serviceCount = services.size();
- for (int i = 1; i < serviceCount; i++) {
- AccessibilityServiceInfo candidate = services.get(i);
- if ((candidate.flags & AccessibilityServiceInfo
- .FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0) {
- enableTouchExploration = true;
- service = candidate;
- break;
- }
- }
- }
-
- ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
- ComponentName componentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!keyguardLocked || !hasMoreThanOneUser) {
- final int userId = ActivityManager.getCurrentUser();
- String enabledServiceString = componentName.flattenToString();
- ContentResolver resolver = mContext.getContentResolver();
- // Enable one speaking accessibility service.
- Settings.Secure.putStringForUser(resolver,
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- enabledServiceString, userId);
- // Allow the services we just enabled to toggle touch exploration.
- Settings.Secure.putStringForUser(resolver,
- Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- enabledServiceString, userId);
- // Enable touch exploration.
- if (enableTouchExploration) {
- Settings.Secure.putIntForUser(resolver, Settings.Secure.TOUCH_EXPLORATION_ENABLED,
- 1, userId);
- }
- // Enable accessibility script injection (AndroidVox) for web content.
- Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
- 1, userId);
- // Turn on accessibility mode last.
- Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_ENABLED,
- 1, userId);
- } else if (keyguardLocked) {
- try {
- mAccessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
- componentName, enableTouchExploration);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
-
- mOnAccessibilityEnabledCallback.run();
+ ServiceInfo serviceInfo = services.get(0).getResolveInfo().serviceInfo;
+ return new ComponentName(serviceInfo.packageName, serviceInfo.name);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0115a08..1645366 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2778,8 +2778,10 @@
// If the divider is behind the navigation bar, don't animate.
if (mNavigationBar != null
- && (win.getFrameLw().top + insets >= mNavigationBar.getFrameLw().top
- || win.getFrameLw().left + insets >= mNavigationBar.getFrameLw().left)) {
+ && ((mNavigationBarOnBottom
+ && win.getFrameLw().top + insets >= mNavigationBar.getFrameLw().top)
+ || (!mNavigationBarOnBottom
+ && win.getFrameLw().left + insets >= mNavigationBar.getFrameLw().left))) {
return 0;
}
if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
@@ -3101,7 +3103,7 @@
} else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
if (down) {
if (repeatCount == 0) {
- toggleKeyboardShortcutsMenu();
+ toggleKeyboardShortcutsMenu(event.getDeviceId());
}
}
} else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
@@ -3576,11 +3578,11 @@
}
}
- private void toggleKeyboardShortcutsMenu() {
+ private void toggleKeyboardShortcutsMenu(int deviceId) {
try {
IStatusBarService statusbar = getStatusBarService();
if (statusbar != null) {
- statusbar.toggleKeyboardShortcutsMenu();
+ statusbar.toggleKeyboardShortcutsMenu(deviceId);
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when showing keyboard shortcuts menu", e);
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
index 9d353c6..86d0468 100644
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ b/services/core/java/com/android/server/policy/StatusBarController.java
@@ -29,6 +29,8 @@
import android.view.animation.TranslateAnimation;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import static android.view.WindowManagerInternal.*;
@@ -103,6 +105,20 @@
}
});
}
+
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ StatusBarManagerInternal statusbar = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ if (statusbar != null) {
+ statusbar.appTransitionFinished();
+ }
+ }
+ });
+ }
};
public StatusBarController() {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 6bda4ed..9614417 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -34,4 +34,5 @@
void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
Rect fullscreenBounds, Rect dockedBounds, String cause);
void toggleSplitScreen();
+ void appTransitionFinished();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d24e1af..4a00ebd 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -214,6 +214,15 @@
} catch (RemoteException ex) {}
}
}
+
+ public void appTransitionFinished() {
+ enforceStatusBarService();
+ if (mBar != null) {
+ try {
+ mBar.appTransitionFinished();
+ } catch (RemoteException ex) {}
+ }
+ }
};
// ================================================================================
@@ -564,10 +573,10 @@
}
@Override
- public void toggleKeyboardShortcutsMenu() {
+ public void toggleKeyboardShortcutsMenu(int deviceId) {
if (mBar != null) {
try {
- mBar.toggleKeyboardShortcutsMenu();
+ mBar.toggleKeyboardShortcutsMenu(deviceId);
} catch (RemoteException ex) {}
}
}
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
index ad8acef0..0f251fd 100644
--- a/services/core/java/com/android/server/utils/ManagedApplicationService.java
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -62,8 +62,6 @@
private IInterface mBoundInterface;
private PendingEvent mPendingEvent;
-
-
private ManagedApplicationService(final Context context, final ComponentName component,
final int userId, int clientLabel, String settingsAction,
BinderChecker binderChecker) {
@@ -211,6 +209,7 @@
} else {
// Service connection wasn't pending, must have been disconnected
mContext.unbindService(this);
+ return;
}
try {
@@ -242,6 +241,8 @@
@Override
public void onServiceDisconnected(ComponentName componentName) {
Slog.w(TAG, "Service disconnected: " + intent);
+ mConnection = null;
+ mBoundInterface = null;
}
};
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 8316efa..93bb9d7 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -38,6 +38,17 @@
public abstract boolean isInVrMode();
/**
+ * Return {@code true} if the given package is the currently bound VrListenerService for the
+ * given user.
+ *
+ * @param packageName The package name to check.
+ * @param userId the user ID to check the package name for.
+ *
+ * @return {@code true} if the given package is the currently bound VrListenerService.
+ */
+ public abstract boolean isCurrentVrListener(String packageName, int userId);
+
+ /**
* Set the current VR mode state.
*
* @param enabled {@code true} to enable VR mode.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 6bf949c..aa6f59e 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -127,6 +127,11 @@
}
@Override
+ public boolean isCurrentVrListener(String packageName, int userId) {
+ return VrManagerService.this.isCurrentVrListener(packageName, userId);
+ }
+
+ @Override
public void registerListener(VrStateListener listener) {
VrManagerService.this.addListener(listener);
}
@@ -355,6 +360,16 @@
}
}
+ private boolean isCurrentVrListener(String packageName, int userId) {
+ synchronized (mLock) {
+ if (mCurrentVrService == null) {
+ return false;
+ }
+ return mCurrentVrService.getComponent().getPackageName().equals(packageName) &&
+ userId == mCurrentVrService.getUserId();
+ }
+ }
+
private void addListener(VrStateListener listener) {
synchronized (mLock) {
mListeners.add(listener);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index fb9b1ce..4848523 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1262,6 +1262,7 @@
return null;
}
+ @Override
public void setWallpaperComponentChecked(ComponentName name, String callingPackage) {
if (isWallpaperSupported(callingPackage) && isWallpaperSettingAllowed(callingPackage)) {
setWallpaperComponent(name);
@@ -1269,6 +1270,7 @@
}
// ToDo: Remove this version of the function
+ @Override
public void setWallpaperComponent(ComponentName name) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
synchronized (mLock) {
@@ -1281,7 +1283,9 @@
final long ident = Binder.clearCallingIdentity();
try {
wallpaper.imageWallpaperPending = false;
- bindWallpaperComponentLocked(name, false, true, wallpaper, null);
+ if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
+ wallpaper.wallpaperId = makeWallpaperIdLocked();
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 5cb7099..d684278 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -43,10 +43,14 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Path;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
@@ -73,6 +77,7 @@
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.animation.ClipRectLRAnimation;
import com.android.server.wm.animation.ClipRectTBAnimation;
+import com.android.server.wm.animation.CurvedTranslateAnimation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -127,6 +132,8 @@
public static final int TRANSIT_TASK_IN_PLACE = 17;
/** An activity is being relaunched (e.g. due to configuration change). */
public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
+ /** A task is being docked from recents. */
+ public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
/** Fraction of animation at which the recents thumbnail stays completely transparent */
private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
@@ -139,13 +146,15 @@
static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+ private static final Interpolator THUMBNAIL_DOCK_INTERPOLATOR =
+ new PathInterpolator(0.85f, 0f, 1f, 1f);
+
/**
* Maximum duration for the clip reveal animation. This is used when there is a lot of movement
* involved, to make it more understandable.
*/
private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
- private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336;
private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
private final Context mContext;
@@ -206,6 +215,7 @@
private final Interpolator mThumbnailFadeOutInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
private final Interpolator mFastOutLinearInInterpolator;
+ private final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
private final int mClipRevealTranslationY;
@@ -226,6 +236,8 @@
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_linear_in);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
mConfigShortAnimTime = context.getResources().getInteger(
com.android.internal.R.integer.config_shortAnimTime);
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
@@ -812,12 +824,14 @@
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
- int duration, Interpolator interpolator) {
+ long duration, Interpolator interpolator) {
if (duration > 0) {
a.setDuration(duration);
}
a.setFillAfter(true);
- a.setInterpolator(interpolator);
+ if (interpolator != null) {
+ a.setInterpolator(interpolator);
+ }
a.initialize(appWidth, appHeight, appWidth, appHeight);
return a;
}
@@ -866,55 +880,95 @@
* This animation runs for the thumbnail that gets cross faded with the enter/exit activity
* when a thumbnail is specified with the pending animation override.
*/
- Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, Bitmap thumbnailHeader,
- final int taskId) {
+ Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
+ Bitmap thumbnailHeader, final int taskId, int orientation) {
Animation a;
final int thumbWidthI = thumbnailHeader.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader.getHeight();
- final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
final int appWidth = appRect.width();
float scaleW = appWidth / thumbWidth;
- float unscaledHeight = thumbHeight * scaleW;
getNextAppTransitionStartRect(taskId, mTmpRect);
- final float unscaledStartY = mTmpRect.top - (unscaledHeight - thumbHeight) / 2f;
- final float toY = appRect.top + -unscaledStartY;
+ final float fromX;
+ final float fromY;
+ final float toX;
+ final float toY;
+ final float pivotX;
+ final float pivotY;
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+
+ // For the curved translate animation to work, the pivot points needs to be at the
+ // same absolute position as the one from the real surface.
+ toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
+ toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
+ pivotX = mTmpRect.width() / 2;
+ pivotY = appRect.height() / 2 / scaleW;
+ } else {
+ pivotX = 0;
+ pivotY = 0;
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+ toX = appRect.left;
+ toY = appRect.top;
+ }
+ final long duration = getAspectScaleDuration();
+ final Interpolator interpolator = getAspectScaleInterpolator();
if (mNextAppTransitionScaleUp) {
// Animation up from the thumbnail to the full screen
- Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
- mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
- scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
+ scale.setInterpolator(interpolator);
+ scale.setDuration(duration);
Animation alpha = new AlphaAnimation(1f, 0f);
- alpha.setInterpolator(mThumbnailFadeOutInterpolator);
- alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
- final float toX = appRect.left + appRect.width() / 2 -
- (mTmpRect.left + thumbWidth / 2);
- Animation translate = new TranslateAnimation(0, toX, 0, toY);
- translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+ ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator);
+ alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+ ? duration / 2
+ : duration);
+ Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
+ translate.setInterpolator(interpolator);
+ translate.setDuration(duration);
+
+ mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
+ mTmpToClipRect.set(appRect);
+
+ // Containing frame is in screen space, but we need the clip rect in the
+ // app space.
+ mTmpToClipRect.offsetTo(0, 0);
+ mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
+ mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
+
+ if (contentInsets != null) {
+ mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
+ (int) (-contentInsets.top * scaleW),
+ (int) (-contentInsets.right * scaleW),
+ (int) (-contentInsets.bottom * scaleW));
+ }
+
+ Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+ clipAnim.setInterpolator(interpolator);
+ clipAnim.setDuration(duration);
// This AnimationSet uses the Interpolators assigned above.
AnimationSet set = new AnimationSet(false);
set.addAnimation(scale);
set.addAnimation(alpha);
set.addAnimation(translate);
+ set.addAnimation(clipAnim);
a = set;
} else {
// Animation down from the full screen to the thumbnail
- Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
- mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
- scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
+ scale.setInterpolator(interpolator);
+ scale.setDuration(duration);
Animation alpha = new AlphaAnimation(0f, 1f);
alpha.setInterpolator(mThumbnailFadeInInterpolator);
- alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
- final float toX = appRect.left + appRect.width() / 2 -
- (mTmpRect.left + thumbWidth / 2);
- Animation translate = new TranslateAnimation(toX, 0, toY, 0);
- translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ alpha.setDuration(duration);
+ Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
+ translate.setInterpolator(interpolator);
+ translate.setDuration(duration);
// This AnimationSet uses the Interpolators assigned above.
AnimationSet set = new AnimationSet(false);
@@ -925,7 +979,48 @@
}
return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
- TOUCH_RESPONSE_INTERPOLATOR);
+ null);
+ }
+
+ private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
+
+ // Almost no x-change - use linear animation
+ if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return new TranslateAnimation(fromX, toX, fromY, toY);
+ } else {
+ final Path path = createCurvedPath(fromX, toX, fromY, toY);
+ return new CurvedTranslateAnimation(path);
+ }
+ }
+
+ private Path createCurvedPath(float fromX, float toX, float fromY, float toY) {
+ final Path path = new Path();
+ path.moveTo(fromX, fromY);
+
+ if (fromY > toY) {
+ // If the object needs to go up, move it in horizontal direction first, then vertical.
+ path.cubicTo(fromX, fromY, toX, 0.9f * fromY + 0.1f * toY, toX, toY);
+ } else {
+ // If the object needs to go down, move it in vertical direction first, then horizontal.
+ path.cubicTo(fromX, fromY, fromX, 0.1f * fromY + 0.9f * toY, toX, toY);
+ }
+ return path;
+ }
+
+ private long getAspectScaleDuration() {
+ if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f);
+ } else {
+ return THUMBNAIL_APP_TRANSITION_DURATION;
+ }
+ }
+
+ private Interpolator getAspectScaleInterpolator() {
+ if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return mFastOutSlowInInterpolator;
+ } else {
+ return TOUCH_RESPONSE_INTERPOLATOR;
+ }
}
/**
@@ -943,17 +1038,23 @@
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = mTmpRect.height();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
-
- // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
- float scale = 1f;
- int scaledTopDecor = 0;
+ final int thumbStartX = mTmpRect.left - containingFrame.left;
+ final int thumbStartY = mTmpRect.top - containingFrame.top;
switch (thumbTransitState) {
- case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
- if (freeform) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ if (freeform && scaleUp) {
a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
containingFrame, surfaceInsets, taskId);
+ } else if (freeform) {
+ a = createAspectScaledThumbnailExitFreeformAnimationLocked(
+ containingFrame, surfaceInsets, taskId);
} else {
+ AnimationSet set = new AnimationSet(true);
+
+ // In portrait, we scale to fit the width
mTmpFromClipRect.set(containingFrame);
mTmpToClipRect.set(containingFrame);
@@ -964,26 +1065,61 @@
// Exclude insets region from the source clip.
mTmpFromClipRect.inset(contentInsets);
-
- // We scale the width and clip to the top/left square
- scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
-
mNextAppTransitionInsets.set(contentInsets);
- Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
- computePivot(mTmpRect.left - containingFrame.left, scale),
- computePivot(mTmpRect.top - containingFrame.top, scale));
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // We scale the width and clip to the top/left square
+ // We scale the width and clip to the top/left square
+ float scale = thumbWidth /
+ (appWidth - contentInsets.left - contentInsets.right);
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
- AnimationSet set = new AnimationSet(true);
- set.addAnimation(clipAnim);
- set.addAnimation(scaleAnim);
- set.addAnimation(translateAnim);
+ mNextAppTransitionInsets.set(contentInsets);
+
+ Animation scaleAnim = new ScaleAnimation(
+ scaleUp ? scale : 1, scaleUp ? 1 : scale,
+ scaleUp ? scale : 1, scaleUp ? 1 : scale,
+ containingFrame.width() / 2f,
+ containingFrame.height() / 2f + contentInsets.top);
+ final float targetX = (mTmpRect.left - containingFrame.left);
+ final float x = containingFrame.width() / 2f
+ - containingFrame.width() / 2f * scale;
+ final float targetY = (mTmpRect.top - containingFrame.top);
+ final float y = containingFrame.height() / 2f
+ - containingFrame.height() / 2f * scale;
+ final float startX = targetX - x;
+ final float startY = targetY - y;
+ Animation clipAnim = scaleUp
+ ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
+ : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
+ Animation translateAnim = scaleUp
+ ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0)
+ : createCurvedMotion(0, startX, 0, startY - contentInsets.top);
+
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
+
+ } else {
+ // In landscape, we don't scale at all and only crop
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
+ mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
+
+ Animation clipAnim = scaleUp
+ ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
+ : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
+ Animation translateAnim = scaleUp
+ ? createCurvedMotion(thumbStartX, 0,
+ thumbStartY - contentInsets.top, 0)
+ : createCurvedMotion(0, thumbStartX, 0,
+ thumbStartY - contentInsets.top);
+
+ set.addAnimation(clipAnim);
+ set.addAnimation(translateAnim);
+ }
a = set;
+ a.setZAdjustment(Animation.ZORDER_TOP);
}
break;
}
@@ -1009,55 +1145,12 @@
}
break;
}
- case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
- // App window scaling down from full screen
- if (freeform) {
- a = createAspectScaledThumbnailExitFreeformAnimationLocked(
- containingFrame, surfaceInsets, taskId);
- } else {
- mTmpFromClipRect.set(containingFrame);
- mTmpToClipRect.set(containingFrame);
-
- // Containing frame is in screen space, but we need the clip rect in the
- // app space.
- mTmpFromClipRect.offsetTo(0, 0);
- mTmpToClipRect.offsetTo(0, 0);
-
- // Exclude insets region from the target clip.
- mTmpToClipRect.inset(contentInsets);
-
- // We scale the width and clip to the top/left square
- scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
-
- mNextAppTransitionInsets.set(contentInsets);
-
- Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
- computePivot(mTmpRect.left - containingFrame.left, scale),
- computePivot(mTmpRect.top - containingFrame.top, scale));
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
-
- AnimationSet set = new AnimationSet(true);
- set.addAnimation(clipAnim);
- set.addAnimation(scaleAnim);
- set.addAnimation(translateAnim);
-
- a = set;
- a.setZAdjustment(Animation.ZORDER_TOP);
- }
- break;
- }
default:
throw new RuntimeException("Invalid thumbnail transition state");
}
- int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
- THUMBNAIL_APP_TRANSITION_DURATION);
- return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
- TOUCH_RESPONSE_INTERPOLATOR);
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
+ getAspectScaleDuration(), getAspectScaleInterpolator());
}
private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
@@ -1411,6 +1504,7 @@
? WindowAnimation_activityCloseEnterAnimation
: WindowAnimation_activityCloseExitAnimation;
break;
+ case TRANSIT_DOCK_TASK_FROM_RECENTS:
case TRANSIT_TASK_OPEN:
animAttr = enter
? WindowAnimation_taskOpenEnterAnimation
@@ -1467,6 +1561,14 @@
return a;
}
+ int getAppStackClipMode() {
+ return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
+ || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+ || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
+ ? STACK_CLIP_NONE
+ : STACK_CLIP_AFTER_ANIM;
+ }
+
void postAnimationCallback() {
if (mNextAppTransitionCallback != null) {
mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
@@ -1686,6 +1788,9 @@
case TRANSIT_ACTIVITY_RELAUNCH: {
return "TRANSIT_ACTIVITY_RELAUNCH";
}
+ case TRANSIT_DOCK_TASK_FROM_RECENTS: {
+ return "TRANSIT_DOCK_TASK_FROM_RECENTS";
+ }
default: {
return "<UNKNOWN>";
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 3a5dec9..aae52e8 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -23,6 +23,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import android.graphics.Matrix;
import android.util.Slog;
@@ -76,8 +77,6 @@
// requires that the duration of the two animations are the same.
SurfaceControl thumbnail;
int thumbnailTransactionSeq;
- int thumbnailX;
- int thumbnailY;
int thumbnailLayer;
int thumbnailForceAboveLayer;
Animation thumbnailAnimation;
@@ -106,6 +105,7 @@
boolean usingTransferredAnimation = false;
private boolean mSkipFirstFrame = false;
+ private int mStackClip = STACK_CLIP_BEFORE_ANIM;
static final Animation sDummyAnimation = new DummyAnimation();
@@ -115,7 +115,8 @@
mAnimator = mService.mAnimator;
}
- public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame) {
+ public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame,
+ int stackClip) {
if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
+ ": " + anim + " wxh=" + width + "x" + height
+ " isVisible=" + mAppToken.isVisible());
@@ -142,6 +143,7 @@
transformation.clear();
transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
hasTransformation = true;
+ mStackClip = stackClip;
this.mSkipFirstFrame = skipFirstFrame;
@@ -186,6 +188,7 @@
mAppToken.allDrawn = false;
mAppToken.deferClearAllDrawn = false;
}
+ mStackClip = STACK_CLIP_BEFORE_ANIM;
}
public boolean isAnimating() {
@@ -201,6 +204,10 @@
deferThumbnailDestruction = false;
}
+ int getStackClip() {
+ return mStackClip;
+ }
+
void transferCurrentAnimation(
AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
@@ -245,7 +252,6 @@
thumbnailTransformation.clear();
final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime);
thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation);
- thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
ScreenRotationAnimation screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
@@ -279,6 +285,7 @@
}
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+ thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
@@ -452,8 +459,6 @@
}
if (thumbnail != null) {
pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
- pw.print(" x="); pw.print(thumbnailX);
- pw.print(" y="); pw.print(thumbnailY);
pw.print(" layer="); pw.println(thumbnailLayer);
pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
pw.print(prefix); pw.print("thumbnailTransformation=");
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 36e8bbb..6741aba 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -96,6 +96,7 @@
private final Interpolator mMinimizedDockInterpolator;
private float mMaximizeMeetFraction;
private final Rect mTouchRegion = new Rect();
+ private boolean mAdjustingForIme;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -173,6 +174,14 @@
return mLastVisibility;
}
+ void setAdjustingForIme(boolean adjusting) {
+ mAdjustingForIme = adjusting;
+ }
+
+ boolean isAdjustingForIme() {
+ return mAdjustingForIme;
+ }
+
void positionDockedStackedDivider(Rect frame) {
TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack == null) {
diff --git a/services/core/java/com/android/server/wm/DragResizeMode.java b/services/core/java/com/android/server/wm/DragResizeMode.java
new file mode 100644
index 0000000..08acf9d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DragResizeMode.java
@@ -0,0 +1,53 @@
+/*
+ * 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.server.wm;
+
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+
+/**
+ * Describes the mode in which a window is drag resizing.
+ */
+class DragResizeMode {
+
+ /**
+ * Freeform mode: Client surface is fullscreen, and client is responsible to draw window at
+ * the correct position.
+ */
+ static final int DRAG_RESIZE_MODE_FREEFORM = 0;
+
+ /**
+ * Mode for resizing the docked (and adjacent) stack: Client surface is fullscreen, but window
+ * is drawn at (0, 0), window manager is responsible for positioning the surface when draging.
+ */
+ static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
+
+ static boolean isModeAllowedForStack(int stackId, int mode) {
+ switch (mode) {
+ case DRAG_RESIZE_MODE_FREEFORM:
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ case DRAG_RESIZE_MODE_DOCKED_DIVIDER:
+ return stackId == DOCKED_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || stackId == HOME_STACK_ID;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f097eb2..4e8f19e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -18,8 +18,6 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -103,6 +101,7 @@
// Whether the task is currently being drag-resized
private boolean mDragResizing;
+ private int mDragResizeMode;
private boolean mHomeTask;
@@ -140,41 +139,7 @@
final String text =
mService.mContext.getString(R.string.dock_non_resizeble_failed_to_dock_text);
mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST, 0, 0, text).sendToTarget();
- return;
}
-
- final int dockSide = mStack.getDockSide();
- if (mResizeMode != RESIZE_MODE_FORCE_RESIZEABLE || dockSide == DOCKED_INVALID) {
- return;
- }
-
- int xOffset = 0;
- int yOffset = 0;
- mStack.getBounds(mTmpRect);
-
- if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
- // The toast was originally placed at the bottom and centered. To place it at the
- // bottom-center of the stack, we offset it horizontally by the diff between the center
- // of the stack bounds vs. the center of the screen.
- displayContent.getLogicalDisplayRect(mTmpRect2);
- xOffset = mTmpRect.centerX() - mTmpRect2.centerX();
- } else if (dockSide == DOCKED_TOP) {
- // The toast was originally placed at the bottom and centered. To place it at the bottom
- // center of the top stack, we offset it vertically by the diff between the bottom of
- // the stack bounds vs. the bottom of the content rect.
- //
- // Note here we use the content rect instead of the display rect, as we want the toast's
- // distance to the dock divider (when it's placed at the top half) to be the same as
- // it's distance to the top of the navigation bar (when it's placed at the bottom).
-
- // We don't adjust for DOCKED_BOTTOM case since it's already at the bottom.
- displayContent.getContentRect(mTmpRect2);
- yOffset = mTmpRect2.bottom - mTmpRect.bottom;
- }
- final String text =
- mService.mContext.getString(R.string.dock_forced_resizable);
- mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST,
- xOffset, yOffset, text).sendToTarget();
}
void addAppToken(int addPos, AppWindowToken wtoken, int resizeMode, boolean homeTask) {
@@ -543,9 +508,14 @@
mStack.getDisplayContent().getLogicalDisplayRect(out);
}
- void setDragResizing(boolean dragResizing) {
+ void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
+ if (!DragResizeMode.isModeAllowedForStack(mStack.mStackId, dragResizeMode)) {
+ throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
+ + mStack.mStackId + " dragResizeMode=" + dragResizeMode);
+ }
mDragResizing = dragResizing;
+ mDragResizeMode = dragResizeMode;
resetDragResizingChangeReported();
}
}
@@ -564,6 +534,10 @@
return mDragResizing || (mStack != null && mStack.isDragResizing());
}
+ int getDragResizeMode() {
+ return mDragResizeMode;
+ }
+
void updateDisplayInfo(final DisplayContent displayContent) {
if (displayContent == null) {
return;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 92701de..ae70aa8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -29,6 +29,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.dipToPixel;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
@@ -379,7 +380,7 @@
private void endDragLocked() {
mResizing = false;
- mTask.setDragResizing(false);
+ mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
}
/** Returns true if the move operation should be ended. */
@@ -409,7 +410,7 @@
bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
}
mWindowDragBounds.set(left, top, right, bottom);
- mTask.setDragResizing(true);
+ mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index c667767..8d67771 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -200,10 +200,11 @@
* @param bounds The adjusted bounds.
* @param keepInsets Whether to keep the insets from the original bounds or to calculate new
* ones depending on the adjusted bounds.
+ * @return true if the adjusted bounds has changed.
*/
- private void setAdjustedBounds(Rect bounds, boolean keepInsets) {
+ private boolean setAdjustedBounds(Rect bounds, boolean keepInsets) {
if (mAdjustedBounds.equals(bounds)) {
- return;
+ return false;
}
mAdjustedBounds.set(bounds);
@@ -211,6 +212,7 @@
alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds,
adjusted && keepInsets ? mBounds : null);
mDisplayContent.layoutNeeded = true;
+ return true;
}
private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
@@ -794,7 +796,9 @@
void setAdjustedForIme(WindowState imeWin) {
mAdjustedForIme = true;
mImeWin = imeWin;
- updateAdjustedBounds();
+ if (updateAdjustedBounds()) {
+ getDisplayContent().mDividerControllerLocked.setAdjustingForIme(true);
+ }
}
/**
@@ -803,7 +807,9 @@
void resetAdjustedForIme() {
mAdjustedForIme = false;
mImeWin = null;
- updateAdjustedBounds();
+ if (updateAdjustedBounds()) {
+ getDisplayContent().mDividerControllerLocked.setAdjustingForIme(true);
+ }
}
/**
@@ -920,7 +926,7 @@
/**
* Updates the adjustment depending on it's current state.
*/
- void updateAdjustedBounds() {
+ boolean updateAdjustedBounds() {
boolean adjust = false;
if (mMinimizeAmount != 0f) {
adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
@@ -931,7 +937,7 @@
mTmpAdjustedBounds.setEmpty();
mLastContentBounds.setEmpty();
}
- setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
+ return setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
}
boolean isAdjustedForMinimizedDockedStack() {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f243761..eae7838 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -36,6 +36,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
@@ -406,7 +408,8 @@
Animation a = mPolicy.createForceHideEnterAnimation(false,
keyguardGoingAwayToShade);
- winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime());
+ winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime(),
+ STACK_CLIP_BEFORE_ANIM);
winAnimator.mKeyguardGoingAwayAnimation = true;
winAnimator.mKeyguardGoingAwayWithWallpaper
= keyguardGoingAwayWithWallpaper;
@@ -445,7 +448,7 @@
}
final AppWindowToken atoken = win.mAppToken;
- if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
+ if (winAnimator.mDrawState == READY_TO_SHOW) {
if (atoken == null || atoken.allDrawn) {
if (winAnimator.performShowLocked()) {
setPendingLayoutChanges(displayId,
@@ -487,7 +490,7 @@
if (a != null) {
if (DEBUG_KEYGUARD) Slog.v(TAG,
"Starting keyguard exit animation on window " + winAnimator.mWin);
- winAnimator.setAnimation(a);
+ winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM);
winAnimator.mKeyguardGoingAwayAnimation = true;
winAnimator.mKeyguardGoingAwayWithWallpaper
= keyguardGoingAwayWithWallpaper;
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index d843a8c..f76f03f 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -77,28 +77,22 @@
int oldLayer = w.mLayer;
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
- w.mLayer = curLayer;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
- w.mLayer = curLayer;
}
- if (w.mLayer != oldLayer) {
- layerChanged = true;
- anyLayerChanged = true;
- }
+ assignAnimLayer(w, curLayer);
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- oldLayer = winAnimator.mAnimLayer;
- winAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
- getSpecialWindowAnimLayerAdjustment(w);
- if (winAnimator.mAnimLayer != oldLayer) {
+ // TODO: Preserved old behavior of code here but not sure comparing
+ // oldLayer to mAnimLayer and mLayer makes sense...though the
+ // worst case would be unintentionalp layer reassignment.
+ if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
layerChanged = true;
anyLayerChanged = true;
}
if (w.mAppToken != null) {
mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
- winAnimator.mAnimLayer);
+ w.mWinAnimator.mAnimLayer);
}
collectSpecialWindows(w);
@@ -223,12 +217,21 @@
private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
if (win != null) {
- win.mLayer = layer;
- win.mWinAnimator.mAnimLayer = layer;
+ assignAnimLayer(win, layer);
layer++;
}
return layer;
}
+
+ private void assignAnimLayer(WindowState w, int layer) {
+ w.mLayer = layer;
+ w.mWinAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
+ getSpecialWindowAnimLayerAdjustment(w);
+ if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
+ && w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
+ w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
+ }
+ }
void dump(PrintWriter pw, String s) {
if (mInputMethodAnimLayerAdjustment != 0 ||
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 14291ca..5771d69 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -202,6 +202,8 @@
import static android.view.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END;
import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
@@ -236,6 +238,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
@@ -798,7 +801,13 @@
= new WindowManagerInternal.AppTransitionListener() {
@Override
+ public void onAppTransitionCancelledLocked() {
+ mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_CANCELLED);
+ }
+
+ @Override
public void onAppTransitionFinishedLocked(IBinder token) {
+ mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_FINISHED);
AppWindowToken atoken = findAppWindowToken(token);
if (atoken == null) {
return;
@@ -2920,9 +2929,9 @@
}
}
final boolean freeformResizing = win.isDragResizing()
- && win.getResizeMode() == WindowState.DRAG_RESIZE_MODE_FREEFORM;
+ && win.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
final boolean dockedResizing = win.isDragResizing()
- && win.getResizeMode() == WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+ && win.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
result |= freeformResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM : 0;
result |= dockedResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED : 0;
if (win.isAnimatingWithSavedSurface()) {
@@ -3039,7 +3048,7 @@
final int containingWidth = frame.width();
final int containingHeight = frame.height();
atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,
- mAppTransition.canSkipFirstFrame());
+ mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode());
}
} else {
atoken.mAppAnimator.clearAnimation();
@@ -4024,8 +4033,6 @@
wAppAnimator.thumbnail.destroy();
}
wAppAnimator.thumbnail = tAppAnimator.thumbnail;
- wAppAnimator.thumbnailX = tAppAnimator.thumbnailX;
- wAppAnimator.thumbnailY = tAppAnimator.thumbnailY;
wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
tAppAnimator.thumbnail = null;
@@ -4989,6 +4996,23 @@
}
}
+ /**
+ * Puts a specific task into docked drag resizing mode. See {@link DragResizeMode}.
+ *
+ * @param taskId The id of the task to put into drag resize mode.
+ * @param resizing Whether to put the task into drag resize mode.
+ */
+ public void setTaskDockedResizing(int taskId, boolean resizing) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("setTaskDockedResizing: taskId " + taskId
+ + " not found.");
+ }
+ task.setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+ }
+ }
+
public void scrollTask(int taskId, Rect bounds) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);
@@ -5735,7 +5759,7 @@
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay)
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false)
- && Build.HARDWARE.contains("goldfish")) {
+ && Build.IS_EMULATOR) {
mH.sendMessage(mH.obtainMessage(H.SHOW_EMULATOR_DISPLAY_OVERLAY));
}
}
@@ -7352,6 +7376,30 @@
}
}
+ private void adjustForImeIfNeeded(final DisplayContent displayContent) {
+ final WindowState imeWin = mInputMethodWindow;
+ final TaskStack focusedStack =
+ mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+ if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
+ && isStackVisibleLocked(DOCKED_STACK_ID)
+ && focusedStack != null
+ && focusedStack.getDockSide() == DOCKED_BOTTOM){
+ final ArrayList<TaskStack> stacks = displayContent.getStacks();
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ if (stack.isVisibleLocked()) {
+ stack.setAdjustedForIme(imeWin);
+ }
+ }
+ } else {
+ final ArrayList<TaskStack> stacks = displayContent.getStacks();
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ stack.resetAdjustedForIme();
+ }
+ }
+ }
+
// -------------------------------------------------------------
// Drag and drop
// -------------------------------------------------------------
@@ -7670,7 +7718,9 @@
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
- public static final int NOTIFY_STARTING_WINDOW_DRAWN = 48;
+ public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48;
+ public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
+ public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
/**
* Used to denote that an integer field in a message will not be used.
@@ -8209,30 +8259,8 @@
case UPDATE_DOCKED_STACK_DIVIDER: {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
-
displayContent.getDockedDividerController().reevaluateVisibility(false);
-
- final WindowState imeWin = mInputMethodWindow;
- final TaskStack focusedStack =
- mCurrentFocus != null ? mCurrentFocus.getStack() : null;
- if (imeWin != null && imeWin.isVisibleNow()
- && isStackVisibleLocked(DOCKED_STACK_ID)
- && focusedStack != null
- && focusedStack.getDockSide() == DOCKED_BOTTOM){
- final ArrayList<TaskStack> stacks = displayContent.getStacks();
- for (int i = stacks.size() - 1; i >= 0; --i) {
- final TaskStack stack = stacks.get(i);
- if (stack.isVisibleLocked()) {
- stack.setAdjustedForIme(imeWin);
- }
- }
- } else {
- final ArrayList<TaskStack> stacks = displayContent.getStacks();
- for (int i = stacks.size() - 1; i >= 0; --i) {
- final TaskStack stack = stacks.get(i);
- stack.resetAdjustedForIme();
- }
- }
+ adjustForImeIfNeeded(displayContent);
}
}
break;
@@ -8273,6 +8301,14 @@
mAmInternal.notifyAppTransitionStarting(msg.arg1);
}
break;
+ case NOTIFY_APP_TRANSITION_CANCELLED: {
+ mAmInternal.notifyAppTransitionCancelled();
+ }
+ break;
+ case NOTIFY_APP_TRANSITION_FINISHED: {
+ mAmInternal.notifyAppTransitionFinished();
+ }
+ break;
case NOTIFY_STARTING_WINDOW_DRAWN: {
mAmInternal.notifyStartingWindowDrawn();
}
@@ -8971,7 +9007,7 @@
+ ", mDrawState=DRAW_PENDING in " + w
+ ", surfaceController " + winAnimator.mSurfaceController);
}
- winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ winAnimator.mDrawState = DRAW_PENDING;
if (w.mAppToken != null) {
w.mAppToken.allDrawn = false;
w.mAppToken.deferClearAllDrawn = false;
@@ -10515,9 +10551,9 @@
}
@Override
- public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
try {
- getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver);
+ getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
} catch (RemoteException e) {
}
}
@@ -10785,7 +10821,7 @@
final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
if (win.isVisibleLw()
&& (win.mAppToken != null || isForceHiding)) {
- win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ win.mWinAnimator.mDrawState = DRAW_PENDING;
// Force add to mResizingWindows.
win.mLastContentInsets.set(-1, -1, -1, -1);
mWaitingForDrawn.add(win);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1695615..a302006 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -71,6 +71,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
@@ -87,6 +88,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -123,9 +126,6 @@
// to capture touch events in that area.
static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
- static final int DRAG_RESIZE_MODE_FREEFORM = 0;
- static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
-
static final boolean DEBUG_DISABLE_SAVING_SURFACES = false;
final WindowManagerService mService;
@@ -655,8 +655,7 @@
}
if (mInsetFrame.isEmpty() && (fullscreenTask
- || (isChildWindow() && (mAttrs.privateFlags
- & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0))) {
+ || layoutInParentFrame())) {
// We use the parent frame as the containing frame for fullscreen and child windows
mContainingFrame.set(pf);
mDisplayFrame.set(df);
@@ -756,20 +755,10 @@
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- if (isVisibleLw() || mWinAnimator.isAnimating()) {
- // We don't adjust the dock divider frame for reasons other than performance. The
- // real reason is that if it gets adjusted before it is shown for the first time,
- // it would get size (0, 0). This causes a problem when we finally show the dock
- // divider and try to draw to it. We do set the surface size at that moment to
- // the correct size, but it's too late for the Surface Flinger to make it
- // available for view rendering and as a result the renderer receives size 1, 1.
- // This way we just keep the divider at the original size and Surface Flinger
- // will return the correct value to the renderer.
- mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
- mContentFrame.set(mFrame);
- if (!mFrame.equals(mLastFrame)) {
- mMovedByResize = true;
- }
+ mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
+ mContentFrame.set(mFrame);
+ if (!mFrame.equals(mLastFrame)) {
+ mMovedByResize = true;
}
} else {
mContentFrame.set(Math.max(mContentFrame.left, frame.left),
@@ -1309,7 +1298,7 @@
*/
boolean hasMoved() {
return mHasSurface && (mContentChanged || mMovedByResize)
- && !mAnimatingExit && !mWinAnimator.mLastHidden && mService.okToDisplay()
+ && !mAnimatingExit && mService.okToDisplay()
&& (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left)
&& (mAttachedWindow == null || !mAttachedWindow.hasMoved());
}
@@ -1883,6 +1872,13 @@
}
private boolean shouldSaveSurface() {
+ if ((mAttrs.flags & FLAG_SECURE) != 0) {
+ // We don't save secure surfaces since their content shouldn't be shown while the app
+ // isn't on screen and content might leak through during the transition animation with
+ // saved surface.
+ return false;
+ }
+
if (ActivityManager.isLowRamDeviceStatic()) {
// Don't save surfaces on Svelte devices.
return false;
@@ -2251,7 +2247,7 @@
return mResizeMode;
}
- private boolean computeDragResizing() {
+ boolean computeDragResizing() {
final Task task = getTask();
if (task == null) {
return false;
@@ -2281,9 +2277,14 @@
return;
}
mDragResizing = resizing;
- mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing()
- ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
- : DRAG_RESIZE_MODE_FREEFORM;
+ final Task task = getTask();
+ if (task != null && task.isDragResizing()) {
+ mResizeMode = task.getDragResizeMode();
+ } else {
+ mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing()
+ ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
+ : DRAG_RESIZE_MODE_FREEFORM;
+ }
}
boolean isDragResizing() {
@@ -2521,7 +2522,7 @@
final int ph = mContainingFrame.height();
final Task task = getTask();
final boolean nonFullscreenTask = inMultiWindowMode();
- final boolean fitToDisplay = task != null && !task.isFloating();
+ final boolean fitToDisplay = task != null && !task.isFloating() && !layoutInParentFrame();
float x, y;
int w,h;
@@ -2595,6 +2596,10 @@
return mAttachedWindow != null;
}
+ boolean layoutInParentFrame() {
+ return isChildWindow() && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0;
+ }
+
void setReplacing(boolean animate) {
if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) != 0
|| mAttrs.type == TYPE_APPLICATION_STARTING) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 41eafe7..1e103f0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -20,6 +20,8 @@
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -38,8 +40,6 @@
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.logWithStack;
-import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -75,6 +75,25 @@
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM;
static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+ /**
+ * Mode how the window gets clipped by the stack bounds during an animation: The clipping should
+ * be applied after applying the animation transformation, i.e. the stack bounds don't move
+ * during the animation.
+ */
+ static final int STACK_CLIP_AFTER_ANIM = 0;
+
+ /**
+ * Mode how the window gets clipped by the stack bounds: The clipping should be applied before
+ * applying the animation transformation, i.e. the stack bounds move with the window.
+ */
+ static final int STACK_CLIP_BEFORE_ANIM = 1;
+
+ /**
+ * Mode how window gets clipped by the stack bounds during an animation: Don't clip the window
+ * by the stack bounds.
+ */
+ static final int STACK_CLIP_NONE = 2;
+
// Unchanging local convenience fields.
final WindowManagerService mService;
final WindowState mWin;
@@ -100,6 +119,7 @@
int mLastLayer;
long mAnimationStartTime;
long mLastAnimationTime;
+ int mStackClip = STACK_CLIP_BEFORE_ANIM;
/**
* Set when we have changed the size of the surface, to know that
@@ -128,7 +148,9 @@
boolean mHasClipRect;
Rect mClipRect = new Rect();
Rect mTmpClipRect = new Rect();
+ Rect mTmpFinalClipRect = new Rect();
Rect mLastClipRect = new Rect();
+ Rect mLastFinalClipRect = new Rect();
Rect mTmpStackBounds = new Rect();
/**
@@ -226,7 +248,7 @@
mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
}
- public void setAnimation(Animation anim, long startTime) {
+ public void setAnimation(Animation anim, long startTime, int stackClip) {
if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
mAnimating = false;
mLocalAnimating = false;
@@ -238,10 +260,15 @@
mTransformation.setAlpha(mLastHidden ? 0 : 1);
mHasLocalTransformation = true;
mAnimationStartTime = startTime;
+ mStackClip = stackClip;
+ }
+
+ public void setAnimation(Animation anim, int stackClip) {
+ setAnimation(anim, -1, stackClip);
}
public void setAnimation(Animation anim) {
- setAnimation(anim, -1);
+ setAnimation(anim, -1, STACK_CLIP_AFTER_ANIM);
}
public void clearAnimation() {
@@ -252,6 +279,7 @@
mAnimation = null;
mKeyguardGoingAwayAnimation = false;
mKeyguardGoingAwayWithWallpaper = false;
+ mStackClip = STACK_CLIP_BEFORE_ANIM;
}
}
@@ -397,6 +425,7 @@
if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
mHasTransformation = false;
mHasLocalTransformation = false;
+ mStackClip = STACK_CLIP_BEFORE_ANIM;
mWin.checkPolicyVisibilityChange();
mTransformation.clear();
if (mDrawState == HAS_DRAWN
@@ -994,6 +1023,17 @@
if (appTransformation.hasClipRect()) {
mClipRect.set(appTransformation.getClipRect());
mHasClipRect = true;
+ // The app transformation clip will be in the coordinate space of the main
+ // activity window, which the animation correctly assumes will be placed at
+ // (0,0)+(insets) relative to the containing frame. This isn't necessarily
+ // true for child windows though which can have an arbitrary frame position
+ // relative to their containing frame. We need to offset the difference
+ // between the containing frame as used to calculate the crop and our
+ // bounds to compensate for this.
+ if (mWin.isChildWindow() && mWin.layoutInParentFrame()) {
+ mClipRect.offset( (mWin.mContainingFrame.left - mWin.mFrame.left),
+ mWin.mContainingFrame.top - mWin.mFrame.top );
+ }
}
}
if (screenAnimation) {
@@ -1119,11 +1159,13 @@
}
}
- Rect calculateSurfaceWindowCrop() {
+ void calculateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect) {
final WindowState w = mWin;
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent == null) {
- return null;
+ clipRect.setEmpty();
+ finalClipRect.setEmpty();
+ return;
}
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Updating crop for window: " + w + ", " + "mLastCrop=" +
@@ -1159,7 +1201,6 @@
final boolean fullscreen = w.isFrameFullscreen(displayInfo);
final boolean isFreeformResizing =
w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
- final Rect clipRect = mTmpClipRect;
// We use the clip rect as provided by the tranformation for non-fullscreen windows to
// avoid premature clipping with the system decor rect.
@@ -1190,7 +1231,8 @@
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
- adjustCropToStackBounds(w, clipRect, isFreeformResizing);
+ finalClipRect.setEmpty();
+ adjustCropToStackBounds(w, clipRect, finalClipRect, isFreeformResizing);
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + clipRect);
w.transformFromScreenToSurfaceSpace(clipRect);
@@ -1199,35 +1241,39 @@
if (w.hasJustMovedInStack() && mLastClipRect.isEmpty() && !clipRect.isEmpty()) {
clipRect.setEmpty();
}
-
- return clipRect;
}
- void updateSurfaceWindowCrop(Rect clipRect, boolean recoveringMemory) {
+ void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
}
+ if (!finalClipRect.equals(mLastFinalClipRect)) {
+ mLastFinalClipRect.set(finalClipRect);
+ mSurfaceController.setFinalCropInTransaction(finalClipRect);
+ }
}
- private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) {
+ private int resolveStackClip() {
+
+ // App animation overrides window animation stack clip mode.
+ if (mAppAnimator != null && mAppAnimator.animation != null) {
+ return mAppAnimator.getStackClip();
+ } else {
+ return mStackClip;
+ }
+ }
+ private void adjustCropToStackBounds(WindowState w, Rect clipRect, Rect finalClipRect,
+ boolean isFreeformResizing) {
final Task task = w.getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
return;
}
- // We don't apply the stack bounds crop if:
- // 1. The window is currently animating in freeform mode, otherwise the animating window
- // will be suddenly (docked) or for whole animation (freeform) cut off.
- // 2. The window that is being replaced during animation, because it was living in a
- // different stack. If we suddenly crop it to the new stack bounds, it might get cut off.
- // We don't want it to happen, so we let it ignore the stack bounds until it gets removed.
- // The window that will replace it will abide them.
- // TODO: identify animations where we don't want to apply docked stack crop to the docked
- // task. For example, if the app is going from freeform to docked mode, we may not
- // want to apply the crop during the animation, since it will make the app appear
- // cropped prematurely.
- if (isAnimating() && (w.mWillReplaceWindow || w.inFreeformWorkspace())) {
+ final int stackClip = resolveStackClip();
+
+ // It's animating and we don't want to clip it to stack bounds during animation - abort.
+ if (isAnimating() && stackClip == STACK_CLIP_NONE) {
return;
}
@@ -1246,16 +1292,24 @@
final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
+ // If we are animating, we either apply the clip before applying all the animation
+ // transformation or after all the transformation.
+ final boolean useFinalClipRect = isAnimating() && stackClip == STACK_CLIP_AFTER_ANIM;
+
// We need to do some acrobatics with surface position, because their clip region is
// relative to the inside of the surface, but the stack bounds aren't.
- clipRect.left = Math.max(0,
- Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
- clipRect.top = Math.max(0,
- Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
- clipRect.right = Math.max(0,
- Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
- clipRect.bottom = Math.max(0,
- Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+ if (useFinalClipRect) {
+ finalClipRect.set(mTmpStackBounds);
+ } else {
+ clipRect.left = Math.max(0,
+ Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
+ clipRect.top = Math.max(0,
+ Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
+ clipRect.right = Math.max(0,
+ Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
+ clipRect.bottom = Math.max(0,
+ Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+ }
}
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
@@ -1268,10 +1322,10 @@
float extraHScale = (float) 1.0;
float extraVScale = (float) 1.0;
- final Rect crop = calculateSurfaceWindowCrop();
+ calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
if (task != null && task.mStack.getForceScaleToCrop()) {
- extraHScale = crop.width() / (float)mTmpSize.width();
- extraVScale = crop.height() / (float)mTmpSize.height();
+ extraHScale = mTmpClipRect.width() / (float)mTmpSize.width();
+ extraVScale = mTmpClipRect.height() / (float)mTmpSize.height();
// In the case of ForceScaleToCrop we scale entire tasks together,
// and so we need to scale our offsets relative to the task bounds
@@ -1285,12 +1339,13 @@
// Since we are scaled to fit in our previously desired crop, we can now
// expose the whole window in buffer space, and not risk extending
// past where the system would have cropped us
- crop.set(0, 0, mTmpSize.width(), mTmpSize.height());
- updateSurfaceWindowCrop(crop, recoveringMemory);
+ mTmpClipRect.set(0, 0, mTmpSize.width(), mTmpSize.height());
+ mTmpFinalClipRect.setEmpty();
+ updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
} else {
mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
recoveringMemory);
- updateSurfaceWindowCrop(crop, recoveringMemory);
+ updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
}
@@ -1447,7 +1502,8 @@
SurfaceControl.openTransaction();
mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
mWin.mFrame.top + top, false);
- updateSurfaceWindowCrop(calculateSurfaceWindowCrop(), false);
+ calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
+ updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
+ " pos=(" + left + "," + top + ")", e);
@@ -1710,6 +1766,7 @@
pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
pw.print(" mAnimation="); pw.println(mAnimation);
+ pw.print(" mStackClip="); pw.println(mStackClip);
}
if (mHasTransformation || mHasLocalTransformation) {
pw.print(prefix); pw.print("XForm: has=");
@@ -1729,6 +1786,9 @@
if (mHasClipRect) {
pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
}
+ if (!mLastFinalClipRect.isEmpty()) {
+ pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw);
+ }
pw.println();
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index be3ad3b..8799c61 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -189,6 +189,16 @@
}
}
+ void setFinalCropInTransaction(Rect clipRect) {
+ if (SHOW_TRANSACTIONS) logSurface(
+ "FINAL CROP " + clipRect.toShortString(), null);
+ try {
+ mSurfaceControl.setFinalCrop(clipRect);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error disconnecting surface in: " + this, e);
+ }
+ }
+
void setLayer(int layer) {
if (mSurfaceControl != null) {
SurfaceControl.openTransaction();
@@ -460,6 +470,7 @@
private final PointF mPosition = new PointF();
private final Point mSize = new Point();
private final Rect mWindowCrop = new Rect();
+ private final Rect mFinalCrop = new Rect();
private boolean mShown = false;
private int mLayerStack;
private boolean mIsOpaque;
@@ -545,6 +556,19 @@
}
@Override
+ public void setFinalCrop(Rect crop) {
+ if (crop != null) {
+ if (!crop.equals(mFinalCrop)) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setFinalCrop("
+ + crop.toShortString() + "): OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mFinalCrop.set(crop);
+ }
+ }
+ super.setFinalCrop(crop);
+ }
+
+ @Override
public void setLayerStack(int layerStack) {
if (layerStack != mLayerStack) {
if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
@@ -655,6 +679,7 @@
pw.print(" mSize="); pw.print(s.mSize.x); pw.print("x");
pw.println(s.mSize.y);
pw.print(" mCrop="); s.mWindowCrop.printShortString(pw); pw.println();
+ pw.print(" mFinalCrop="); s.mFinalCrop.printShortString(pw); pw.println();
pw.print(" Transform: ("); pw.print(s.mDsdx); pw.print(", ");
pw.print(s.mDtdx); pw.print(", "); pw.print(s.mDsdy);
pw.print(", "); pw.print(s.mDtdy); pw.println(")");
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 5ad771f..3b0081d 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -9,6 +9,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
@@ -18,6 +19,7 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -30,13 +32,21 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerService.H.*;
-import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.DO_TRAVERSAL;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
+import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
+import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
+import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
@@ -687,7 +697,11 @@
w.getTask().mStack.isAdjustedForMinimizedDockedStack();
if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
&& !w.isDragResizing() && !adjustedForMinimizedDockedStack
- && (task == null || !w.getTask().mStack.getFreezeMovementAnimations())) {
+ && (task == null || !w.getTask().mStack.getFreezeMovementAnimations())
+ && !w.mWinAnimator.mLastHidden) {
+ winAnimator.setMoveAnimation(left, top);
+ } else if (w.mAttrs.type == TYPE_DOCK_DIVIDER &&
+ displayContent.getDockedDividerController().isAdjustingForIme()) {
winAnimator.setMoveAnimation(left, top);
}
@@ -701,11 +715,11 @@
w.mClient.moved(left, top);
} catch (RemoteException e) {
}
+ w.mMovedByResize = false;
}
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
w.mContentChanged = false;
- w.mMovedByResize = false;
// Moved from updateWindowsAndWallpaperLocked().
if (w.mHasSurface) {
@@ -805,6 +819,8 @@
mService.updateResizingWindows(w);
}
+ displayContent.getDockedDividerController().setAdjustingForIme(false);
+
mService.mDisplayManagerInternal.setDisplayProperties(displayId,
mDisplayHasContent,
mPreferredRefreshRate,
@@ -1218,6 +1234,10 @@
if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
}
+ if (mService.mAppTransition.getAppTransition()
+ == AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ appAnimator.startProlongAnimation(PROLONG_ANIMATION_AT_START);
+ }
}
return topOpeningApp;
}
@@ -1556,12 +1576,13 @@
WindowState win = appToken.findMainWindow();
Rect appRect = win != null ? win.getContentFrameLw() :
new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
+ Rect insets = win != null ? win.mContentInsets : null;
// For the new aspect-scaled transition, we want it to always show
// above the animating opening/closing window, and we want to
// synchronize its thumbnail surface with the surface for the
// open/close animation (only on the way down)
anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
- thumbnailHeader, taskId);
+ insets, thumbnailHeader, taskId, mService.mCurConfiguration.orientation);
openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
openingAppAnimator.deferThumbnailDestruction =
!mService.mAppTransition.isNextThumbnailTransitionScaleUp();
@@ -1576,8 +1597,6 @@
openingAppAnimator.thumbnailLayer = openingLayer;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
- openingAppAnimator.thumbnailX = mTmpStartRect.left;
- openingAppAnimator.thumbnailY = mTmpStartRect.top;
} catch (Surface.OutOfResourcesException e) {
Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
+ dirty.width() + " h=" + dirty.height(), e);
diff --git a/services/core/java/com/android/server/wm/animation/CurvedTranslateAnimation.java b/services/core/java/com/android/server/wm/animation/CurvedTranslateAnimation.java
new file mode 100644
index 0000000..33ac2ff
--- /dev/null
+++ b/services/core/java/com/android/server/wm/animation/CurvedTranslateAnimation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.server.wm.animation;
+
+import android.animation.KeyframeSet;
+import android.animation.PathKeyframes;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+/**
+ * Translate animation which follows a curved path.
+ */
+public class CurvedTranslateAnimation extends Animation {
+
+ private final PathKeyframes mKeyframes;
+
+ public CurvedTranslateAnimation(Path path) {
+ mKeyframes = KeyframeSet.ofPath(path);
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ PointF location = (PointF) mKeyframes.getValue(interpolatedTime);
+ t.getMatrix().setTranslate(location.x, location.y);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c362c9c..7da0a9a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7513,7 +7513,9 @@
/**
* Sets which packages may enter lock task mode.
*
- * This function can only be called by the device owner.
+ * <p>This function can only be called by the device owner or alternatively by the profile owner
+ * in case the user is affiliated.
+ *
* @param packages The list of packages allowed to enter lock task mode.
*/
@Override
@@ -7521,10 +7523,17 @@
throws SecurityException {
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
- int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
- setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
+ ActiveAdmin deviceOwner = getActiveAdminWithPolicyForUidLocked(
+ who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, mInjector.binderGetCallingUid());
+ ActiveAdmin profileOwner = getActiveAdminWithPolicyForUidLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid());
+ if (deviceOwner != null || (profileOwner != null && isAffiliatedUser())) {
+ int userHandle = mInjector.userHandleGetCallingUserId();
+ setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
+ } else {
+ throw new SecurityException("Admin " + who +
+ " is neither the device owner or affiliated user's profile owner.");
+ }
}
}
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
new file mode 100644
index 0000000..f169411
--- /dev/null
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.net.apf;
+
+/**
+ * APF program support capabilities.
+ *
+ * @hide
+ */
+public class ApfCapabilities {
+ /**
+ * Version of APF instruction set supported for packet filtering. 0 indicates no support for
+ * packet filtering using APF programs.
+ */
+ public final int apfVersionSupported;
+
+ /**
+ * Maximum size of APF program allowed.
+ */
+ public final int maximumApfProgramSize;
+
+ /**
+ * Format of packets passed to APF filter. Should be one of ARPHRD_*
+ */
+ public final int apfPacketFormat;
+
+ ApfCapabilities(int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) {
+ this.apfVersionSupported = apfVersionSupported;
+ this.maximumApfProgramSize = maximumApfProgramSize;
+ this.apfPacketFormat = apfPacketFormat;
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
similarity index 65%
rename from services/core/java/com/android/server/connectivity/ApfFilter.java
rename to services/net/java/android/net/apf/ApfFilter.java
index 824db65..ebbf991 100644
--- a/services/core/java/com/android/server/connectivity/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity;
+package android.net.apf;
import static android.system.OsConstants.*;
@@ -22,15 +22,16 @@
import android.net.apf.ApfGenerator;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
+import android.net.ip.IpManager;
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
import android.util.Log;
import android.util.Pair;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.ConnectivityService;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -52,6 +53,17 @@
* listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
* filter out redundant duplicate ones.
*
+ * Threading model:
+ * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
+ * know what RAs to filter for, thus generating APF programs is dependent on mRas.
+ * mRas can be accessed by multiple threads:
+ * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
+ * - callers of:
+ * - setMulticastFilter(), which can cause an APF program to be generated.
+ * - dump(), which dumps mRas among other things.
+ * - shutdown(), which clears mRas.
+ * So access to mRas is synchronized.
+ *
* @hide
*/
public class ApfFilter {
@@ -93,23 +105,62 @@
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private final ConnectivityService mConnectivityService;
- private final NetworkAgentInfo mNai;
- private ReceiveThread mReceiveThread;
- private String mIfaceName;
- private long mUniqueCounter;
+ private static final int ETH_HEADER_LEN = 14;
+ private static final int ETH_ETHERTYPE_OFFSET = 12;
+ private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
+ private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
+ // Endianness is not an issue for this constant because the APF interpreter always operates in
+ // network byte order.
+ private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
+ private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+ private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
- private ApfFilter(ConnectivityService connectivityService, NetworkAgentInfo nai) {
- mConnectivityService = connectivityService;
- mNai = nai;
+ private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
+ private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
+ private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int IPV6_HEADER_LEN = 40;
+ // The IPv6 all nodes address ff02::1
+ private static final byte[] IPV6_ALL_NODES_ADDRESS =
+ new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+
+ private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
+
+ // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
+ private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
+ private static final int UDP_HEADER_LEN = 8;
+
+ private static final int DHCP_CLIENT_PORT = 68;
+ // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
+ private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
+
+ private final ApfCapabilities mApfCapabilities;
+ private final IpManager.Callback mIpManagerCallback;
+ private final NetworkInterface mNetworkInterface;
+ private byte[] mHardwareAddress;
+ private ReceiveThread mReceiveThread;
+ @GuardedBy("this")
+ private long mUniqueCounter;
+ @GuardedBy("this")
+ private boolean mMulticastFilter;
+
+ private ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
+ IpManager.Callback ipManagerCallback) {
+ mApfCapabilities = apfCapabilities;
+ mIpManagerCallback = ipManagerCallback;
+ mNetworkInterface = networkInterface;
+
maybeStartFilter();
}
private void log(String s) {
- Log.d(TAG, "(" + mNai.network.netId + "): " + s);
+ Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s);
}
- private long getUniqueNumber() {
+ @GuardedBy("this")
+ private long getUniqueNumberLocked() {
return mUniqueCounter++;
}
@@ -118,37 +169,26 @@
* filters to ignore useless RAs.
*/
private void maybeStartFilter() {
- mIfaceName = mNai.linkProperties.getInterfaceName();
- if (mIfaceName == null) return;
FileDescriptor socket;
try {
+ mHardwareAddress = mNetworkInterface.getHardwareAddress();
+ synchronized(this) {
+ // Install basic filters
+ installNewProgramLocked();
+ }
socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
- NetworkInterface.getByName(mIfaceName).getIndex());
+ mNetworkInterface.getIndex());
Os.bind(socket, addr);
- NetworkUtils.attachRaFilter(socket, mNai.networkMisc.apfPacketFormat);
+ NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
} catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error filtering raw socket", e);
+ Log.e(TAG, "Error starting filter", e);
return;
}
mReceiveThread = new ReceiveThread(socket);
mReceiveThread.start();
}
- /**
- * mNai's LinkProperties may have changed, take appropriate action.
- */
- public void updateFilter() {
- // If we're not listening for RAs, try starting.
- if (mReceiveThread == null) {
- maybeStartFilter();
- // If interface name has changed, restart.
- } else if (!mIfaceName.equals(mNai.linkProperties.getInterfaceName())) {
- shutdown();
- maybeStartFilter();
- }
- }
-
// Returns seconds since Unix Epoch.
private static long curTime() {
return System.currentTimeMillis() / 1000L;
@@ -156,12 +196,6 @@
// A class to hold information about an RA.
private class Ra {
- private static final int ETH_HEADER_LEN = 14;
-
- private static final int IPV6_HEADER_LEN = 40;
- private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
- private static final int IPV6_DST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
-
// From RFC4861:
private static final int ICMP6_RA_HEADER_LEN = 16;
private static final int ICMP6_RA_CHECKSUM_OFFSET =
@@ -250,7 +284,7 @@
StringBuffer sb = new StringBuffer();
sb.append(String.format("RA %s -> %s %d ",
IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
- IPv6AddresstoString(IPV6_DST_ADDR_OFFSET),
+ IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
uint16(mPacket.getShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET))));
for (int i: mPrefixOptionOffsets) {
String prefix = IPv6AddresstoString(i + 16);
@@ -302,7 +336,7 @@
ICMP6_RA_ROUTER_LIFETIME_OFFSET,
ICMP6_RA_ROUTER_LIFETIME_LEN);
- // Parse ICMP6 options
+ // Parse ICMPv6 options
mPrefixOptionOffsets = new ArrayList<>();
mPacket.position(ICMP6_RA_OPTION_OFFSET);
while (mPacket.hasRemaining()) {
@@ -394,13 +428,16 @@
}
boolean isExpired() {
- return currentLifetime() < 0;
+ // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
+ // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
+ return currentLifetime() <= 0;
}
// Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
// Jump to the next filter if packet doesn't match this RA.
- long generateFilter(ApfGenerator gen) throws IllegalInstructionException {
- String nextFilterLabel = "Ra" + getUniqueNumber();
+ @GuardedBy("ApfFilter.this")
+ long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ String nextFilterLabel = "Ra" + getUniqueNumberLocked();
// Skip if packet is not the right size
gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
gen.addJumpIfR0NotEquals(mPacket.limit(), nextFilterLabel);
@@ -447,6 +484,8 @@
// Maximum number of RAs to filter for.
private static final int MAX_RAS = 10;
+
+ @GuardedBy("this")
private ArrayList<Ra> mRas = new ArrayList<Ra>();
// There is always some marginal benefit to updating the installed APF program when an RA is
@@ -460,42 +499,167 @@
private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
// When did we last install a filter program? In seconds since Unix Epoch.
+ @GuardedBy("this")
private long mLastTimeInstalledProgram;
// How long should the last installed filter program live for? In seconds.
+ @GuardedBy("this")
private long mLastInstalledProgramMinLifetime;
- // For debugging only. The length in bytes of the last program.
+ // For debugging only. The last program installed.
+ @GuardedBy("this")
private byte[] mLastInstalledProgram;
- private void installNewProgram() {
- if (mRas.size() == 0) return;
+ /**
+ * Generate filter code to process IPv4 packets. Execution of this code ends in either the
+ * DROP_LABEL or PASS_LABEL and does not fall off the end.
+ * Preconditions:
+ * - Packet being filtered is IPv4
+ * - R1 is initialized to 0
+ */
+ @GuardedBy("this")
+ private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ // Here's a basic summary of what the IPv4 filter program does:
+ //
+ // if it's multicast and we're dropping multicast:
+ // drop
+ // if it's not broadcast:
+ // pass
+ // if it's not DHCP destined to our MAC:
+ // drop
+ // pass
+
+ if (mMulticastFilter) {
+ // Check for multicast destination address range
+ gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
+ gen.addAnd(0xf0);
+ gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
+ }
+
+ // Drop all broadcasts besides DHCP addressed to us
+ // If not a broadcast packet, pass
+ // NOTE: Relies on R1 being initialized to 0 which is the offset of the ethernet
+ // destination MAC address
+ gen.addJumpIfBytesNotEqual(Register.R1, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+ // If not UDP, drop
+ gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+ gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
+ // If fragment, drop. This matches the BPF filter installed by the DHCP client.
+ gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
+ gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
+ // If not to DHCP client port, drop
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
+ // If not DHCP to our MAC address, drop
+ gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
+ // NOTE: Relies on R1 containing IPv4 header offset.
+ gen.addAddR1();
+ gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+
+ // Otherwise, pass
+ gen.addJump(gen.PASS_LABEL);
+ }
+
+
+ /**
+ * Generate filter code to process IPv6 packets. Execution of this code ends in either the
+ * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
+ * Preconditions:
+ * - Packet being filtered is IPv6
+ * - R1 is initialized to 0
+ */
+ @GuardedBy("this")
+ private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ // Here's a basic summary of what the IPv6 filter program does:
+ //
+ // if it's not ICMPv6:
+ // pass
+ // if it's ICMPv6 NA to ff02::1:
+ // drop
+
+ // If not ICMPv6, pass
+ gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
+ // TODO: Drop multicast if the multicast filter is enabled.
+ gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+ // Add unsolicited multicast neighbor announcements filter
+ String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
+ // If not neighbor announcements, skip unsolicited multicast NA filter
+ gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
+ gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
+ // If to ff02::1, drop
+ // TODO: Drop only if they don't contain the address of on-link neighbours.
+ gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
+ skipUnsolicitedMulticastNALabel);
+ gen.addJump(gen.DROP_LABEL);
+ gen.defineLabel(skipUnsolicitedMulticastNALabel);
+ }
+
+ /**
+ * Begin generating an APF program to:
+ * <ul>
+ * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
+ * <li>Drop IPv4 multicast packets, if mMulticastFilter,
+ * <li>Pass all other IPv4 packets,
+ * <li>Pass all non-ICMPv6 IPv6 packets,
+ * <li>Pass all non-IPv4 and non-IPv6 packets,
+ * <li>Drop IPv6 ICMPv6 NAs to ff02::1.
+ * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
+ * insertion of RA filters here, or if there aren't any, just passes the packets.
+ * </ul>
+ */
+ @GuardedBy("this")
+ private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
+ ApfGenerator gen = new ApfGenerator();
+ // This is guaranteed to return true because of the check in maybeCreate.
+ gen.setApfVersion(mApfCapabilities.apfVersionSupported);
+
+ // Here's a basic summary of what the initial program does:
+ //
+ // if it's IPv4:
+ // insert IPv4 filter to drop or pass these appropriately
+ // if it's not IPv6:
+ // pass
+ // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
+
+ // Add IPv4 filters:
+ String skipIPv4FiltersLabel = "skipIPv4Filters";
+ // If not IPv4, skip IPv4 filters
+ gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
+ gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
+ // NOTE: Relies on R1 being initialized to 0.
+ generateIPv4FilterLocked(gen);
+ gen.defineLabel(skipIPv4FiltersLabel);
+
+ // Add IPv6 filters:
+ // If not IPv6, pass
+ // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
+ // execute the IPv4 filter, since that filter does not fall through, but either drops or
+ // passes.
+ gen.addJumpIfR0NotEquals(ETH_P_IPV6, gen.PASS_LABEL);
+ generateIPv6FilterLocked(gen);
+ return gen;
+ }
+
+ @GuardedBy("this")
+ private void installNewProgramLocked() {
+ purgeExpiredRasLocked();
final byte[] program;
long programMinLifetime = Long.MAX_VALUE;
try {
- ApfGenerator gen = new ApfGenerator();
- // This is guaranteed to return true because of the check in maybeInstall.
- gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
// Step 1: Determine how many RA filters we can fit in the program.
- int ras = 0;
+ ApfGenerator gen = beginProgramLocked();
+ ArrayList<Ra> rasToFilter = new ArrayList<Ra>();
for (Ra ra : mRas) {
- if (ra.isExpired()) continue;
- ra.generateFilter(gen);
- if (gen.programLengthOverEstimate() > mNai.networkMisc.maximumApfProgramSize) {
- // We went too far. Use prior number of RAs in "ras".
- break;
- } else {
- // Yay! this RA filter fits, increment "ras".
- ras++;
- }
+ ra.generateFilterLocked(gen);
+ // Stop if we get too big.
+ if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break;
+ rasToFilter.add(ra);
}
- // Step 2: Generate RA filters
- gen = new ApfGenerator();
- // This is guaranteed to return true because of the check in maybeInstall.
- gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
- for (Ra ra : mRas) {
- if (ras-- == 0) break;
- if (ra.isExpired()) continue;
- programMinLifetime = Math.min(programMinLifetime, ra.generateFilter(gen));
+ // Step 2: Actually generate the program
+ gen = beginProgramLocked();
+ for (Ra ra : rasToFilter) {
+ programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
}
// Execution will reach the end of the program if no filters match, which will pass the
// packet to the AP.
@@ -509,19 +673,18 @@
mLastInstalledProgram = program;
if (VDBG) {
hexDump("Installing filter: ", program, program.length);
- } else {
- Log.d(TAG, "Installing filter length=" + program.length);
}
- mConnectivityService.pushApfProgramToNetwork(mNai, program);
+ mIpManagerCallback.installPacketFilter(program);
}
// Install a new filter program if the last installed one will die soon.
- private void maybeInstallNewProgram() {
+ @GuardedBy("this")
+ private void maybeInstallNewProgramLocked() {
if (mRas.size() == 0) return;
// If the current program doesn't expire for a while, don't bother updating.
long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) {
- installNewProgram();
+ installNewProgramLocked();
}
}
@@ -529,7 +692,19 @@
log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
}
- private void processRa(byte[] packet, int length) {
+ @GuardedBy("this")
+ private void purgeExpiredRasLocked() {
+ for (int i = 0; i < mRas.size();) {
+ if (mRas.get(i).isExpired()) {
+ log("Expiring " + mRas.get(i));
+ mRas.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ private synchronized void processRa(byte[] packet, int length) {
if (VDBG) hexDump("Read packet = ", packet, length);
// Have we seen this RA before?
@@ -551,66 +726,70 @@
// Swap to front of array.
mRas.add(0, mRas.remove(i));
- maybeInstallNewProgram();
+ maybeInstallNewProgramLocked();
return;
}
}
- // Purge expired RAs.
- for (int i = 0; i < mRas.size();) {
- if (mRas.get(i).isExpired()) {
- log("Expired RA " + mRas.get(i));
- mRas.remove(i);
- } else {
- i++;
- }
- }
+ purgeExpiredRasLocked();
// TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
if (mRas.size() >= MAX_RAS) return;
+ final Ra ra;
try {
- Ra ra = new Ra(packet, length);
- log("Adding " + ra);
- mRas.add(ra);
+ ra = new Ra(packet, length);
} catch (Exception e) {
Log.e(TAG, "Error parsing RA: " + e);
return;
}
- installNewProgram();
+ // Ignore 0 lifetime RAs.
+ if (ra.isExpired()) return;
+ log("Adding " + ra);
+ mRas.add(ra);
+ installNewProgramLocked();
}
/**
- * Install an {@link ApfFilter} on {@code nai} if {@code nai} supports packet
+ * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
* filtering using APF programs.
*/
- public static void maybeInstall(ConnectivityService connectivityService, NetworkAgentInfo nai) {
- if (nai.networkMisc == null) return;
- if (nai.networkMisc.apfVersionSupported == 0) return;
- if (nai.networkMisc.maximumApfProgramSize < 512) {
- Log.e(TAG, "Unacceptably small APF limit: " + nai.networkMisc.maximumApfProgramSize);
- return;
+ public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities,
+ NetworkInterface networkInterface, IpManager.Callback ipManagerCallback) {
+ if (apfCapabilities == null || networkInterface == null) return null;
+ if (apfCapabilities.apfVersionSupported == 0) return null;
+ if (apfCapabilities.maximumApfProgramSize < 512) {
+ Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
+ return null;
}
// For now only support generating programs for Ethernet frames. If this restriction is
// lifted:
// 1. the program generator will need its offsets adjusted.
// 2. the packet filter attached to our packet socket will need its offset adjusted.
- if (nai.networkMisc.apfPacketFormat != ARPHRD_ETHER) return;
- if (!new ApfGenerator().setApfVersion(nai.networkMisc.apfVersionSupported)) {
- Log.e(TAG, "Unsupported APF version: " + nai.networkMisc.apfVersionSupported);
- return;
+ if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
+ if (!new ApfGenerator().setApfVersion(apfCapabilities.apfVersionSupported)) {
+ Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
+ return null;
}
- nai.apfFilter = new ApfFilter(connectivityService, nai);
+ return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback);
}
- public void shutdown() {
+ public synchronized void shutdown() {
if (mReceiveThread != null) {
log("shutting down");
mReceiveThread.halt(); // Also closes socket.
mReceiveThread = null;
}
+ mRas.clear();
}
- public void dump(IndentingPrintWriter pw) {
- pw.println("APF version: " + mNai.networkMisc.apfVersionSupported);
- pw.println("Max program size: " + mNai.networkMisc.maximumApfProgramSize);
+ public synchronized void setMulticastFilter(boolean enabled) {
+ if (mMulticastFilter != enabled) {
+ mMulticastFilter = enabled;
+ installNewProgramLocked();
+ }
+ }
+
+ public synchronized void dump(IndentingPrintWriter pw) {
+ pw.println("APF version: " + mApfCapabilities.apfVersionSupported);
+ pw.println("Max program size: " + mApfCapabilities.maximumApfProgramSize);
pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
if (mLastTimeInstalledProgram == 0) {
pw.println("No program installed.");
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index e2562cd..406dd56 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -30,6 +30,8 @@
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
+import android.net.metrics.IpConnectivityEvent;
+import android.net.metrics.DhcpClientEvent;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
@@ -136,7 +138,7 @@
MessageUtils.findMessageNames(sMessageClasses);
// DHCP parameters that we request.
- private static final byte[] REQUESTED_PARAMS = new byte[] {
+ /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
DHCP_SUBNET_MASK,
DHCP_ROUTER,
DHCP_DNS_SERVER,
@@ -146,6 +148,7 @@
DHCP_LEASE_TIME,
DHCP_RENEWAL_TIME,
DHCP_REBINDING_TIME,
+ DHCP_VENDOR_INFO,
};
// DHCP flag that means "yes, we support unicast."
@@ -355,11 +358,15 @@
if (!stopped) {
Log.e(TAG, "Read error", e);
}
+ DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_RECV_ERROR,
+ mIfaceName, e.getMessage());
} catch (DhcpPacket.ParseException e) {
Log.e(TAG, "Can't parse packet: " + e.getMessage());
if (PACKET_DBG) {
Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
}
+ DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_PARSE_ERROR, mIfaceName,
+ e.getMessage());
}
}
if (DBG) Log.d(TAG, "Receive thread stopped");
@@ -456,7 +463,9 @@
abstract class LoggingState extends State {
public void enter() {
- if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
+ String msg = "Entering state " + getName();
+ if (STATE_DBG) Log.d(TAG, msg);
+ DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_STATE_CHANGE, mIfaceName, msg);
}
private String messageName(int what) {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index c7c5015..66c7909 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -19,6 +19,8 @@
import com.android.internal.util.MessageUtils;
import android.content.Context;
+import android.net.apf.ApfCapabilities;
+import android.net.apf.ApfFilter;
import android.net.DhcpResults;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -38,10 +40,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.net.NetlinkTracker;
+import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -108,6 +112,9 @@
// Called when the IpManager state machine terminates.
public void onQuit() {}
+
+ // Install an APF program to filter incoming packets.
+ public void installPacketFilter(byte[] filter) {}
}
public static class WaitForProvisioningCallback extends Callback {
@@ -179,6 +186,11 @@
return this;
}
+ public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
+ mConfig.mApfCapabilities = apfCapabilities;
+ return this;
+ }
+
public ProvisioningConfiguration build() {
return new ProvisioningConfiguration(mConfig);
}
@@ -187,6 +199,7 @@
/* package */ boolean mUsingIpReachabilityMonitor = true;
/* package */ boolean mRequestedPreDhcpAction;
/* package */ StaticIpConfiguration mStaticIpConfig;
+ /* package */ ApfCapabilities mApfCapabilities;
public ProvisioningConfiguration() {}
@@ -194,6 +207,7 @@
mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
mRequestedPreDhcpAction = other.mRequestedPreDhcpAction;
mStaticIpConfig = other.mStaticIpConfig;
+ mApfCapabilities = other.mApfCapabilities;
}
}
@@ -229,7 +243,7 @@
private final INetworkManagementService mNwService;
private final NetlinkTracker mNetlinkTracker;
- private int mInterfaceIndex;
+ private NetworkInterface mNetworkInterface;
/**
* Non-final member variables accessed only from within our StateMachine.
@@ -240,6 +254,7 @@
private DhcpResults mDhcpResults;
private String mTcpBufferSizes;
private ProxyInfo mHttpProxy;
+ private ApfFilter mApfFilter;
/**
* Member variables accessed both from within the StateMachine thread
@@ -325,7 +340,7 @@
}
public void startProvisioning(ProvisioningConfiguration req) {
- getInterfaceIndex();
+ getNetworkInterface();
sendMessage(CMD_START, new ProvisioningConfiguration(req));
}
@@ -378,6 +393,19 @@
}
}
+ public void dumpApf(PrintWriter writer) {
+ writer.println("--------------------------------------------------------------------");
+ writer.println("APF dump:");
+ // Thread-unsafe access to mApfFilter but just used for debugging.
+ ApfFilter apfFilter = mApfFilter;
+ if (apfFilter != null) {
+ apfFilter.dump(new IndentingPrintWriter(writer, " "));
+ } else {
+ writer.println("No apf support");
+ }
+ writer.println("--------------------------------------------------------------------");
+ }
+
/**
* Internals.
@@ -392,7 +420,7 @@
protected String getLogRecString(Message msg) {
final String logLine = String.format(
"iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
- mInterfaceName, mInterfaceIndex,
+ mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
msg.arg1, msg.arg2, Objects.toString(msg.obj));
if (VDBG) {
Log.d(mTag, getWhatToString(msg.what) + " " + logLine);
@@ -400,12 +428,12 @@
return logLine;
}
- private void getInterfaceIndex() {
+ private void getNetworkInterface() {
try {
- mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
+ mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
} catch (SocketException | NullPointerException e) {
// TODO: throw new IllegalStateException.
- Log.e(mTag, "ALERT: Failed to get interface index: ", e);
+ Log.e(mTag, "ALERT: Failed to get interface object: ", e);
}
}
@@ -732,6 +760,8 @@
class StartedState extends State {
@Override
public void enter() {
+ mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
+ mCallback);
// Set privacy extensions.
try {
mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
@@ -788,6 +818,11 @@
mDhcpClient.doQuit();
}
+ if (mApfFilter != null) {
+ mApfFilter.shutdown();
+ mApfFilter = null;
+ }
+
resetLinkProperties();
}
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 5b4fd50..af3175a 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -24,6 +24,8 @@
import android.net.LinkProperties.ProvisioningChange;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.metrics.IpReachabilityMonitorMessageEvent;
+import android.net.metrics.IpReachabilityMonitorProbeEvent;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.NetlinkMessage;
@@ -162,7 +164,7 @@
private boolean mRunning;
/**
- * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
+ * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
* for the given IP address on the specified interface index.
*
* @return true, if the request was successfully passed to the kernel; false otherwise.
@@ -203,7 +205,8 @@
} catch (ErrnoException | InterruptedIOException | SocketException e) {
Log.d(TAG, "Error " + msgSnippet, e);
}
-
+ IpReachabilityMonitorProbeEvent.logEvent("ifindex-" + ifIndex, ip.getHostAddress(),
+ returnValue);
return returnValue;
}
@@ -400,8 +403,7 @@
return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
}
-
- // TODO: simply the number of objects by making this extend Thread.
+ // TODO: simplify the number of objects by making this extend Thread.
private final class NetlinkSocketObserver implements Runnable {
private NetlinkSocket mSocket;
@@ -519,6 +521,8 @@
final short msgType = neighMsg.getHeader().nlmsg_type;
final short nudState = ndMsg.ndm_state;
+ IpReachabilityMonitorMessageEvent.logEvent(maybeGetInterfaceName(mInterfaceIndex),
+ destination.getHostAddress(), msgType, nudState);
final String eventMsg = "NeighborEvent{"
+ "elapsedMs=" + whenMs + ", "
+ destination.getHostAddress() + ", "
@@ -549,4 +553,11 @@
}
}
}
+
+ private String maybeGetInterfaceName(int index) {
+ if (index == mInterfaceIndex) {
+ return mInterfaceName;
+ }
+ return "ifindex-" + index;
+ }
}
diff --git a/services/net/java/android/net/metrics/CaptivePortalCheckResultEvent.java b/services/net/java/android/net/metrics/CaptivePortalCheckResultEvent.java
new file mode 100644
index 0000000..163f7e40
--- /dev/null
+++ b/services/net/java/android/net/metrics/CaptivePortalCheckResultEvent.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class CaptivePortalCheckResultEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "CaptivePortalCheckResultEvent";
+
+ private int mNetId;
+ private int mResult;
+
+ public CaptivePortalCheckResultEvent(int netId, int result) {
+ mNetId = netId;
+ mResult = result;
+ }
+
+ public CaptivePortalCheckResultEvent(Parcel in) {
+ mNetId = in.readInt();
+ mResult = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mNetId);
+ out.writeInt(mResult);
+ }
+
+ public static final Parcelable.Creator<CaptivePortalCheckResultEvent> CREATOR
+ = new Parcelable.Creator<CaptivePortalCheckResultEvent>() {
+ public CaptivePortalCheckResultEvent createFromParcel(Parcel in) {
+ return new CaptivePortalCheckResultEvent(in);
+ }
+
+ public CaptivePortalCheckResultEvent[] newArray(int size) {
+ return new CaptivePortalCheckResultEvent[size];
+ }
+ };
+
+ public static void logEvent(int netId, int result) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_NETMON_CHECK_RESULT,
+ new CaptivePortalCheckResultEvent(netId, result));
+ }
+};
diff --git a/services/net/java/android/net/metrics/CaptivePortalStateChangeEvent.java b/services/net/java/android/net/metrics/CaptivePortalStateChangeEvent.java
new file mode 100644
index 0000000..d0cc120
--- /dev/null
+++ b/services/net/java/android/net/metrics/CaptivePortalStateChangeEvent.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class CaptivePortalStateChangeEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "CaptivePortalStateChangeEvent";
+
+ public static final int NETWORK_MONITOR_CONNECTED = 0;
+ public static final int NETWORK_MONITOR_DISCONNECTED = 1;
+ public static final int NETWORK_MONITOR_VALIDATED = 2;
+ private int mState;
+
+ public CaptivePortalStateChangeEvent(int state) {
+ mState = state;
+ }
+
+ public CaptivePortalStateChangeEvent(Parcel in) {
+ mState = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mState);
+ }
+
+ public static final Parcelable.Creator<CaptivePortalStateChangeEvent> CREATOR
+ = new Parcelable.Creator<CaptivePortalStateChangeEvent>() {
+ public CaptivePortalStateChangeEvent createFromParcel(Parcel in) {
+ return new CaptivePortalStateChangeEvent(in);
+ }
+
+ public CaptivePortalStateChangeEvent[] newArray(int size) {
+ return new CaptivePortalStateChangeEvent[size];
+ }
+ };
+
+ public static void logEvent(int state) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_NETMON_STATE_CHANGE,
+ new CaptivePortalStateChangeEvent(state));
+ }
+};
diff --git a/services/net/java/android/net/metrics/ConnectivityServiceChangeEvent.java b/services/net/java/android/net/metrics/ConnectivityServiceChangeEvent.java
new file mode 100644
index 0000000..92b376c
--- /dev/null
+++ b/services/net/java/android/net/metrics/ConnectivityServiceChangeEvent.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ConnectivityServiceChangeEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "ConnectivityServiceChangeEvent";
+
+ private int mNetId;
+
+ public ConnectivityServiceChangeEvent(int netId) {
+ mNetId = netId;
+ }
+
+ public ConnectivityServiceChangeEvent(Parcel in) {
+ mNetId = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mNetId);
+ }
+
+ public static final Parcelable.Creator<ConnectivityServiceChangeEvent> CREATOR
+ = new Parcelable.Creator<ConnectivityServiceChangeEvent>() {
+ public ConnectivityServiceChangeEvent createFromParcel(Parcel in) {
+ return new ConnectivityServiceChangeEvent(in);
+ }
+
+ public ConnectivityServiceChangeEvent[] newArray(int size) {
+ return new ConnectivityServiceChangeEvent[size];
+ }
+ };
+
+ public static void logEvent(int netId) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_CONSRV_DEFAULT_NET_CHANGE,
+ new ConnectivityServiceChangeEvent(netId));
+ }
+};
diff --git a/services/net/java/android/net/metrics/DhcpClientEvent.java b/services/net/java/android/net/metrics/DhcpClientEvent.java
new file mode 100644
index 0000000..2c24034
--- /dev/null
+++ b/services/net/java/android/net/metrics/DhcpClientEvent.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class DhcpClientEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "DhcpClientEvent";
+
+ private String mIfName;
+ private String mMsg;
+
+ public DhcpClientEvent(String ifName, String msg) {
+ mIfName = ifName;
+ mMsg = msg;
+ }
+
+ public DhcpClientEvent(Parcel in) {
+ mIfName = in.readString();
+ mMsg = in.readString();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mIfName);
+ out.writeString(mMsg);
+ }
+
+ public static final Parcelable.Creator<DhcpClientEvent> CREATOR
+ = new Parcelable.Creator<DhcpClientEvent>() {
+ public DhcpClientEvent createFromParcel(Parcel in) {
+ return new DhcpClientEvent(in);
+ }
+
+ public DhcpClientEvent[] newArray(int size) {
+ return new DhcpClientEvent[size];
+ }
+ };
+
+ public static void logEvent(int eventType, String ifName, String msg) {
+ IpConnectivityEvent.logEvent(eventType, new DhcpClientEvent(ifName, msg));
+ }
+};
diff --git a/services/net/java/android/net/metrics/IpConnectivityEvent.java b/services/net/java/android/net/metrics/IpConnectivityEvent.java
new file mode 100644
index 0000000..f277bd0
--- /dev/null
+++ b/services/net/java/android/net/metrics/IpConnectivityEvent.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.net.metrics;
+
+import android.net.ConnectivityMetricsLogger;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class IpConnectivityEvent implements Parcelable {
+ // IPRM = IpReachabilityMonitor
+ // DHCP = DhcpClient
+ // NETMON = NetworkMonitorEvent
+ // CONSRV = ConnectivityServiceEvent
+ public static final String TAG = "IpConnectivityEvent";
+ public static final int IPCE_IPRM_BASE = 0*1024;
+ public static final int IPCE_DHCP_BASE = 1*1024;
+ public static final int IPCE_NETMON_BASE = 2*1024;
+ public static final int IPCE_CONSRV_BASE = 3*1024;
+
+ public static final int IPCE_IPRM_PROBE_RESULT = IPCE_IPRM_BASE + 0;
+ public static final int IPCE_IPRM_MESSAGE_RECEIVED = IPCE_IPRM_BASE + 1;
+ public static final int IPCE_DHCP_RECV_ERROR = IPCE_DHCP_BASE + 0;
+ public static final int IPCE_DHCP_PARSE_ERROR = IPCE_DHCP_BASE + 1;
+ public static final int IPCE_DHCP_TIMEOUT = IPCE_DHCP_BASE + 2;
+ public static final int IPCE_DHCP_STATE_CHANGE = IPCE_DHCP_BASE + 3;
+ public static final int IPCE_NETMON_STATE_CHANGE = IPCE_NETMON_BASE + 0;
+ public static final int IPCE_NETMON_CHECK_RESULT = IPCE_NETMON_BASE + 1;
+ public static final int IPCE_CONSRV_DEFAULT_NET_CHANGE = IPCE_CONSRV_BASE + 0;
+
+ private static ConnectivityMetricsLogger mMetricsLogger = new ConnectivityMetricsLogger();
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ }
+
+ public static void logEvent(int tag, IpConnectivityEvent event) {
+ long timestamp = System.currentTimeMillis();
+ mMetricsLogger.logEvent(timestamp, ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY,
+ tag, event);
+ }
+};
diff --git a/services/net/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java b/services/net/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java
new file mode 100644
index 0000000..a8c18d6
--- /dev/null
+++ b/services/net/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class IpReachabilityMonitorMessageEvent extends IpConnectivityEvent
+ implements Parcelable {
+ public static final String TAG = "IpReachabilityMonitorMessageEvent";
+
+ private String mIfName;
+ private String mDestination;
+ private int mMsgType;
+ private int mNudState;
+
+ public IpReachabilityMonitorMessageEvent(String ifName, String destination, int msgType,
+ int nudState) {
+ mIfName = ifName;
+ mDestination = destination;
+ mMsgType = msgType;
+ mNudState = nudState;
+ }
+
+ public IpReachabilityMonitorMessageEvent(Parcel in) {
+ mIfName = in.readString();
+ mDestination = in.readString();
+ mMsgType = in.readInt();
+ mNudState = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mIfName);
+ out.writeString(mDestination);
+ out.writeInt(mMsgType);
+ out.writeInt(mNudState);
+ }
+
+ public static final Parcelable.Creator<IpReachabilityMonitorMessageEvent> CREATOR
+ = new Parcelable.Creator<IpReachabilityMonitorMessageEvent>() {
+ public IpReachabilityMonitorMessageEvent createFromParcel(Parcel in) {
+ return new IpReachabilityMonitorMessageEvent(in);
+ }
+
+ public IpReachabilityMonitorMessageEvent[] newArray(int size) {
+ return new IpReachabilityMonitorMessageEvent[size];
+ }
+ };
+
+ public static void logEvent(String ifName, String destination, int msgType, int nudState) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_IPRM_MESSAGE_RECEIVED,
+ new IpReachabilityMonitorMessageEvent(ifName, destination, msgType, nudState));
+ }
+};
diff --git a/services/net/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java b/services/net/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java
new file mode 100644
index 0000000..172cbf8
--- /dev/null
+++ b/services/net/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class IpReachabilityMonitorProbeEvent extends IpConnectivityEvent
+ implements Parcelable {
+ public static final String TAG = "IpReachabilityMonitorProbeEvent";
+
+ private String mIfName;
+ private String mDestination;
+ private boolean mSuccess;
+
+ public IpReachabilityMonitorProbeEvent(String ifName, String destination, boolean success) {
+ mIfName = ifName;
+ mDestination = destination;
+ mSuccess = success;
+ }
+
+ public IpReachabilityMonitorProbeEvent(Parcel in) {
+ mIfName = in.readString();
+ mDestination = in.readString();
+ mSuccess = in.readByte() > 0 ? true : false;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mIfName);
+ out.writeString(mDestination);
+ out.writeByte((byte)(mSuccess ? 1 : 0));
+ }
+
+ public static final Parcelable.Creator<IpReachabilityMonitorProbeEvent> CREATOR
+ = new Parcelable.Creator<IpReachabilityMonitorProbeEvent>() {
+ public IpReachabilityMonitorProbeEvent createFromParcel(Parcel in) {
+ return new IpReachabilityMonitorProbeEvent(in);
+ }
+
+ public IpReachabilityMonitorProbeEvent[] newArray(int size) {
+ return new IpReachabilityMonitorProbeEvent[size];
+ }
+ };
+
+ public static void logEvent(String ifName, String destination, boolean success) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_IPRM_PROBE_RESULT,
+ new IpReachabilityMonitorProbeEvent(ifName, destination, success));
+ }
+};
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 9b99c67..9c3a852 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -813,6 +814,20 @@
}
@Override
+ public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
+ @NonNull CharSequence appPackageName) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ service.mSpooler.setStatus(printJobId, status, appPackageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void onPrintersAdded(ParceledListSlice printers) {
RemotePrintService service = mWeakService.get();
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index e1d8c6c..be822c8 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -301,6 +302,35 @@
}
/**
+ * Set status of a print job.
+ *
+ * @param printJobId The print job to update
+ * @param status The new status as a string resource
+ * @param appPackageName The app package name the string res belongs to
+ */
+ public final void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
+ @NonNull CharSequence appPackageName) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ mCanUnbind = false;
+ }
+ try {
+ getRemoteInstanceLazy().setStatusRes(printJobId, status, appPackageName);
+ } catch (RemoteException|TimeoutException re) {
+ Slog.e(LOG_TAG, "Error setting status.", re);
+ } finally {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()");
+ }
+ synchronized (mLock) {
+ mCanUnbind = true;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ /**
* Handle that a custom icon for a printer was loaded.
*
* @param printerId the id of the printer the icon belongs to
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index c322ab8..876d95b 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -631,18 +631,10 @@
byte[] hwaddr = {
(byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
};
- byte[] params = new byte[] {
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_LEASE_TIME,
- };
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
- false /* do unicast */, params);
+ false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
byte[] headers = new byte[] {
// Ethernet header.
@@ -650,14 +642,14 @@
(byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
(byte) 0x08, (byte) 0x00,
// IP header.
- (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x52,
+ (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
(byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
- (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x8c,
+ (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
// UDP header.
(byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
- (byte) 0x01, (byte) 0x3e, (byte) 0xd8, (byte) 0xa4,
+ (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
// BOOTP.
(byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
@@ -688,13 +680,17 @@
'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
// Requested parameter list.
- (byte) 0x37, (byte) 0x06,
+ (byte) 0x37, (byte) 0x0a,
DHCP_SUBNET_MASK,
DHCP_ROUTER,
DHCP_DNS_SERVER,
DHCP_DOMAIN_NAME,
DHCP_MTU,
+ DHCP_BROADCAST_ADDRESS,
DHCP_LEASE_TIME,
+ DHCP_RENEWAL_TIME,
+ DHCP_REBINDING_TIME,
+ DHCP_VENDOR_INFO,
// End options.
(byte) 0xff,
// Our packets are always of even length. TODO: find out why and possibly fix it.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 72a458b..622e46e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -313,7 +313,7 @@
public void testScreenChangesRules() throws Exception {
Future<Void> future;
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -322,7 +322,7 @@
verifyAndReset();
// push strict policy for foreground uid, verify ALLOW rule
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -332,7 +332,7 @@
// now turn screen off and verify REJECT rule
expect(mPowerManager.isInteractive()).andReturn(false).atLeastOnce();
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -342,7 +342,7 @@
// and turn screen back on, verify ALLOW rule restored
expect(mPowerManager.isInteractive()).andReturn(true).atLeastOnce();
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -354,7 +354,7 @@
public void testPolicyNone() throws Exception {
Future<Void> future;
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -363,7 +363,7 @@
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in foreground
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -372,7 +372,7 @@
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in background
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -385,7 +385,7 @@
Future<Void> future;
// POLICY_REJECT should RULE_ALLOW in background
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -394,7 +394,7 @@
verifyAndReset();
// POLICY_REJECT should RULE_ALLOW in foreground
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -403,7 +403,7 @@
verifyAndReset();
// POLICY_REJECT should RULE_REJECT in background
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -416,7 +416,7 @@
Future<Void> future;
// POLICY_NONE should have RULE_ALLOW in background
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -426,7 +426,7 @@
verifyAndReset();
// adding POLICY_REJECT should cause RULE_REJECT
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -435,7 +435,7 @@
verifyAndReset();
// removing POLICY_REJECT should return us to RULE_ALLOW
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -632,7 +632,7 @@
Future<Void> future;
// POLICY_REJECT should RULE_REJECT in background
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -641,7 +641,7 @@
verifyAndReset();
// uninstall should clear RULE_REJECT
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -890,9 +890,9 @@
expectLastCall().atLeastOnce();
}
- private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
+ private void expectSetUidMeteredNetworkBlacklist(int uid, boolean rejectOnQuotaInterfaces)
throws Exception {
- mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ mNetworkManager.setUidMeteredNetworkBlacklist(uid, rejectOnQuotaInterfaces);
expectLastCall().atLeastOnce();
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index afb7d93..b4c6e6a 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -250,7 +250,7 @@
* in its manifest.
* <p>
* See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
- * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ * {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
@@ -296,13 +296,13 @@
* Consider, for example, a scenario where a user has two phones with the same phone number.
* When a user places a call on one device, the telephony stack can represent that call on
* the other device by adding it to the {@link ConnectionService} with the
- * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability set.
+ * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
* <p>
* An {@link InCallService} will only see calls with this property if it has the
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
* in its manifest.
* <p>
- * See {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ * See {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
*/
public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
@@ -686,7 +686,7 @@
sb.append(", caps: ");
sb.append(capabilitiesToString(mCallCapabilities));
sb.append(", props: ");
- sb.append(mCallProperties);
+ sb.append(propertiesToString(mCallProperties));
sb.append("]");
return sb.toString();
}
@@ -813,6 +813,7 @@
private String mRemainingPostDialSequence;
private VideoCallImpl mVideoCallImpl;
private Details mDetails;
+ private Bundle mExtras;
/**
* Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
@@ -988,6 +989,89 @@
}
/**
+ * Adds some extras to this {@link Call}. Existing keys are replaced and new ones are
+ * added.
+ * <p>
+ * No assumptions should be made as to how an In-Call UI or service will handle these
+ * extras. Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
+ *
+ * @param extras The extras to add.
+ */
+ public final void putExtras(Bundle extras) {
+ if (extras == null) {
+ return;
+ }
+
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putAll(extras);
+ mInCallAdapter.putExtras(mTelecomCallId, extras);
+ }
+
+ /**
+ * Adds a boolean extra to this {@link Call}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, boolean value) {
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putBoolean(key, value);
+ mInCallAdapter.putExtra(mTelecomCallId, key, value);
+ }
+
+ /**
+ * Adds an integer extra to this {@code Connection}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, int value) {
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putInt(key, value);
+ mInCallAdapter.putExtra(mTelecomCallId, key, value);
+ }
+
+ /**
+ * Adds a string extra to this {@code Connection}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, String value) {
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putString(key, value);
+ mInCallAdapter.putExtra(mTelecomCallId, key, value);
+ }
+
+ /**
+ * Removes extras from this {@code Connection}.
+ *
+ * @param keys The keys of the extras to remove.
+ */
+ public final void removeExtras(List<String> keys) {
+ if (mExtras != null) {
+ for (String key : keys) {
+ mExtras.remove(key);
+ }
+ if (mExtras.size() == 0) {
+ mExtras = null;
+ }
+ }
+ mInCallAdapter.removeExtras(mTelecomCallId, keys);
+ }
+
+ /**
* Obtains the parent of this {@code Call} in a conference, if any.
*
* @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 1b70d65..06851ee 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,10 +16,12 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.telecom.Connection.VideoProvider;
+import android.util.ArraySet;
import java.util.ArrayList;
import java.util.Collections;
@@ -51,10 +53,13 @@
public void onDestroyed(Conference conference) {}
public void onConnectionCapabilitiesChanged(
Conference conference, int connectionCapabilities) {}
+ public void onConnectionPropertiesChanged(
+ Conference conference, int connectionProperties) {}
public void onVideoStateChanged(Conference c, int videoState) { }
public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
- public void onExtrasChanged(Conference conference, Bundle extras) {}
+ public void onExtrasChanged(Conference c, Bundle extras) {}
+ public void onExtrasRemoved(Conference c, List<String> keys) {}
}
private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -71,10 +76,12 @@
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private String mDisconnectMessage;
private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
private StatusHints mStatusHints;
private Bundle mExtras;
+ private Set<String> mPreviousExtraKeys;
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@Override
@@ -152,6 +159,16 @@
}
/**
+ * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
+ * {@link Connection} for valid values.
+ *
+ * @return A bitmask of the properties of the conference call.
+ */
+ public final int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Whether the given capabilities support the specified capability.
*
* @param capabilities A capability bit field.
@@ -360,7 +377,7 @@
* Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
* {@link Connection} for valid values.
*
- * @param connectionCapabilities A bitmask of the {@code PhoneCapabilities} of the conference call.
+ * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
*/
public final void setConnectionCapabilities(int connectionCapabilities) {
if (connectionCapabilities != mConnectionCapabilities) {
@@ -373,6 +390,22 @@
}
/**
+ * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
+ * {@link Connection} for valid values.
+ *
+ * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
+ */
+ public final void setConnectionProperties(int connectionProperties) {
+ if (connectionProperties != mConnectionProperties) {
+ mConnectionProperties = connectionProperties;
+
+ for (Listener l : mListeners) {
+ l.onConnectionPropertiesChanged(this, mConnectionProperties);
+ }
+ }
+ }
+
+ /**
* Adds the specified connection as a child of this conference.
*
* @param connection The connection to add.
@@ -640,23 +673,171 @@
}
/**
- * Set some extras that can be associated with this {@code Conference}. No assumptions should
- * be made as to how an In-Call UI or service will handle these extras.
+ * Replaces all the extras associated with this {@code Conference}.
+ * <p>
+ * New or existing keys are replaced in the {@code Conference} extras. Keys which are no longer
+ * in the new extras, but were present the last time {@code setExtras} was called are removed.
+ * <p>
+ * No assumptions should be made as to how an In-Call UI or service will handle these extras.
* Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
*
- * @param extras The extras associated with this {@code Connection}.
+ * @param extras The extras associated with this {@code Conference}.
+ * @deprecated Use {@link #putExtras(Bundle)} to add extras. Use {@link #removeExtras(List)}
+ * to remove extras.
*/
public final void setExtras(@Nullable Bundle extras) {
- mExtras = extras;
+ // Add/replace any new or changed extras values.
+ putExtras(extras);
+
+ // If we have used "setExtras" in the past, compare the key set from the last invocation to
+ // the current one and remove any keys that went away.
+ if (mPreviousExtraKeys != null) {
+ List<String> toRemove = new ArrayList<String>();
+ for (String oldKey : mPreviousExtraKeys) {
+ if (!extras.containsKey(oldKey)) {
+ toRemove.add(oldKey);
+ }
+ }
+
+ if (!toRemove.isEmpty()) {
+ removeExtras(toRemove);
+ }
+ }
+
+ // Track the keys the last time set called setExtras. This way, the next time setExtras is
+ // called we can see if the caller has removed any extras values.
+ if (mPreviousExtraKeys == null) {
+ mPreviousExtraKeys = new ArraySet<String>();
+ }
+ mPreviousExtraKeys.clear();
+ mPreviousExtraKeys.addAll(extras.keySet());
+ }
+
+ /**
+ * Adds some extras to this {@link Conference}. Existing keys are replaced and new ones are
+ * added.
+ * <p>
+ * No assumptions should be made as to how an In-Call UI or service will handle these extras.
+ * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
+ *
+ * @param extras The extras to add.
+ */
+ public final void putExtras(@NonNull Bundle extras) {
+ if (extras == null) {
+ return;
+ }
+
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putAll(extras);
+
for (Listener l : mListeners) {
l.onExtrasChanged(this, extras);
}
}
/**
- * @return The extras associated with this conference.
+ * Adds a boolean extra to this {@link Conference}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, boolean value) {
+ Bundle newExtras = new Bundle();
+ newExtras.putBoolean(key, value);
+ putExtras(newExtras);
+ }
+
+ /**
+ * Adds an integer extra to this {@link Conference}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, int value) {
+ Bundle newExtras = new Bundle();
+ newExtras.putInt(key, value);
+ putExtras(newExtras);
+ }
+
+ /**
+ * Adds a string extra to this {@link Conference}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, String value) {
+ Bundle newExtras = new Bundle();
+ newExtras.putString(key, value);
+ putExtras(newExtras);
+ }
+
+ /**
+ * Removes an extra from this {@link Conference}.
+ *
+ * @param keys The key of the extra key to remove.
+ */
+ public final void removeExtras(List<String> keys) {
+ if (keys == null || keys.isEmpty()) {
+ return;
+ }
+
+ if (mExtras != null) {
+ for (String key : keys) {
+ mExtras.remove(key);
+ }
+ if (mExtras.size() == 0) {
+ mExtras = null;
+ }
+ }
+
+ for (Listener l : mListeners) {
+ l.onExtrasRemoved(this, keys);
+ }
+ }
+
+ /**
+ * Returns the extras associated with this conference.
+ * <p>
+ * Extras should be updated using {@link #putExtras(Bundle)} and {@link #removeExtras(List)}.
+ * <p>
+ * Telecom or an {@link InCallService} can also update the extras via
+ * {@link android.telecom.Call#putExtras(Bundle)}, and
+ * {@link Call#removeExtras(List)}.
+ * <p>
+ * The conference is notified of changes to the extras made by Telecom or an
+ * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
+ *
+ * @return The extras associated with this connection.
*/
public final Bundle getExtras() {
return mExtras;
}
+
+ /**
+ * Notifies this {@link Conference} of a change to the extras made outside the
+ * {@link ConnectionService}.
+ * <p>
+ * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
+ * {@link android.telecom.Call#putExtras(Bundle)}, and
+ * {@link Call#removeExtras(List)}.
+ *
+ * @param extras The new extras bundle.
+ */
+ public void onExtrasChanged(Bundle extras) {}
+
+ /**
+ * Handles a change to extras received from Telecom.
+ *
+ * @param extras The new extras.
+ * @hide
+ */
+ final void handleExtrasChanged(Bundle extras) {
+ mExtras = extras;
+ onExtrasChanged(mExtras);
+ }
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 51a6588..310c957 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -20,6 +20,7 @@
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraManager;
@@ -30,6 +31,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.view.Surface;
import java.util.ArrayList;
@@ -96,7 +98,7 @@
* The state of an external connection which is in the process of being pulled from a remote
* device to the local device.
* <p>
- * A connection can only be in this state if the {@link #CAPABILITY_IS_EXTERNAL_CALL} and
+ * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
* {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
*/
public static final int STATE_PULLING_CALL = 7;
@@ -192,31 +194,28 @@
public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
/**
- * Whether the call is a generic conference, where we do not know the precise state of
- * participants in the conference (eg. on CDMA).
- *
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
+ public static final int CAPABILITY_UNUSED_2 = 0x00004000;
/**
- * Connection is using high definition audio.
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000;
+ public static final int CAPABILITY_UNUSED_3 = 0x00008000;
/**
- * Connection is using WIFI.
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_WIFI = 0x00010000;
+ public static final int CAPABILITY_UNUSED_4 = 0x00010000;
/**
- * Indicates that the current device callback number should be shown.
- *
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000;
+ public static final int CAPABILITY_UNUSED_5 = 0x00020000;
/**
* Speed up audio setup for MT call.
@@ -279,32 +278,64 @@
public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
/**
+ * When set for an external connection, indicates that this {@code Connection} can be pulled
+ * from a remote device to the current device.
+ * <p>
+ * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
+ * is set.
+ */
+ public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
+
+ //**********************************************************************************************
+ // Next CAPABILITY value: 0x02000000
+ //**********************************************************************************************
+
+ /**
+ * Indicates that the current device callback number should be shown.
+ *
+ * @hide
+ */
+ public static final int PROPERTY_SHOW_CALLBACK_NUMBER = 1<<0;
+
+ /**
+ * Whether the call is a generic conference, where we do not know the precise state of
+ * participants in the conference (eg. on CDMA).
+ *
+ * @hide
+ */
+ public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
+
+ /**
+ * Connection is using high definition audio.
+ * @hide
+ */
+ public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
+
+ /**
+ * Connection is using WIFI.
+ * @hide
+ */
+ public static final int PROPERTY_WIFI = 1<<3;
+
+ /**
* When set, indicates that the {@code Connection} does not actually exist locally for the
* {@link ConnectionService}.
* <p>
* Consider, for example, a scenario where a user has two devices with the same phone number.
* When a user places a call on one devices, the telephony stack can represent that call on the
* other device by adding is to the {@link ConnectionService} with the
- * {@code CAPABILITY_IS_EXTERNAL_CALL} capability set.
+ * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
* <p>
* An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
* external connections. Only those {@link InCallService}s which have the
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
* manifest will see external connections.
*/
- public static final int CAPABILITY_IS_EXTERNAL_CALL = 0x01000000;
+ public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
- /**
- * When set for an external connection, indicates that this {@code Connection} can be pulled
- * from a remote device to the current device.
- * <p>
- * Should only be set on a {@code Connection} where {@link #CAPABILITY_IS_EXTERNAL_CALL}
- * is set.
- */
- public static final int CAPABILITY_CAN_PULL_CALL = 0x02000000;
//**********************************************************************************************
- // Next CAPABILITY value: 0x04000000
+ // Next PROPERTY value: 1<<5
//**********************************************************************************************
/**
@@ -452,18 +483,6 @@
if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
}
- if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
- builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
- }
- if (can(capabilities, CAPABILITY_WIFI)) {
- builder.append(" CAPABILITY_WIFI");
- }
- if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
- builder.append(" CAPABILITY_GENERIC_CONFERENCE");
- }
- if (can(capabilities, CAPABILITY_SHOW_CALLBACK_NUMBER)) {
- builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
- }
if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
}
@@ -479,9 +498,6 @@
if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
}
- if (can(capabilities, CAPABILITY_IS_EXTERNAL_CALL)) {
- builder.append(" CAPABILITY_IS_EXTERNAL_CALL");
- }
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(" CAPABILITY_CAN_PULL_CALL");
}
@@ -490,6 +506,34 @@
return builder.toString();
}
+ public static String propertiesToString(int properties) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[Properties:");
+
+ if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
+ builder.append(" PROPERTY_SHOW_CALLBACK_NUMBER");
+ }
+
+ if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
+ builder.append(" PROPERTY_HIGH_DEF_AUDIO");
+ }
+
+ if (can(properties, PROPERTY_WIFI)) {
+ builder.append(" PROPERTY_WIFI");
+ }
+
+ if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
+ builder.append(" PROPERTY_GENERIC_CONFERENCE");
+ }
+
+ if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
+ builder.append(" PROPERTY_IS_EXTERNAL_CALL");
+ }
+
+ builder.append("]");
+ return builder.toString();
+ }
+
/** @hide */
public abstract static class Listener {
public void onStateChanged(Connection c, int state) {}
@@ -503,6 +547,7 @@
public void onRingbackRequested(Connection c, boolean ringback) {}
public void onDestroyed(Connection c) {}
public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
+ public void onConnectionPropertiesChanged(Connection c, int properties) {}
public void onVideoProviderChanged(
Connection c, VideoProvider videoProvider) {}
public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
@@ -516,6 +561,7 @@
public void onConferenceStarted() {}
public void onConferenceMergeFailed(Connection c) {}
public void onExtrasChanged(Connection c, Bundle extras) {}
+ public void onExtrasRemoved(Connection c, List<String> keys) {}
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
}
@@ -1172,6 +1218,7 @@
private int mCallerDisplayNamePresentation;
private boolean mRingbackRequested = false;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private VideoProvider mVideoProvider;
private boolean mAudioModeIsVoip;
private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
@@ -1183,6 +1230,13 @@
private Bundle mExtras;
/**
+ * Tracks the key set for the extras bundle provided on the last invocation of
+ * {@link #setExtras(Bundle)}. Used so that on subsequent invocations we can remove any extras
+ * keys which were set previously but are no longer present in the replacement Bundle.
+ */
+ private Set<String> mPreviousExtraKeys;
+
+ /**
* Create a new Connection.
*/
public Connection() {}
@@ -1318,6 +1372,17 @@
}
/**
+ * Returns the extras associated with this connection.
+ * <p>
+ * Extras should be updated using {@link #putExtras(Bundle)}.
+ * <p>
+ * Telecom or an {@link InCallService} can also update the extras via
+ * {@link android.telecom.Call#putExtras(Bundle)}, and
+ * {@link Call#removeExtras(List)}.
+ * <p>
+ * The connection is notified of changes to the extras made by Telecom or an
+ * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
+ *
* @return The extras associated with this connection.
*/
public final Bundle getExtras() {
@@ -1418,6 +1483,13 @@
}
/**
+ * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
+ */
+ public final int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Sets the value of the {@link #getAddress()} property.
*
* @param address The new address.
@@ -1614,6 +1686,21 @@
}
/**
+ * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
+ *
+ * @param connectionProperties The new connection properties.
+ */
+ public final void setConnectionProperties(int connectionProperties) {
+ checkImmutable();
+ if (mConnectionProperties != connectionProperties) {
+ mConnectionProperties = connectionProperties;
+ for (Listener l : mListeners) {
+ l.onConnectionPropertiesChanged(this, mConnectionProperties);
+ }
+ }
+ }
+
+ /**
* Tears down the Connection object.
*/
public final void destroy() {
@@ -1777,21 +1864,133 @@
}
/**
- * Set some extras that can be associated with this {@code Connection}. No assumptions should
- * be made as to how an In-Call UI or service will handle these extras.
+ * Set some extras that can be associated with this {@code Connection}.
+ * <p>
+ * New or existing keys are replaced in the {@code Connection} extras. Keys which are no longer
+ * in the new extras, but were present the last time {@code setExtras} was called are removed.
+ * <p>
+ * No assumptions should be made as to how an In-Call UI or service will handle these extras.
* Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
*
* @param extras The extras associated with this {@code Connection}.
+ * @deprecated Use {@link #putExtras(Bundle)} to add extras. Use {@link #removeExtras(List)}
+ * to remove extras.
*/
public final void setExtras(@Nullable Bundle extras) {
checkImmutable();
- mExtras = extras;
+
+ // Add/replace any new or changed extras values.
+ putExtras(extras);
+
+ // If we have used "setExtras" in the past, compare the key set from the last invocation to
+ // the current one and remove any keys that went away.
+ if (mPreviousExtraKeys != null) {
+ List<String> toRemove = new ArrayList<String>();
+ for (String oldKey : mPreviousExtraKeys) {
+ if (!extras.containsKey(oldKey)) {
+ toRemove.add(oldKey);
+ }
+ }
+ if (!toRemove.isEmpty()) {
+ removeExtras(toRemove);
+ }
+ }
+
+ // Track the keys the last time set called setExtras. This way, the next time setExtras is
+ // called we can see if the caller has removed any extras values.
+ if (mPreviousExtraKeys == null) {
+ mPreviousExtraKeys = new ArraySet<String>();
+ }
+ mPreviousExtraKeys.clear();
+ mPreviousExtraKeys.addAll(extras.keySet());
+ }
+
+ /**
+ * Adds some extras to this {@code Connection}. Existing keys are replaced and new ones are
+ * added.
+ * <p>
+ * No assumptions should be made as to how an In-Call UI or service will handle these extras.
+ * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
+ *
+ * @param extras The extras to add.
+ */
+ public final void putExtras(@NonNull Bundle extras) {
+ checkImmutable();
+ if (extras == null) {
+ return;
+ }
+
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putAll(extras);
+
for (Listener l : mListeners) {
l.onExtrasChanged(this, extras);
}
}
/**
+ * Adds a boolean extra to this {@code Connection}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, boolean value) {
+ Bundle newExtras = new Bundle();
+ newExtras.putBoolean(key, value);
+ putExtras(newExtras);
+ }
+
+ /**
+ * Adds an integer extra to this {@code Connection}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, int value) {
+ Bundle newExtras = new Bundle();
+ newExtras.putInt(key, value);
+ putExtras(newExtras);
+ }
+
+ /**
+ * Adds a string extra to this {@code Connection}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(String key, String value) {
+ Bundle newExtras = new Bundle();
+ newExtras.putString(key, value);
+ putExtras(newExtras);
+ }
+
+ /**
+ * Removes an extra from this {@code Connection}.
+ *
+ * @param keys The key of the extra key to remove.
+ */
+ public final void removeExtras(List<String> keys) {
+ if (mExtras != null) {
+ for (String key : keys) {
+ mExtras.remove(key);
+ }
+
+ if (mExtras.size() == 0) {
+ mExtras = null;
+ }
+ }
+
+ for (Listener l : mListeners) {
+ l.onExtrasRemoved(this, keys);
+ }
+ }
+
+ /**
* Notifies this Connection that the {@link #getAudioState()} property has a new value.
*
* @param state The new connection audio state.
@@ -1909,10 +2108,10 @@
* The {@link InCallService} issues a request to pull an external call to the local device via
* {@link Call#pullExternalCall()}.
* <p>
- * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL} and
- * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability bits must be set.
+ * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
+ * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
* <p>
- * For more information on external calls, see {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
*/
public void onPullExternalCall() {}
@@ -1928,6 +2127,18 @@
*/
public void onCallEvent(String event, Bundle extras) {}
+ /**
+ * Notifies this {@link Connection} of a change to the extras made outside the
+ * {@link ConnectionService}.
+ * <p>
+ * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
+ * the {@link android.telecom.Call#putExtras(Bundle)} and
+ * {@link Call#removeExtras(List)}.
+ *
+ * @param extras The new extras bundle.
+ */
+ public void onExtrasChanged(Bundle extras) {}
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
@@ -2048,6 +2259,17 @@
}
/**
+ * Handles a change to extras received from Telecom.
+ *
+ * @param extras The new extras.
+ * @hide
+ */
+ final void handleExtrasChanged(Bundle extras) {
+ mExtras = extras;
+ onExtrasChanged(mExtras);
+ }
+
+ /**
* Notifies listeners that the merge request failed.
*
* @hide
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index d18b317..4cab7f0 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -105,6 +105,7 @@
private static final int MSG_SILENCE = 21;
private static final int MSG_PULL_EXTERNAL_CALL = 22;
private static final int MSG_SEND_CALL_EVENT = 23;
+ private static final int MSG_ON_EXTRAS_CHANGED = 24;
private static Connection sNullConnection;
@@ -261,6 +262,14 @@
args.arg3 = extras;
mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget();
}
+
+ @Override
+ public void onExtrasChanged(String callId, Bundle extras) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = extras;
+ mHandler.obtainMessage(MSG_ON_EXTRAS_CHANGED, args).sendToTarget();
+ }
};
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -414,6 +423,17 @@
}
break;
}
+ case MSG_ON_EXTRAS_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ Bundle extras = (Bundle) args.arg2;
+ handleExtrasChanged(callId, extras);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
default:
break;
}
@@ -475,6 +495,16 @@
}
@Override
+ public void onConnectionPropertiesChanged(
+ Conference conference,
+ int connectionProperties) {
+ String id = mIdByConference.get(conference);
+ Log.d(this, "call capabilities: conference: %s",
+ Connection.propertiesToString(connectionProperties));
+ mAdapter.setConnectionProperties(id, connectionProperties);
+ }
+
+ @Override
public void onVideoStateChanged(Conference c, int videoState) {
String id = mIdByConference.get(c);
Log.d(this, "onVideoStateChanged set video state %d", videoState);
@@ -492,13 +522,25 @@
@Override
public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
String id = mIdByConference.get(conference);
- mAdapter.setStatusHints(id, statusHints);
+ if (id != null) {
+ mAdapter.setStatusHints(id, statusHints);
+ }
}
@Override
- public void onExtrasChanged(Conference conference, Bundle extras) {
- String id = mIdByConference.get(conference);
- mAdapter.setExtras(id, extras);
+ public void onExtrasChanged(Conference c, Bundle extras) {
+ String id = mIdByConference.get(c);
+ if (id != null) {
+ mAdapter.putExtras(id, extras);
+ }
+ }
+
+ @Override
+ public void onExtrasRemoved(Conference c, List<String> keys) {
+ String id = mIdByConference.get(c);
+ if (id != null) {
+ mAdapter.removeExtras(id, keys);
+ }
}
};
@@ -591,6 +633,14 @@
}
@Override
+ public void onConnectionPropertiesChanged(Connection c, int properties) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "properties: parcelableconnection: %s",
+ Connection.propertiesToString(properties));
+ mAdapter.setConnectionProperties(id, properties);
+ }
+
+ @Override
public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
String id = mIdByConnection.get(c);
Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
@@ -639,12 +689,20 @@
}
@Override
- public void onExtrasChanged(Connection connection, Bundle extras) {
- String id = mIdByConnection.get(connection);
+ public void onExtrasChanged(Connection c, Bundle extras) {
+ String id = mIdByConnection.get(c);
if (id != null) {
- mAdapter.setExtras(id, extras);
+ mAdapter.putExtras(id, extras);
}
}
+
+ public void onExtrasRemoved(Connection c, List<String> keys) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.removeExtras(id, keys);
+ }
+ }
+
@Override
public void onConnectionEvent(Connection connection, String event, Bundle extras) {
@@ -700,10 +758,11 @@
Uri address = connection.getAddress();
String number = address == null ? "null" : address.getSchemeSpecificPart();
- Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",
+ Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
Connection.toLogSafePhoneNumber(number),
Connection.stateToString(connection.getState()),
- Connection.capabilitiesToString(connection.getConnectionCapabilities()));
+ Connection.capabilitiesToString(connection.getConnectionCapabilities()),
+ Connection.propertiesToString(connection.getConnectionProperties()));
Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
mAdapter.handleCreateConnectionComplete(
@@ -713,6 +772,7 @@
request.getAccountHandle(),
connection.getState(),
connection.getConnectionCapabilities(),
+ connection.getConnectionProperties(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
@@ -929,6 +989,27 @@
}
+ /**
+ * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom.
+ * <p>
+ * These extra changes can originate from Telecom itself, or from an {@link InCallService} via
+ * the {@link android.telecom.Call#putExtra(String, boolean)},
+ * {@link android.telecom.Call#putExtra(String, int)},
+ * {@link android.telecom.Call#putExtra(String, String)},
+ * {@link Call#removeExtras(List)}.
+ *
+ * @param callId The ID of the call receiving the event.
+ * @param extras The new extras bundle.
+ */
+ private void handleExtrasChanged(String callId, Bundle extras) {
+ Log.d(this, "handleExtrasChanged(%s, %s)", callId, extras);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);
+ } else if (mConferenceById.containsKey(callId)) {
+ findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);
+ }
+ }
+
private void onPostDialContinue(String callId, boolean proceed) {
Log.d(this, "onPostDialContinue(%s)", callId);
findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
@@ -1049,6 +1130,7 @@
conference.getPhoneAccountHandle(),
conference.getState(),
conference.getConnectionCapabilities(),
+ conference.getConnectionProperties(),
connectionIds,
conference.getVideoProvider() == null ?
null : conference.getVideoProvider().getInterface(),
@@ -1089,6 +1171,7 @@
phoneAccountHandle,
connection.getState(),
connection.getConnectionCapabilities(),
+ connection.getConnectionProperties(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index e91128f..c8cd3c0 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -196,6 +196,15 @@
}
}
+ void setConnectionProperties(String callId, int properties) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setConnectionProperties(callId, properties);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
/**
* Indicates whether or not the specified call is currently conferenced into the specified
* conference call.
@@ -398,16 +407,88 @@
}
/**
- * Sets extras associated with a connection.
+ * Adds some extras associated with a {@code Connection}.
*
* @param callId The unique ID of the call.
- * @param extras The extras to associate with this call.
+ * @param extras The extras to add.
*/
- void setExtras(String callId, Bundle extras) {
- Log.v(this, "setExtras: %s", extras);
+ void putExtras(String callId, Bundle extras) {
+ Log.v(this, "putExtras: %s", callId);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setExtras(callId, extras);
+ adapter.putExtras(callId, extras);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Adds an extra associated with a {@code Connection}.
+ *
+ * @param callId The unique ID of the call.
+ * @param key The extra key.
+ * @param value The extra value.
+ */
+ void putExtra(String callId, String key, boolean value) {
+ Log.v(this, "putExtra: %s %s=%b", callId, key, value);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(key, value);
+ adapter.putExtras(callId, bundle);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Adds an extra associated with a {@code Connection}.
+ *
+ * @param callId The unique ID of the call.
+ * @param key The extra key.
+ * @param value The extra value.
+ */
+ void putExtra(String callId, String key, int value) {
+ Log.v(this, "putExtra: %s %s=%d", callId, key, value);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putInt(key, value);
+ adapter.putExtras(callId, bundle);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Adds an extra associated with a {@code Connection}.
+ *
+ * @param callId The unique ID of the call.
+ * @param key The extra key.
+ * @param value The extra value.
+ */
+ void putExtra(String callId, String key, String value) {
+ Log.v(this, "putExtra: %s %s=%s", callId, key, value);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putString(key, value);
+ adapter.putExtras(callId, bundle);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Removes extras associated with a {@code Connection}.
+ * @param callId The unique ID of the call.
+ * @param keys The extra keys to remove.
+ */
+ void removeExtras(String callId, List<String> keys) {
+ Log.v(this, "removeExtras: %s %s", callId, keys);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.removeExtras(callId, keys);
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 4b15e54..bf28feb 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -61,8 +61,10 @@
private static final int MSG_ADD_EXISTING_CONNECTION = 21;
private static final int MSG_ON_POST_DIAL_CHAR = 22;
private static final int MSG_SET_CONFERENCE_MERGE_FAILED = 23;
- private static final int MSG_SET_EXTRAS = 24;
- private static final int MSG_ON_CONNECTION_EVENT = 25;
+ private static final int MSG_PUT_EXTRAS = 24;
+ private static final int MSG_REMOVE_EXTRAS = 25;
+ private static final int MSG_ON_CONNECTION_EVENT = 26;
+ private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
private final IConnectionServiceAdapter mDelegate;
@@ -117,6 +119,9 @@
case MSG_SET_CONNECTION_CAPABILITIES:
mDelegate.setConnectionCapabilities((String) msg.obj, msg.arg1);
break;
+ case MSG_SET_CONNECTION_PROPERTIES:
+ mDelegate.setConnectionProperties((String) msg.obj, msg.arg1);
+ break;
case MSG_SET_IS_CONFERENCED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -233,10 +238,19 @@
}
break;
}
- case MSG_SET_EXTRAS: {
+ case MSG_PUT_EXTRAS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setExtras((String) args.arg1, (Bundle) args.arg2);
+ mDelegate.putExtras((String) args.arg1, (Bundle) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_REMOVE_EXTRAS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.removeExtras((String) args.arg1, (List<String>) args.arg2);
} finally {
args.recycle();
}
@@ -312,6 +326,13 @@
}
@Override
+ public void setConnectionProperties(String connectionId, int connectionProperties) {
+ mHandler.obtainMessage(
+ MSG_SET_CONNECTION_PROPERTIES, connectionProperties, 0, connectionId)
+ .sendToTarget();
+ }
+
+ @Override
public void setConferenceMergeFailed(String callId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
@@ -425,11 +446,19 @@
}
@Override
- public final void setExtras(String connectionId, Bundle extras) {
+ public final void putExtras(String connectionId, Bundle extras) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = extras;
- mHandler.obtainMessage(MSG_SET_EXTRAS, args).sendToTarget();
+ mHandler.obtainMessage(MSG_PUT_EXTRAS, args).sendToTarget();
+ }
+
+ @Override
+ public final void removeExtras(String connectionId, List<String> keys) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = keys;
+ mHandler.obtainMessage(MSG_REMOVE_EXTRAS, args).sendToTarget();
}
@Override
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 52ef4a7..3f270d9 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -21,6 +21,8 @@
import com.android.internal.telecom.IInCallAdapter;
+import java.util.List;
+
/**
* Receives commands from {@link InCallService} implementations which should be executed by
* Telecom. When Telecom binds to a {@link InCallService}, an instance of this class is given to
@@ -278,6 +280,79 @@
}
/**
+ * Intructs Telecom to add extras to a call.
+ *
+ * @param callId The callId to add the extras to.
+ * @param extras The extras.
+ */
+ public void putExtras(String callId, Bundle extras) {
+ try {
+ mAdapter.putExtras(callId, extras);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Intructs Telecom to add an extra to a call.
+ *
+ * @param callId The callId to add the extras to.
+ * @param key The extra key.
+ * @param value The extra value.
+ */
+ public void putExtra(String callId, String key, boolean value) {
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(key, value);
+ mAdapter.putExtras(callId, bundle);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Intructs Telecom to add an extra to a call.
+ *
+ * @param callId The callId to add the extras to.
+ * @param key The extra key.
+ * @param value The extra value.
+ */
+ public void putExtra(String callId, String key, int value) {
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putInt(key, value);
+ mAdapter.putExtras(callId, bundle);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Intructs Telecom to add an extra to a call.
+ *
+ * @param callId The callId to add the extras to.
+ * @param key The extra key.
+ * @param value The extra value.
+ */
+ public void putExtra(String callId, String key, String value) {
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putString(key, value);
+ mAdapter.putExtras(callId, bundle);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Intructs Telecom to remove extras from a call.
+ * @param callId The callId to remove the extras from.
+ * @param keys The extra keys to remove.
+ */
+ public void removeExtras(String callId, List<String> keys) {
+ try {
+ mAdapter.removeExtras(callId, keys);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
* Instructs Telecom to turn the proximity sensor on.
*/
public void turnProximitySensorOn() {
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index 870f5ee..f5689d8 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -34,6 +34,7 @@
private PhoneAccountHandle mPhoneAccount;
private int mState;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private List<String> mConnectionIds;
private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
private final IVideoProvider mVideoProvider;
@@ -45,6 +46,7 @@
PhoneAccountHandle phoneAccount,
int state,
int connectionCapabilities,
+ int connectionProperties,
List<String> connectionIds,
IVideoProvider videoProvider,
int videoState,
@@ -54,6 +56,7 @@
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = connectionCapabilities;
+ mConnectionProperties = connectionProperties;
mConnectionIds = connectionIds;
mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
mVideoProvider = videoProvider;
@@ -72,6 +75,8 @@
.append(Connection.stateToString(mState))
.append(", capabilities: ")
.append(Connection.capabilitiesToString(mConnectionCapabilities))
+ .append(", properties: ")
+ .append(Connection.propertiesToString(mConnectionProperties))
.append(", connectTime: ")
.append(mConnectTimeMillis)
.append(", children: ")
@@ -95,6 +100,10 @@
return mConnectionCapabilities;
}
+ public int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
public List<String> getConnectionIds() {
return mConnectionIds;
}
@@ -134,9 +143,11 @@
int videoState = source.readInt();
StatusHints statusHints = source.readParcelable(classLoader);
Bundle extras = source.readBundle(classLoader);
+ int properties = source.readInt();
- return new ParcelableConference(phoneAccount, state, capabilities, connectionIds,
- videoCallProvider, videoState, connectTimeMillis, statusHints, extras);
+ return new ParcelableConference(phoneAccount, state, capabilities, properties,
+ connectionIds, videoCallProvider, videoState, connectTimeMillis, statusHints,
+ extras);
}
@Override
@@ -164,5 +175,6 @@
destination.writeInt(mVideoState);
destination.writeParcelable(mStatusHints, 0);
destination.writeBundle(mExtras);
+ destination.writeInt(mConnectionProperties);
}
}
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index ce51c96..540f388 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -36,6 +36,7 @@
private final PhoneAccountHandle mPhoneAccount;
private final int mState;
private final int mConnectionCapabilities;
+ private final int mConnectionProperties;
private final Uri mAddress;
private final int mAddressPresentation;
private final String mCallerDisplayName;
@@ -55,6 +56,7 @@
PhoneAccountHandle phoneAccount,
int state,
int capabilities,
+ int properties,
Uri address,
int addressPresentation,
String callerDisplayName,
@@ -71,6 +73,7 @@
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = capabilities;
+ mConnectionProperties = properties;
mAddress = address;
mAddressPresentation = addressPresentation;
mCallerDisplayName = callerDisplayName;
@@ -94,11 +97,26 @@
return mState;
}
- // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
+ /**
+ * Returns the current connection capabilities bit-mask. Connection capabilities are defined as
+ * {@code CAPABILITY_*} constants in {@link Connection}.
+ *
+ * @return Bit-mask containing capabilities of the connection.
+ */
public int getConnectionCapabilities() {
return mConnectionCapabilities;
}
+ /**
+ * Returns the current connection properties bit-mask. Connection properties are defined as
+ * {@code PROPERTY_*} constants in {@link Connection}.
+ *
+ * @return Bit-mask containing properties of the connection.
+ */
+ public int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
public Uri getHandle() {
return mAddress;
}
@@ -160,6 +178,8 @@
.append(mState)
.append(", capabilities:")
.append(Connection.capabilitiesToString(mConnectionCapabilities))
+ .append(", properties:")
+ .append(Connection.propertiesToString(mConnectionProperties))
.append(", extras:")
.append(mExtras)
.toString();
@@ -189,11 +209,13 @@
List<String> conferenceableConnectionIds = new ArrayList<>();
source.readStringList(conferenceableConnectionIds);
Bundle extras = Bundle.setDefusable(source.readBundle(classLoader), true);
+ int properties = source.readInt();
return new ParcelableConnection(
phoneAccount,
state,
capabilities,
+ properties,
address,
addressPresentation,
callerDisplayName,
@@ -241,5 +263,6 @@
destination.writeParcelable(mDisconnectCause, 0);
destination.writeStringList(mConferenceableConnectionIds);
destination.writeBundle(mExtras);
+ destination.writeInt(mConnectionProperties);
}
}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index ae5cd46..943da6d 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -92,6 +92,18 @@
int connectionCapabilities) {}
/**
+ * Indicates that the call properties of this {@code RemoteConference} have changed.
+ * See {@link #getConnectionProperties()}.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param connectionProperties The new properties of the {@code RemoteConference}.
+ */
+ public void onConnectionPropertiesChanged(
+ RemoteConference conference,
+ int connectionProperties) {}
+
+
+ /**
* Invoked when the set of {@link RemoteConnection}s which can be added to this conference
* call have changed.
*
@@ -133,6 +145,7 @@
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private Bundle mExtras;
/** @hide */
@@ -244,6 +257,24 @@
}
/** @hide */
+ void setConnectionProperties(final int connectionProperties) {
+ if (mConnectionProperties != connectionProperties) {
+ mConnectionProperties = connectionProperties;
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionPropertiesChanged(
+ conference, mConnectionProperties);
+ }
+ });
+ }
+ }
+ }
+
+ /** @hide */
void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
mConferenceableConnections.clear();
mConferenceableConnections.addAll(conferenceableConnections);
@@ -279,15 +310,35 @@
}
/** @hide */
- void setExtras(final Bundle extras) {
- mExtras = extras;
+ void putExtras(final Bundle extras) {
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putAll(extras);
+
+ notifyExtrasChanged();
+ }
+
+ /** @hide */
+ void removeExtras(List<String> keys) {
+ if (mExtras == null || keys == null || keys.isEmpty()) {
+ return;
+ }
+ for (String key : keys) {
+ mExtras.remove(key);
+ }
+
+ notifyExtrasChanged();
+ }
+
+ private void notifyExtrasChanged() {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final RemoteConference conference = this;
final Callback callback = record.getCallback();
record.getHandler().post(new Runnable() {
@Override
public void run() {
- callback.onExtrasChanged(conference, extras);
+ callback.onExtrasChanged(conference, mExtras);
}
});
}
@@ -322,6 +373,16 @@
}
/**
+ * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
+ * {@link Connection} for valid values.
+ *
+ * @return A bitmask of the properties of the conference call.
+ */
+ public final int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Obtain the extras associated with this {@code RemoteConnection}.
*
* @return The extras for this connection.
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 5b602eb..dc8eaf6 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -90,6 +90,17 @@
int connectionCapabilities) {}
/**
+ * Indicates that the call properties of this {@code RemoteConnection} have changed.
+ * See {@link #getConnectionProperties()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param connectionProperties The new properties of the {@code RemoteConnection}.
+ */
+ public void onConnectionPropertiesChanged(
+ RemoteConnection connection,
+ int connectionProperties) {}
+
+ /**
* Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
* pause character. This causes the post-dial signals to stop pending user confirmation. An
* implementation should present this choice to the user and invoke
@@ -588,6 +599,7 @@
private boolean mRingbackRequested;
private boolean mConnected;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private int mVideoState;
private VideoProvider mVideoProvider;
private boolean mIsVoipAudioMode;
@@ -624,6 +636,7 @@
mDisconnectCause = connection.getDisconnectCause();
mRingbackRequested = connection.isRingbackRequested();
mConnectionCapabilities = connection.getConnectionCapabilities();
+ mConnectionProperties = connection.getConnectionProperties();
mVideoState = connection.getVideoState();
mVideoProvider = new RemoteConnection.VideoProvider(connection.getVideoProvider());
mIsVoipAudioMode = connection.getIsVoipAudioMode();
@@ -719,6 +732,16 @@
}
/**
+ * Obtains the properties of this {@code RemoteConnection}.
+ *
+ * @return A bitmask of the properties of the {@code RemoteConnection}, as defined in the
+ * {@code PROPERTY_*} constants in class {@link Connection}.
+ */
+ public int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Determines if the audio mode of this {@code RemoteConnection} is VOIP.
*
* @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
@@ -1114,6 +1137,23 @@
/**
* @hide
*/
+ void setConnectionProperties(final int connectionProperties) {
+ mConnectionProperties = connectionProperties;
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionPropertiesChanged(connection, connectionProperties);
+ }
+ });
+ }
+ }
+
+ /**
+ * @hide
+ */
void setDestroyed() {
if (!mCallbackRecords.isEmpty()) {
// Make sure that the callbacks are notified that the call is destroyed first.
@@ -1302,15 +1342,35 @@
}
/** @hide */
- void setExtras(final Bundle extras) {
- mExtras = extras;
+ void putExtras(final Bundle extras) {
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putAll(extras);
+
+ notifyExtrasChanged();
+ }
+
+ /** @hide */
+ void removeExtras(List<String> keys) {
+ if (mExtras == null || keys == null || keys.isEmpty()) {
+ return;
+ }
+ for (String key : keys) {
+ mExtras.remove(key);
+ }
+
+ notifyExtrasChanged();
+ }
+
+ private void notifyExtrasChanged() {
for (CallbackRecord record : mCallbackRecords) {
final RemoteConnection connection = this;
final Callback callback = record.getCallback();
record.getHandler().post(new Runnable() {
@Override
public void run() {
- callback.onExtrasChanged(connection, extras);
+ callback.onExtrasChanged(connection, mExtras);
}
});
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index fa7183a..21a7706 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -61,6 +61,7 @@
mPendingConnections.remove(connection);
// Unconditionally initialize the connection ...
connection.setConnectionCapabilities(parcel.getConnectionCapabilities());
+ connection.setConnectionProperties(parcel.getConnectionProperties());
if (parcel.getHandle() != null
|| parcel.getState() != Connection.STATE_DISCONNECTED) {
connection.setAddress(parcel.getHandle(), parcel.getHandlePresentation());
@@ -156,6 +157,17 @@
}
@Override
+ public void setConnectionProperties(String callId, int connectionProperties) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "setConnectionProperties")
+ .setConnectionProperties(connectionProperties);
+ } else {
+ findConferenceForAction(callId, "setConnectionProperties")
+ .setConnectionProperties(connectionProperties);
+ }
+ }
+
+ @Override
public void setIsConferenced(String callId, String conferenceCallId) {
// Note: callId should not be null; conferenceCallId may be null
RemoteConnection connection =
@@ -321,13 +333,20 @@
}
@Override
- public void setExtras(String callId, Bundle extras) {
- if (mConnectionById.containsKey(callId)) {
- findConnectionForAction(callId, "setExtras")
- .setExtras(extras);
+ public void putExtras(String callId, Bundle extras) {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "putExtras").putExtras(extras);
} else {
- findConferenceForAction(callId, "setExtras")
- .setExtras(extras);
+ findConferenceForAction(callId, "putExtras").putExtras(extras);
+ }
+ }
+
+ @Override
+ public void removeExtras(String callId, List<String> keys) {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "removeExtra").removeExtras(keys);
+ } else {
+ findConferenceForAction(callId, "removeExtra").removeExtras(keys);
}
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 3ee0e9f..a4c1798 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -79,4 +79,6 @@
void pullExternalCall(String callId);
void sendCallEvent(String callId, String event, in Bundle extras);
+
+ void onExtrasChanged(String callId, in Bundle extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index dff1b11..9bc8ffe 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -55,6 +55,8 @@
void setConnectionCapabilities(String callId, int connectionCapabilities);
+ void setConnectionProperties(String callId, int connectionProperties);
+
void setIsConferenced(String callId, String conferenceCallId);
void setConferenceMergeFailed(String callId);
@@ -85,7 +87,9 @@
void addExistingConnection(String callId, in ParcelableConnection connection);
- void setExtras(String callId, in Bundle extras);
+ void putExtras(String callId, in Bundle extras);
+
+ void removeExtras(String callId, in List<String> keys);
void onConnectionEvent(String callId, String event, in Bundle extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 0678fe2..49f9b3b 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -65,4 +65,8 @@
void pullExternalCall(String callId);
void sendCallEvent(String callId, String event, in Bundle extras);
+
+ void putExtras(String callId, in Bundle extras);
+
+ void removeExtras(String callId, in List<String> keys);
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index ae130d4..bb2b447 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -32,6 +32,7 @@
import com.android.internal.telephony.IPhoneStateListener;
import java.util.List;
+import java.lang.ref.WeakReference;
/**
* A listener class for monitoring changes in specific telephony states
@@ -533,84 +534,101 @@
/**
* The callback methods need to be called on the handler thread where
* this object was created. If the binder did that for us it'd be nice.
+ *
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * IPhoneStateListener.Stub callback retaining references to the outside PhoneStateListeners:
+ * even caller has been destroyed and "un-registered" the PhoneStateListener, it is still not
+ * eligible for GC given the references coming from:
+ * Native Stack --> PhoneStateListener --> Context (Activity).
+ * memory of caller's context will be collected after GC from service side get triggered
*/
- IPhoneStateListener callback = new IPhoneStateListener.Stub() {
+ private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
+ private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;
+
+ public IPhoneStateListenerStub(PhoneStateListener phoneStateListener) {
+ mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
+ }
+
+ private void send(int what, int arg1, int arg2, Object obj) {
+ PhoneStateListener listener = mPhoneStateListenerWeakRef.get();
+ if (listener != null) {
+ Message.obtain(listener.mHandler, what, arg1, arg2, obj).sendToTarget();
+ }
+ }
+
public void onServiceStateChanged(ServiceState serviceState) {
- Message.obtain(mHandler, LISTEN_SERVICE_STATE, 0, 0, serviceState).sendToTarget();
+ send(LISTEN_SERVICE_STATE, 0, 0, serviceState);
}
public void onSignalStrengthChanged(int asu) {
- Message.obtain(mHandler, LISTEN_SIGNAL_STRENGTH, asu, 0, null).sendToTarget();
+ send(LISTEN_SIGNAL_STRENGTH, asu, 0, null);
}
public void onMessageWaitingIndicatorChanged(boolean mwi) {
- Message.obtain(mHandler, LISTEN_MESSAGE_WAITING_INDICATOR, mwi ? 1 : 0, 0, null)
- .sendToTarget();
+ send(LISTEN_MESSAGE_WAITING_INDICATOR, mwi ? 1 : 0, 0, null);
}
public void onCallForwardingIndicatorChanged(boolean cfi) {
- Message.obtain(mHandler, LISTEN_CALL_FORWARDING_INDICATOR, cfi ? 1 : 0, 0, null)
- .sendToTarget();
+ send(LISTEN_CALL_FORWARDING_INDICATOR, cfi ? 1 : 0, 0, null);
}
public void onCellLocationChanged(Bundle bundle) {
CellLocation location = CellLocation.newFromBundle(bundle);
- Message.obtain(mHandler, LISTEN_CELL_LOCATION, 0, 0, location).sendToTarget();
+ send(LISTEN_CELL_LOCATION, 0, 0, location);
}
public void onCallStateChanged(int state, String incomingNumber) {
- Message.obtain(mHandler, LISTEN_CALL_STATE, state, 0, incomingNumber).sendToTarget();
+ send(LISTEN_CALL_STATE, state, 0, incomingNumber);
}
public void onDataConnectionStateChanged(int state, int networkType) {
- Message.obtain(mHandler, LISTEN_DATA_CONNECTION_STATE, state, networkType).
- sendToTarget();
+ send(LISTEN_DATA_CONNECTION_STATE, state, networkType, null);
}
public void onDataActivity(int direction) {
- Message.obtain(mHandler, LISTEN_DATA_ACTIVITY, direction, 0, null).sendToTarget();
+ send(LISTEN_DATA_ACTIVITY, direction, 0, null);
}
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- Message.obtain(mHandler, LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength).sendToTarget();
+ send(LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength);
}
public void onOtaspChanged(int otaspMode) {
- Message.obtain(mHandler, LISTEN_OTASP_CHANGED, otaspMode, 0).sendToTarget();
+ send(LISTEN_OTASP_CHANGED, otaspMode, 0, null);
}
public void onCellInfoChanged(List<CellInfo> cellInfo) {
- Message.obtain(mHandler, LISTEN_CELL_INFO, 0, 0, cellInfo).sendToTarget();
+ send(LISTEN_CELL_INFO, 0, 0, cellInfo);
}
public void onPreciseCallStateChanged(PreciseCallState callState) {
- Message.obtain(mHandler, LISTEN_PRECISE_CALL_STATE, 0, 0, callState).sendToTarget();
+ send(LISTEN_PRECISE_CALL_STATE, 0, 0, callState);
}
public void onPreciseDataConnectionStateChanged(
PreciseDataConnectionState dataConnectionState) {
- Message.obtain(mHandler, LISTEN_PRECISE_DATA_CONNECTION_STATE, 0, 0,
- dataConnectionState).sendToTarget();
+ send(LISTEN_PRECISE_DATA_CONNECTION_STATE, 0, 0, dataConnectionState);
}
public void onDataConnectionRealTimeInfoChanged(
DataConnectionRealTimeInfo dcRtInfo) {
- Message.obtain(mHandler, LISTEN_DATA_CONNECTION_REAL_TIME_INFO, 0, 0,
- dcRtInfo).sendToTarget();
+ send(LISTEN_DATA_CONNECTION_REAL_TIME_INFO, 0, 0, dcRtInfo);
}
public void onVoLteServiceStateChanged(VoLteServiceState lteState) {
- Message.obtain(mHandler, LISTEN_VOLTE_STATE, 0, 0, lteState).sendToTarget();
+ send(LISTEN_VOLTE_STATE, 0, 0, lteState);
}
public void onOemHookRawEvent(byte[] rawData) {
- Message.obtain(mHandler, LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData).sendToTarget();
+ send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
}
public void onCarrierNetworkChange(boolean active) {
- Message.obtain(mHandler, LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active).sendToTarget();
+ send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
- };
+ }
+
+ IPhoneStateListener callback = new IPhoneStateListenerStub(this);
private void log(String s) {
Rlog.d(LOG_TAG, s);
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 96c6243..303746c 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -201,7 +201,7 @@
* "14" vs (int) 14).
* Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
* updateWifiStateFromExtras(Bundle)} to determine whether to set the
- * {@link android.telecom.Connection#CAPABILITY_WIFI} capability on a connection.
+ * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
*/
public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.java b/telephony/java/com/android/ims/ImsExternalCallState.java
index edb6bfc..71c1837 100644
--- a/telephony/java/com/android/ims/ImsExternalCallState.java
+++ b/telephony/java/com/android/ims/ImsExternalCallState.java
@@ -28,7 +28,7 @@
*/
/**
- * Parcelable object to handle VICE Dialog Information
+ * Parcelable object to handle MultiEndpoint Dialog Information
* @hide
*/
public class ImsExternalCallState implements Parcelable {
@@ -39,19 +39,30 @@
public static final int CALL_STATE_CONFIRMED = 1;
public static final int CALL_STATE_TERMINATED = 2;
// Dialog Id
- public int mCallId;
+ private int mCallId;
// Number
- public Uri mAddress;
- public boolean mIsPullable;
+ private Uri mAddress;
+ private boolean mIsPullable;
// CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
- public int mCallState;
+ private int mCallState;
// ImsCallProfile#CALL_TYPE_*
- public int mCallType;
- public boolean mIsHeld;
+ private int mCallType;
+ private boolean mIsHeld;
public ImsExternalCallState() {
}
+ public ImsExternalCallState(int callId, Uri address, boolean isPullable, int callState,
+ int callType, boolean isCallheld) {
+ mCallId = callId;
+ mAddress = address;
+ mIsPullable = isPullable;
+ mCallState = callState;
+ mCallType = callType;
+ mIsHeld = isCallheld;
+ Rlog.d(TAG, "ImsExternalCallState = " + this);
+ }
+
public ImsExternalCallState(Parcel in) {
mCallId = in.readInt();
ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
@@ -60,12 +71,7 @@
mCallState = in.readInt();
mCallType = in.readInt();
mIsHeld = (in.readInt() != 0);
- Rlog.d(TAG, "ImsExternalCallState const = " +
- "callid = " + getCallId() +
- ", address = " + getAddress() +
- ", mCallState = " + getCallState() +
- ", calltype = " + getCallType() +
- ", isheld = " + isCallHeld());
+ Rlog.d(TAG, "ImsExternalCallState const = " + this);
}
@Override
@@ -81,6 +87,7 @@
out.writeInt(mCallState);
out.writeInt(mCallType);
out.writeInt(mIsHeld ? 1 : 0);
+ Rlog.d(TAG, "ImsExternalCallState writeToParcel = " + out.toString());
}
public static final Parcelable.Creator<ImsExternalCallState> CREATOR =
diff --git a/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
index 70a474e..27b8fa1 100644
--- a/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
@@ -32,7 +32,7 @@
*
* @return void.
*/
- void notifyRefreshExternalCallState(in List<ImsExternalCallState> externalCallDialogs);
+ void onImsExternalCallStateUpdate(in List<ImsExternalCallState> externalCallDialogs);
}
diff --git a/telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl b/telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl
index 1bfb9b2..1374caa 100644
--- a/telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl
+++ b/telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl
@@ -34,5 +34,5 @@
* Query api to get the latest Dialog Event Package information
* Should be invoked only after setListener is done
*/
- void requestDialogEventPackageState();
+ void requestImsExternalCallStateInfo();
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index ea3b5c9..6567ea7 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -218,11 +218,4 @@
*/
static final String PROPERTY_VIDEOCALL_AUDIO_OUTPUT = "persist.radio.call.audio.output";
- /**
- * For MultiEndpoint Feature
- * If true: Dial intent is for call pull functionality
- * if false: normal dial
- */
- static final String EXTRA_IS_CALL_PULL =
- "android.telephony.extra.IS_CALL_PULL";
}
diff --git a/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml
new file mode 100644
index 0000000..5d23d36e
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com
+ </domain>
+ <domain> developer.android.com </domain>
+ <pin-set>
+ <pin digest="SHA-256"> 7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y= </pin>
+ </pin-set>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
index 4c12c2d..0412bc7 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -65,4 +65,9 @@
}
return certs;
}
+
+ @Override
+ public void handleTrustStorageUpdate() {
+ // Nothing to do.
+ }
}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index 10bcc18..f7066a6 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -464,4 +464,16 @@
} catch (RuntimeException expected) {
}
}
+
+ public void testDomainWhitespaceTrimming() throws Exception {
+ XmlConfigSource source =
+ new XmlConfigSource(getContext(), R.xml.domain_whitespace, false);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig defaultConfig = appConfig.getConfigForHostname("");
+ MoreAsserts.assertNotEqual(defaultConfig, appConfig.getConfigForHostname("developer.android.com"));
+ MoreAsserts.assertNotEqual(defaultConfig, appConfig.getConfigForHostname("android.com"));
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
}
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index a848346..75e837b 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -9,17 +9,37 @@
from fontTools import ttLib
LANG_TO_SCRIPT = {
+ 'as': 'Beng',
+ 'bn': 'Beng',
+ 'cy': 'Latn',
+ 'da': 'Latn',
'de': 'Latn',
'en': 'Latn',
'es': 'Latn',
+ 'et': 'Latn',
'eu': 'Latn',
- 'ja': 'Jpan',
- 'ko': 'Kore',
+ 'fr': 'Latn',
+ 'ga': 'Latn',
+ 'gu': 'Gujr',
+ 'hi': 'Deva',
+ 'hr': 'Latn',
'hu': 'Latn',
'hy': 'Armn',
+ 'ja': 'Jpan',
+ 'kn': 'Knda',
+ 'ko': 'Kore',
+ 'ml': 'Mlym',
+ 'mn': 'Cyrl',
+ 'mr': 'Deva',
'nb': 'Latn',
'nn': 'Latn',
+ 'or': 'Orya',
+ 'pa': 'Guru',
'pt': 'Latn',
+ 'sl': 'Latn',
+ 'ta': 'Taml',
+ 'te': 'Telu',
+ 'tk': 'Latn',
}
def lang_to_script(lang_code):
diff --git a/tools/layoutlib/.idea/codeStyleSettings.xml b/tools/layoutlib/.idea/codeStyleSettings.xml
index 89f7b34..ac90d1e 100644
--- a/tools/layoutlib/.idea/codeStyleSettings.xml
+++ b/tools/layoutlib/.idea/codeStyleSettings.xml
@@ -40,6 +40,7 @@
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
+ <option name="KEEP_LINE_BREAKS" value="false" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
@@ -55,6 +56,7 @@
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
+ <option name="WRAP_LONG_LINES" value="true" />
<arrangement>
<groups>
<group>
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index 985dd5a..ea320c7 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -21,6 +21,7 @@
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
@@ -84,7 +85,7 @@
return new BridgeTypedArray(resources, resources.mContext, numEntries, platformFile);
}
- private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
+ private static Pair<ResourceType, String> getResourceInfo(Resources resources, int id,
boolean[] platformResFlag_out) {
// first get the String related to this id in the framework
Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
@@ -97,11 +98,7 @@
if (resourceInfo != null) {
platformResFlag_out[0] = true;
- String attributeName = resourceInfo.getSecond();
-
- return Pair.of(attributeName,
- resources.mContext.getRenderResources().getFrameworkResource(
- resourceInfo.getFirst(), attributeName));
+ return resourceInfo;
}
// didn't find a match in the framework? look in the project.
@@ -110,13 +107,24 @@
if (resourceInfo != null) {
platformResFlag_out[0] = false;
- String attributeName = resourceInfo.getSecond();
-
- return Pair.of(attributeName,
- resources.mContext.getRenderResources().getProjectResource(
- resourceInfo.getFirst(), attributeName));
+ return resourceInfo;
}
}
+ return null;
+ }
+
+ private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
+ boolean[] platformResFlag_out) {
+ Pair<ResourceType, String> resourceInfo =
+ getResourceInfo(resources, id, platformResFlag_out);
+
+ if (resourceInfo != null) {
+ String attributeName = resourceInfo.getSecond();
+ RenderResources renderResources = resources.mContext.getRenderResources();
+ return Pair.of(attributeName, platformResFlag_out[0] ?
+ renderResources.getFrameworkResource(resourceInfo.getFirst(), attributeName) :
+ renderResources.getProjectResource(resourceInfo.getFirst(), attributeName));
+ }
return null;
}
@@ -626,17 +634,57 @@
@LayoutlibDelegate
static String getResourceEntryName(Resources resources, int resid) throws NotFoundException {
- throw new UnsupportedOperationException();
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, new boolean[1]);
+ if (resourceInfo != null) {
+ return resourceInfo.getSecond();
+ }
+ throwException(resid, null);
+ return null;
+
}
@LayoutlibDelegate
static String getResourceName(Resources resources, int resid) throws NotFoundException {
- throw new UnsupportedOperationException();
+ boolean[] platformOut = new boolean[1];
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, platformOut);
+ String packageName;
+ if (resourceInfo != null) {
+ if (platformOut[0]) {
+ packageName = SdkConstants.ANDROID_NS_NAME;
+ } else {
+ packageName = resources.mContext.getPackageName();
+ packageName = packageName == null ? SdkConstants.APP_PREFIX : packageName;
+ }
+ return packageName + ':' + resourceInfo.getFirst().getName() + '/' +
+ resourceInfo.getSecond();
+ }
+ throwException(resid, null);
+ return null;
+ }
+
+ @LayoutlibDelegate
+ static String getResourcePackageName(Resources resources, int resid) throws NotFoundException {
+ boolean[] platformOut = new boolean[1];
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, platformOut);
+ if (resourceInfo != null) {
+ if (platformOut[0]) {
+ return SdkConstants.ANDROID_NS_NAME;
+ }
+ String packageName = resources.mContext.getPackageName();
+ return packageName == null ? SdkConstants.APP_PREFIX : packageName;
+ }
+ throwException(resid, null);
+ return null;
}
@LayoutlibDelegate
static String getResourceTypeName(Resources resources, int resid) throws NotFoundException {
- throw new UnsupportedOperationException();
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, new boolean[1]);
+ if (resourceInfo != null) {
+ return resourceInfo.getFirst().getName();
+ }
+ throwException(resid, null);
+ return null;
}
@LayoutlibDelegate
@@ -849,22 +897,17 @@
* @throws NotFoundException
*/
private static void throwException(Resources resources, int id) throws NotFoundException {
- // first get the String related to this id in the framework
- Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+ throwException(id, getResourceInfo(resources, id, new boolean[1]));
+ }
- // if the name is unknown in the framework, get it from the custom view loader.
- if (resourceInfo == null && resources.mLayoutlibCallback != null) {
- resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
- }
-
+ private static void throwException(int id, @Nullable Pair<ResourceType, String> resourceInfo) {
String message;
if (resourceInfo != null) {
message = String.format(
"Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.",
resourceInfo.getFirst(), id, resourceInfo.getSecond());
} else {
- message = String.format(
- "Could not resolve resource value: 0x%1$X.", id);
+ message = String.format("Could not resolve resource value: 0x%1$X.", id);
}
throw new NotFoundException(message);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 7f41348..309c1b8 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -564,7 +564,8 @@
}
@Override
- public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
+ public void requestAppKeyboardShortcuts(
+ IResultReceiver receiver, int deviceId) throws RemoteException {
}
@Override
diff --git a/tools/layoutlib/bridge/src/android/view/WindowCallback.java b/tools/layoutlib/bridge/src/android/view/WindowCallback.java
index 411417c..1ea8a9f 100644
--- a/tools/layoutlib/bridge/src/android/view/WindowCallback.java
+++ b/tools/layoutlib/bridge/src/android/view/WindowCallback.java
@@ -141,9 +141,4 @@
public void onActionModeFinished(ActionMode mode) {
}
-
- @Override
- public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, @Nullable Menu menu) {
-
- }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 533a10a..a83f100 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -103,7 +103,8 @@
}
@Override
- public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
+ public void requestAppKeyboardShortcuts(
+ IResultReceiver receiver, int deviceId) throws RemoteException {
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index d417eb7..3031701 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -66,6 +66,6 @@
@Override
public void requestAppKeyboardShortcuts(
- KeyboardShortcutsReceiver receiver) {
+ KeyboardShortcutsReceiver receiver, int deviceId) {
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 4e4fcd0..0c53753 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -122,7 +122,7 @@
// build the context
mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
- mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(),
+ mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(mParams),
mParams.getTargetSdkVersion(), mParams.isRtlSupported());
setUp();
@@ -130,7 +130,6 @@
return SUCCESS.createResult();
}
-
/**
* Prepares the scene for action.
* <p>
@@ -320,10 +319,11 @@
}
}
- private Configuration getConfiguration() {
+ // VisibleForTesting
+ public static Configuration getConfiguration(RenderParams params) {
Configuration config = new Configuration();
- HardwareConfig hardwareConfig = mParams.getHardwareConfig();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
ScreenSize screenSize = hardwareConfig.getScreenSize();
if (screenSize != null) {
@@ -392,7 +392,7 @@
} else {
config.screenLayout |= Configuration.SCREENLAYOUT_ROUND_UNDEFINED;
}
- String locale = getParams().getLocale();
+ String locale = params.getLocale();
if (locale != null && !locale.isEmpty()) config.locale = new Locale(locale);
// TODO: fill in more config info.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
index 08a8faf..161bf41 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
@@ -27,8 +27,8 @@
public class DynamicIdMap {
- private final Map<Pair<ResourceType, String>, Integer> mDynamicIds = new HashMap<Pair<ResourceType, String>, Integer>();
- private final SparseArray<Pair<ResourceType, String>> mRevDynamicIds = new SparseArray<Pair<ResourceType, String>>();
+ private final Map<Pair<ResourceType, String>, Integer> mDynamicIds = new HashMap<>();
+ private final SparseArray<Pair<ResourceType, String>> mRevDynamicIds = new SparseArray<>();
private int mDynamicSeed;
public DynamicIdMap(int seed) {
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index c2f06e8..a5561fa 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -29,11 +29,14 @@
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.io.FolderWrapper;
import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
import com.android.resources.Density;
import com.android.resources.Navigation;
+import com.android.resources.ResourceType;
import com.android.utils.ILogger;
import org.junit.AfterClass;
@@ -42,13 +45,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
import java.io.File;
-import java.io.FileFilter;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
@@ -160,13 +165,8 @@
if (!host.isDirectory()) {
return null;
}
- File[] hosts = host.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- return path.isDirectory() && (path.getName().startsWith("linux-") || path.getName()
- .startsWith("darwin-"));
- }
- });
+ File[] hosts = host.listFiles(path -> path.isDirectory() &&
+ (path.getName().startsWith("linux-") || path.getName().startsWith("darwin-")));
for (File hostOut : hosts) {
String platformDir = getPlatformDirFromHostOut(hostOut);
if (platformDir != null) {
@@ -184,12 +184,9 @@
if (!sdkDir.isDirectory()) {
return null;
}
- File[] sdkDirs = sdkDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- // We need to search for $TARGET_PRODUCT (usually, sdk_phone_armv7)
- return path.isDirectory() && path.getName().startsWith("sdk");
- }
+ File[] sdkDirs = sdkDir.listFiles(path -> {
+ // We need to search for $TARGET_PRODUCT (usually, sdk_phone_armv7)
+ return path.isDirectory() && path.getName().startsWith("sdk");
});
for (File dir : sdkDirs) {
String platformDir = getPlatformDirFromHostOutSdkSdk(dir);
@@ -201,46 +198,34 @@
}
private static String getPlatformDirFromHostOutSdkSdk(File sdkDir) {
- File[] possibleSdks = sdkDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- return path.isDirectory() && path.getName().contains("android-sdk");
- }
- });
+ File[] possibleSdks = sdkDir.listFiles(
+ path -> path.isDirectory() && path.getName().contains("android-sdk"));
for (File possibleSdk : possibleSdks) {
File platformsDir = new File(possibleSdk, "platforms");
- File[] platforms = platformsDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- return path.isDirectory() && path.getName().startsWith("android-");
- }
- });
+ File[] platforms = platformsDir.listFiles(
+ path -> path.isDirectory() && path.getName().startsWith("android-"));
if (platforms == null || platforms.length == 0) {
continue;
}
- Arrays.sort(platforms, new Comparator<File>() {
- // Codenames before ints. Higher APIs precede lower.
- @Override
- public int compare(File o1, File o2) {
- final int MAX_VALUE = 1000;
- String suffix1 = o1.getName().substring("android-".length());
- String suffix2 = o2.getName().substring("android-".length());
- int suff1, suff2;
- try {
- suff1 = Integer.parseInt(suffix1);
- } catch (NumberFormatException e) {
- suff1 = MAX_VALUE;
- }
- try {
- suff2 = Integer.parseInt(suffix2);
- } catch (NumberFormatException e) {
- suff2 = MAX_VALUE;
- }
- if (suff1 != MAX_VALUE || suff2 != MAX_VALUE) {
- return suff2 - suff1;
- }
- return suffix2.compareTo(suffix1);
+ Arrays.sort(platforms, (o1, o2) -> {
+ final int MAX_VALUE = 1000;
+ String suffix1 = o1.getName().substring("android-".length());
+ String suffix2 = o2.getName().substring("android-".length());
+ int suff1, suff2;
+ try {
+ suff1 = Integer.parseInt(suffix1);
+ } catch (NumberFormatException e) {
+ suff1 = MAX_VALUE;
}
+ try {
+ suff2 = Integer.parseInt(suffix2);
+ } catch (NumberFormatException e) {
+ suff2 = MAX_VALUE;
+ }
+ if (suff1 != MAX_VALUE || suff2 != MAX_VALUE) {
+ return suff2 - suff1;
+ }
+ return suffix2.compareTo(suffix1);
});
return platforms[0].getAbsolutePath();
}
@@ -261,6 +246,7 @@
return null;
}
}
+
/**
* Initialize the bridge and the resource maps.
*/
@@ -325,8 +311,7 @@
@Test
public void testExpand() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "expand_vert_layout.xml");
+ LayoutPullParser parser = createLayoutPullParser("expand_vert_layout.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -348,8 +333,7 @@
.setScreenHeight(300)
.setDensity(Density.XHIGH)
.setNavigation(Navigation.NONAV);
- parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "expand_horz_layout.xml");
+ parser = createLayoutPullParser("expand_horz_layout.xml");
params = getSessionParams(parser, customConfigGenerator,
layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
RenderingMode.H_SCROLL, 22);
@@ -361,8 +345,7 @@
@Test
public void testVectorAnimation() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "indeterminate_progressbar.xml");
+ LayoutPullParser parser = createLayoutPullParser("indeterminate_progressbar.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -373,8 +356,7 @@
renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
- parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "indeterminate_progressbar.xml");
+ parser = createLayoutPullParser("indeterminate_progressbar.xml");
params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
RenderingMode.V_SCROLL, 22);
@@ -388,8 +370,7 @@
@Test
public void testVectorDrawable() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "vector_drawable.xml");
+ LayoutPullParser parser = createLayoutPullParser("vector_drawable.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -405,8 +386,7 @@
@Test
public void testScrolling() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "scrolled.xml");
+ LayoutPullParser parser = createLayoutPullParser("scrolled.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -435,6 +415,39 @@
assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
}
+ @Test
+ public void testGetResourceNameVariants() throws Exception {
+ // Setup
+ SessionParams params = createSessionParams("", ConfigGenerator.NEXUS_4);
+ AssetManager assetManager = AssetManager.getSystem();
+ DisplayMetrics metrics = new DisplayMetrics();
+ Configuration configuration = RenderAction.getConfiguration(params);
+ Resources resources = new Resources(assetManager, metrics, configuration);
+ resources.mLayoutlibCallback = params.getLayoutlibCallback();
+ resources.mContext =
+ new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+ params.getAssets(), params.getLayoutlibCallback(), configuration,
+ params.getTargetSdkVersion(), params.isRtlSupported());
+ // Test
+ assertEquals("android:style/ButtonBar",
+ resources.getResourceName(android.R.style.ButtonBar));
+ assertEquals("android", resources.getResourcePackageName(android.R.style.ButtonBar));
+ assertEquals("ButtonBar", resources.getResourceEntryName(android.R.style.ButtonBar));
+ assertEquals("style", resources.getResourceTypeName(android.R.style.ButtonBar));
+ int id = resources.mLayoutlibCallback.getResourceId(ResourceType.STRING, "app_name");
+ assertEquals("com.android.layoutlib.test.myapplication:string/app_name",
+ resources.getResourceName(id));
+ assertEquals("com.android.layoutlib.test.myapplication",
+ resources.getResourcePackageName(id));
+ assertEquals("string", resources.getResourceTypeName(id));
+ assertEquals("app_name", resources.getResourceEntryName(id));
+ }
+
+ @NonNull
+ private LayoutPullParser createLayoutPullParser(String layoutPath) {
+ return new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutPath);
+ }
+
/**
* Create a new rendering session and test that rendering the given layout doesn't throw any
* exceptions and matches the provided image.
@@ -505,16 +518,21 @@
private RenderResult renderAndVerify(String layoutFileName, String goldenFileName,
ConfigGenerator deviceConfig)
throws ClassNotFoundException {
+ SessionParams params = createSessionParams(layoutFileName, deviceConfig);
+ return renderAndVerify(params, goldenFileName);
+ }
+
+ private SessionParams createSessionParams(String layoutFileName, ConfigGenerator deviceConfig)
+ throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
+ LayoutPullParser parser = createLayoutPullParser(layoutFileName);
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
// TODO: Set up action bar handler properly to test menu rendering.
// Create session params.
- SessionParams params = getSessionParams(parser, deviceConfig,
+ return getSessionParams(parser, deviceConfig,
layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
- return renderAndVerify(params, goldenFileName);
}
/**
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 6c16ed0..96ae523 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -24,7 +24,9 @@
import com.android.ide.common.rendering.api.ParserFactory;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams.Key;
import com.android.ide.common.resources.IntArrayWrapper;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.resources.ResourceType;
import com.android.util.Pair;
import com.android.utils.ILogger;
@@ -176,4 +178,12 @@
}
};
}
+
+ @Override
+ public <T> T getFlag(Key<T> key) {
+ if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
+ return (T) PACKAGE_NAME;
+ }
+ return null;
+ }
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
index c79b662..1110494 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
@@ -56,9 +56,7 @@
public LayoutPullParser(File layoutFile) {
try {
init(new FileInputStream(layoutFile));
- } catch (XmlPullParserException e) {
- throw new IOError(e);
- } catch (FileNotFoundException e) {
+ } catch (XmlPullParserException | FileNotFoundException e) {
throw new IOError(e);
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index f32bb76..1a00cc9 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -168,6 +168,7 @@
"android.content.res.Resources#getLayout",
"android.content.res.Resources#getResourceEntryName",
"android.content.res.Resources#getResourceName",
+ "android.content.res.Resources#getResourcePackageName",
"android.content.res.Resources#getResourceTypeName",
"android.content.res.Resources#getString",
"android.content.res.Resources#getStringArray",
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index a9259fa..67cf107 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -56,6 +56,11 @@
*/
public int anqpDomainId;
+ /*
+ * This field is equivalent to the |flags|, rather than the |capabilities| field
+ * of the per-BSS scan results returned by WPA supplicant. See the definition of
+ * |struct wpa_bss| in wpa_supplicant/bss.h for more details.
+ */
/**
* Describes the authentication, key management, and encryption schemes
* supported by the access point.
@@ -211,6 +216,10 @@
/** {@hide} */
public static final long FLAG_80211mc_RESPONDER = 0x0000000000000002;
+ /*
+ * These flags are specific to the ScanResult class, and are not related to the |flags|
+ * field of the per-BSS scan results from WPA supplicant.
+ */
/**
* Defines flags; such as {@link #FLAG_PASSPOINT_NETWORK}.
* {@hide}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7dc8049..06dea07 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -684,6 +684,13 @@
/**
* @hide
+ * A hint about whether or not the network represented by this WifiConfiguration
+ * is metered.
+ */
+ public boolean meteredHint;
+
+ /**
+ * @hide
* Number of time the scorer overrode a the priority based choice, when comparing two
* WifiConfigurations, note that since comparing WifiConfiguration happens very often
* potentially at every scan, this number might become very large, even on an idle
@@ -1302,6 +1309,7 @@
selfAdded = false;
didSelfAdd = false;
ephemeral = false;
+ meteredHint = false;
validatedInternetAccess = false;
mIpConfiguration = new IpConfiguration();
lastUpdateUid = -1;
@@ -1399,7 +1407,9 @@
if (this.selfAdded) sbuf.append(" selfAdded");
if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
if (this.ephemeral) sbuf.append(" ephemeral");
- if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess || this.ephemeral) {
+ if (this.meteredHint) sbuf.append(" meteredHint");
+ if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
+ || this.ephemeral || this.meteredHint) {
sbuf.append("\n");
}
sbuf.append(" KeyMgmt:");
@@ -1546,18 +1556,6 @@
return sbuf.toString();
}
- /**
- * Construct a WifiConfiguration from a scanned network
- * @param scannedAP the scan result used to construct the config entry
- * TODO: figure out whether this is a useful way to construct a new entry.
- *
- public WifiConfiguration(ScanResult scannedAP) {
- networkId = -1;
- SSID = scannedAP.SSID;
- BSSID = scannedAP.BSSID;
- }
- */
-
/** {@hide} */
public String getPrintableSsid() {
if (SSID == null) return "";
@@ -1832,6 +1830,7 @@
selfAdded = source.selfAdded;
validatedInternetAccess = source.validatedInternetAccess;
ephemeral = source.ephemeral;
+ meteredHint = source.meteredHint;
if (source.visibility != null) {
visibility = new Visibility(source.visibility);
}
@@ -1870,11 +1869,6 @@
}
}
- /** {@hide} */
- //public static final int NOTHING_TAG = 0;
- /** {@hide} */
- //public static final int SCAN_CACHE_TAG = 1;
-
/** Implement the Parcelable interface {@hide} */
@Override
public void writeToParcel(Parcel dest, int flags) {
@@ -1916,6 +1910,7 @@
dest.writeInt(didSelfAdd ? 1 : 0);
dest.writeInt(validatedInternetAccess ? 1 : 0);
dest.writeInt(ephemeral ? 1 : 0);
+ dest.writeInt(meteredHint ? 1 : 0);
dest.writeInt(creatorUid);
dest.writeInt(lastConnectUid);
dest.writeInt(lastUpdateUid);
@@ -1985,6 +1980,7 @@
config.didSelfAdd = in.readInt() != 0;
config.validatedInternetAccess = in.readInt() != 0;
config.ephemeral = in.readInt() != 0;
+ config.meteredHint = in.readInt() != 0;
config.creatorUid = in.readInt();
config.lastConnectUid = in.readInt();
config.lastUpdateUid = in.readInt();
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index c5e7bff..ecf54474f 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -277,6 +277,12 @@
* non-zero => scan was truncated, so results may not be complete
*/
private int mFlags;
+ /**
+ * Indicates the buckets that were scanned to generate these results.
+ * This is not relevant to WifiScanner API users and is used internally.
+ * {@hide}
+ */
+ private int mBucketsScanned;
/** all scan results discovered in this scan, sorted by timestamp in ascending order */
private ScanResult mResults[];
@@ -288,9 +294,18 @@
mResults = results;
}
+ /** {@hide} */
+ public ScanData(int id, int flags, int bucketsScanned, ScanResult[] results) {
+ mId = id;
+ mFlags = flags;
+ mBucketsScanned = bucketsScanned;
+ mResults = results;
+ }
+
public ScanData(ScanData s) {
mId = s.mId;
mFlags = s.mFlags;
+ mBucketsScanned = s.mBucketsScanned;
mResults = new ScanResult[s.mResults.length];
for (int i = 0; i < s.mResults.length; i++) {
ScanResult result = s.mResults[i];
@@ -321,6 +336,7 @@
if (mResults != null) {
dest.writeInt(mId);
dest.writeInt(mFlags);
+ dest.writeInt(mBucketsScanned);
dest.writeInt(mResults.length);
for (int i = 0; i < mResults.length; i++) {
ScanResult result = mResults[i];
@@ -337,12 +353,13 @@
public ScanData createFromParcel(Parcel in) {
int id = in.readInt();
int flags = in.readInt();
+ int bucketsScanned = in.readInt();
int n = in.readInt();
ScanResult results[] = new ScanResult[n];
for (int i = 0; i < n; i++) {
results[i] = ScanResult.CREATOR.createFromParcel(in);
}
- return new ScanData(id, flags, results);
+ return new ScanData(id, flags, bucketsScanned, results);
}
public ScanData[] newArray(int size) {