method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
- method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+ method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
- method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+ method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
method public boolean isTetheringSupported();
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
- method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+ method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * Normal interruption filter.
+ * Normal interruption filter - no notifications are suppressed.
*/
public static final int INTERRUPTION_FILTER_ALL = 1;
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * Priority interruption filter.
+ * Priority interruption filter - all notifications are suppressed except those that match
+ * the priority criteria. Some audio streams are muted. See
+ * {@link Policy#priorityCallSenders}, {@link Policy#priorityCategories},
+ * {@link Policy#priorityMessageSenders} to define or query this criteria. Users can
+ * additionally specify packages that can bypass this interruption filter.
*/
public static final int INTERRUPTION_FILTER_PRIORITY = 2;
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * No interruptions filter.
+ * No interruptions filter - all notifications are suppressed and all audio streams (except
+ * those used for phone calls) and vibrations are muted.
*/
public static final int INTERRUPTION_FILTER_NONE = 3;
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * Alarms only interruption filter.
+ * Alarms only interruption filter - all notifications except those of category
+ * {@link Notification#CATEGORY_ALARM} are suppressed. Some audio streams are muted.
*/
public static final int INTERRUPTION_FILTER_ALARMS = 4;
* @return {@code true} if the package is set as always-on VPN controller; {@code false}
* otherwise.
* @throws SecurityException if {@code admin} is not a device or a profile owner.
+ * @throws NameNotFoundException if {@code vpnPackage} is not installed.
+ * @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being
+ * set as always-on, or if always-on VPN is not available.
*/
- public boolean setAlwaysOnVpnPackage(@NonNull ComponentName admin,
- @Nullable String vpnPackage) {
+ public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
+ throws NameNotFoundException, UnsupportedOperationException {
if (mService != null) {
try {
- return mService.setAlwaysOnVpnPackage(admin, vpnPackage);
+ if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage)) {
+ throw new NameNotFoundException(vpnPackage);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- return false;
}
/**
/**
* Called by the profile owner of a managed profile to obtain a {@link DevicePolicyManager}
* whose calls act on the parent profile.
- * <p>
- * Note only some methods will work on the parent Manager.
+ *
+ * <p>The following methods are supported for the parent instance, all other methods will
+ * throw a SecurityException when called on the parent instance:
+ * <ul>
+ * <li>{@link #getPasswordQuality}</li>
+ * <li>{@link #setPasswordQuality}</li>
+ * <li>{@link #getPasswordMinimumLength}</li>
+ * <li>{@link #setPasswordMinimumLength}</li>
+ * <li>{@link #getPasswordMinimumUpperCase}</li>
+ * <li>{@link #setPasswordMinimumUpperCase}</li>
+ * <li>{@link #getPasswordMinimumLowerCase}</li>
+ * <li>{@link #setPasswordMinimumLowerCase}</li>
+ * <li>{@link #getPasswordMinimumLetters}</li>
+ * <li>{@link #setPasswordMinimumLetters}</li>
+ * <li>{@link #getPasswordMinimumNumeric}</li>
+ * <li>{@link #setPasswordMinimumNumeric}</li>
+ * <li>{@link #getPasswordMinimumSymbols}</li>
+ * <li>{@link #setPasswordMinimumSymbols}</li>
+ * <li>{@link #getPasswordMinimumNonLetter}</li>
+ * <li>{@link #setPasswordMinimumNonLetter}</li>
+ * <li>{@link #getPasswordHistoryLength}</li>
+ * <li>{@link #setPasswordHistoryLength}</li>
+ * <li>{@link #getPasswordExpirationTimeout}</li>
+ * <li>{@link #setPasswordExpirationTimeout}</li>
+ * <li>{@link #getPasswordExpiration}</li>
+ * <li>{@link #isActivePasswordSufficient}</li>
+ * <li>{@link #getCurrentFailedPasswordAttempts}</li>
+ * <li>{@link #getMaximumFailedPasswordsForWipe}</li>
+ * <li>{@link #setMaximumFailedPasswordsForWipe}</li>
+ * <li>{@link #getMaximumTimeToLock}</li>
+ * <li>{@link #setMaximumTimeToLock}</li>
+ * <li>{@link #lockNow}</li>
+ * <li>{@link #getKeyguardDisabledFeatures}</li>
+ * <li>{@link #setKeyguardDisabledFeatures}</li>
+ * <li>{@link #getTrustAgentConfiguration}</li>
+ * <li>{@link #setTrustAgentConfiguration}</li>
+ * </ul>
*
* @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
* @throws SecurityException if {@code admin} is not a profile owner.
}
public void unlinkToDeath(IBinder.DeathRecipient recipient, int flags) {
- mRemoteDevice.asBinder().unlinkToDeath(recipient, flags);
+ if (mRemoteDevice.asBinder() != null) {
+ mRemoteDevice.asBinder().unlinkToDeath(recipient, flags);
+ }
}
public void disconnect() {
mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
- mService.updateInputViewShown();
+ // In Android M and prior, state change of
+ // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
+ // #onConfigurationChanged(). For compatibility reasons, we reset the internal
+ // state as if configuration was changed.
+ mService.resetStateForNewConfiguration();
}
}
*/
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ resetStateForNewConfiguration();
+ }
+ private void resetStateForNewConfiguration() {
boolean visible = mWindowVisible;
int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
*
* @param networkCallback The {@link NetworkCallback} that the system will call as the
* system default network changes.
- * @hide
*/
public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
// This works because if the NetworkCapabilities are null,
+++ /dev/null
-/*
- * Copyright (C) 2011 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;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.util.Protocol;
-
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketTimeoutException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Performs a simple DNS "ping" by sending a "server status" query packet to the
- * DNS server. As long as the server replies, we consider it a success.
- * <p>
- * We do not use a simple hostname lookup because that could be cached and the
- * API may not differentiate between a time out and a failure lookup (which we
- * really care about).
- * <p>
- *
- * @hide
- */
-public final class DnsPinger extends Handler {
- private static final boolean DBG = false;
-
- private static final int RECEIVE_POLL_INTERVAL_MS = 200;
- private static final int DNS_PORT = 53;
-
- /** Short socket timeout so we don't block one any 'receive' call */
- private static final int SOCKET_TIMEOUT_MS = 1;
-
- /** Used to generate IDs */
- private static final Random sRandom = new Random();
- private static final AtomicInteger sCounter = new AtomicInteger();
-
- private ConnectivityManager mConnectivityManager = null;
- private final Context mContext;
- private final int mConnectionType;
- private final Handler mTarget;
- private final ArrayList<InetAddress> mDefaultDns;
- private String TAG;
-
- //Invalidates old dns requests upon a cancel
- private AtomicInteger mCurrentToken = new AtomicInteger();
-
- private static final int BASE = Protocol.BASE_DNS_PINGER;
-
- /**
- * Async response packet for dns pings.
- * arg1 is the ID of the ping, also returned by {@link #pingDnsAsync(InetAddress, int, int)}
- * arg2 is the delay, or is negative on error.
- */
- public static final int DNS_PING_RESULT = BASE;
- /** An error code for a {@link #DNS_PING_RESULT} packet */
- public static final int TIMEOUT = -1;
- /** An error code for a {@link #DNS_PING_RESULT} packet */
- public static final int SOCKET_EXCEPTION = -2;
-
- /**
- * Send a new ping via a socket. arg1 is ID, arg2 is timeout, obj is InetAddress to ping
- */
- private static final int ACTION_PING_DNS = BASE + 1;
- private static final int ACTION_LISTEN_FOR_RESPONSE = BASE + 2;
- private static final int ACTION_CANCEL_ALL_PINGS = BASE + 3;
-
- private List<ActivePing> mActivePings = new ArrayList<ActivePing>();
- private int mEventCounter;
-
- private class ActivePing {
- DatagramSocket socket;
- int internalId;
- short packetId;
- int timeout;
- Integer result;
- long start = SystemClock.elapsedRealtime();
- }
-
- /* Message argument for ACTION_PING_DNS */
- private class DnsArg {
- InetAddress dns;
- int seq;
-
- DnsArg(InetAddress d, int s) {
- dns = d;
- seq = s;
- }
- }
-
- public DnsPinger(Context context, String TAG, Looper looper,
- Handler target, int connectionType) {
- super(looper);
- this.TAG = TAG;
- mContext = context;
- mTarget = target;
- mConnectionType = connectionType;
- if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {
- throw new IllegalArgumentException("Invalid connectionType in constructor: "
- + connectionType);
- }
- mDefaultDns = new ArrayList<InetAddress>();
- mDefaultDns.add(getDefaultDns());
- mEventCounter = 0;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ACTION_PING_DNS:
- DnsArg dnsArg = (DnsArg) msg.obj;
- if (dnsArg.seq != mCurrentToken.get()) {
- break;
- }
- try {
- ActivePing newActivePing = new ActivePing();
- InetAddress dnsAddress = dnsArg.dns;
- newActivePing.internalId = msg.arg1;
- newActivePing.timeout = msg.arg2;
- newActivePing.socket = new DatagramSocket();
- // Set some socket properties
- newActivePing.socket.setSoTimeout(SOCKET_TIMEOUT_MS);
-
- // Try to bind but continue ping if bind fails
- try {
- newActivePing.socket.setNetworkInterface(NetworkInterface.getByName(
- getCurrentLinkProperties().getInterfaceName()));
- } catch (Exception e) {
- loge("sendDnsPing::Error binding to socket " + e);
- }
-
- newActivePing.packetId = (short) sRandom.nextInt();
- byte[] buf = mDnsQuery.clone();
- buf[0] = (byte) (newActivePing.packetId >> 8);
- buf[1] = (byte) newActivePing.packetId;
-
- // Send the DNS query
- DatagramPacket packet = new DatagramPacket(buf,
- buf.length, dnsAddress, DNS_PORT);
- if (DBG) {
- log("Sending a ping " + newActivePing.internalId +
- " to " + dnsAddress.getHostAddress()
- + " with packetId " + newActivePing.packetId + ".");
- }
-
- newActivePing.socket.send(packet);
- mActivePings.add(newActivePing);
- mEventCounter++;
- sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
- RECEIVE_POLL_INTERVAL_MS);
- } catch (IOException e) {
- sendResponse(msg.arg1, -9999, SOCKET_EXCEPTION);
- }
- break;
- case ACTION_LISTEN_FOR_RESPONSE:
- if (msg.arg1 != mEventCounter) {
- break;
- }
- for (ActivePing curPing : mActivePings) {
- try {
- /** Each socket will block for {@link #SOCKET_TIMEOUT_MS} in receive() */
- byte[] responseBuf = new byte[2];
- DatagramPacket replyPacket = new DatagramPacket(responseBuf, 2);
- curPing.socket.receive(replyPacket);
- // Check that ID field matches (we're throwing out the rest of the packet)
- if (responseBuf[0] == (byte) (curPing.packetId >> 8) &&
- responseBuf[1] == (byte) curPing.packetId) {
- curPing.result =
- (int) (SystemClock.elapsedRealtime() - curPing.start);
- } else {
- if (DBG) {
- log("response ID didn't match, ignoring packet");
- }
- }
- } catch (SocketTimeoutException e) {
- // A timeout here doesn't mean anything - squelsh this exception
- } catch (Exception e) {
- if (DBG) {
- log("DnsPinger.pingDns got socket exception: " + e);
- }
- curPing.result = SOCKET_EXCEPTION;
- }
- }
- Iterator<ActivePing> iter = mActivePings.iterator();
- while (iter.hasNext()) {
- ActivePing curPing = iter.next();
- if (curPing.result != null) {
- sendResponse(curPing.internalId, curPing.packetId, curPing.result);
- curPing.socket.close();
- iter.remove();
- } else if (SystemClock.elapsedRealtime() >
- curPing.start + curPing.timeout) {
- sendResponse(curPing.internalId, curPing.packetId, TIMEOUT);
- curPing.socket.close();
- iter.remove();
- }
- }
- if (!mActivePings.isEmpty()) {
- sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
- RECEIVE_POLL_INTERVAL_MS);
- }
- break;
- case ACTION_CANCEL_ALL_PINGS:
- for (ActivePing activePing : mActivePings)
- activePing.socket.close();
- mActivePings.clear();
- break;
- }
- }
-
- /**
- * Returns a list of DNS addresses, coming from either the link properties of the
- * specified connection or the default system DNS if the link properties has no dnses.
- * @return a non-empty non-null list
- */
- public List<InetAddress> getDnsList() {
- LinkProperties curLinkProps = getCurrentLinkProperties();
- if (curLinkProps == null) {
- loge("getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
- return mDefaultDns;
- }
-
- Collection<InetAddress> dnses = curLinkProps.getDnsServers();
- if (dnses == null || dnses.size() == 0) {
- loge("getDns::LinkProps has null dns - returning default");
- return mDefaultDns;
- }
-
- return new ArrayList<InetAddress>(dnses);
- }
-
- /**
- * Send a ping. The response will come via a {@link #DNS_PING_RESULT} to the handler
- * specified at creation.
- * @param dns address of dns server to ping
- * @param timeout timeout for ping
- * @return an ID field, which will also be included in the {@link #DNS_PING_RESULT} message.
- */
- public int pingDnsAsync(InetAddress dns, int timeout, int delay) {
- int id = sCounter.incrementAndGet();
- sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout,
- new DnsArg(dns, mCurrentToken.get())), delay);
- return id;
- }
-
- public void cancelPings() {
- mCurrentToken.incrementAndGet();
- obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget();
- }
-
- private void sendResponse(int internalId, int externalId, int responseVal) {
- if(DBG) {
- log("Responding to packet " + internalId +
- " externalId " + externalId +
- " and val " + responseVal);
- }
- mTarget.sendMessage(obtainMessage(DNS_PING_RESULT, internalId, responseVal));
- }
-
- private LinkProperties getCurrentLinkProperties() {
- if (mConnectivityManager == null) {
- mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
-
- return mConnectivityManager.getLinkProperties(mConnectionType);
- }
-
- private InetAddress getDefaultDns() {
- String dns = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.DEFAULT_DNS_SERVER);
- if (dns == null || dns.length() == 0) {
- dns = mContext.getResources().getString(
- com.android.internal.R.string.config_default_dns_server);
- }
- try {
- return NetworkUtils.numericToInetAddress(dns);
- } catch (IllegalArgumentException e) {
- loge("getDefaultDns::malformed default dns address");
- return null;
- }
- }
-
- private static final byte[] mDnsQuery = new byte[] {
- 0, 0, // [0-1] is for ID (will set each time)
- 1, 0, // [2-3] are flags. Set byte[2] = 1 for recursion desired (RD) on. Currently on.
- 0, 1, // [4-5] bytes are for number of queries (QCOUNT)
- 0, 0, // [6-7] unused count field for dns response packets
- 0, 0, // [8-9] unused count field for dns response packets
- 0, 0, // [10-11] unused count field for dns response packets
- 3, 'w', 'w', 'w',
- 6, 'g', 'o', 'o', 'g', 'l', 'e',
- 3, 'c', 'o', 'm',
- 0, // null terminator of address (also called empty TLD)
- 0, 1, // QTYPE, set to 1 = A (host address)
- 0, 1 // QCLASS, set to 1 = IN (internet)
- };
-
- private void log(String s) {
- Log.d(TAG, s);
- }
-
- private void loge(String s) {
- Log.e(TAG, s);
- }
-}
*/
void setDnsServersForNetwork(int netId, in String[] servers, String domains);
- /**
- * Flush the DNS cache associated with the specified network.
- */
- void flushNetworkDnsCache(int netId);
-
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
void setFirewallInterfaceRule(String iface, boolean allow);
if (includepad) {
spacing = metrics.bottom - metrics.top;
+ mDesc = metrics.bottom;
} else {
spacing = metrics.descent - metrics.ascent;
+ mDesc = metrics.descent;
}
mBottom = spacing;
mTopPadding = metrics.top - metrics.ascent;
mBottomPadding = metrics.bottom - metrics.descent;
}
-
- mDesc = spacing + mBottomPadding + (includepad ? metrics.top : metrics.ascent);
}
/**
@Override
public int getLineTop(int line) {
- int top = mLines[mColumns * line + TOP];
- if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
- line != mLineCount) {
- top += getBottomPadding();
- }
- return top;
+ return mLines[mColumns * line + TOP];
}
@Override
public int getLineDescent(int line) {
- int descent = mLines[mColumns * line + DESCENT];
- if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
- line != mLineCount) {
- descent += getBottomPadding();
- }
- return descent;
+ return mLines[mColumns * line + DESCENT];
}
@Override
// Action arguments
/**
- * Argument for specifying index of {@link android.text.style.ClickableSpan} the click action is
- * related to.
- * <p>
- * <strong>Type:</strong> int<br>
- * <strong>Actions:</strong>
- * {@link AccessibilityAction#ACTION_CLICK}
- * </p>
- *
- * @see AccessibilityAction#ACTION_CLICK
- */
- public static final String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT =
- "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
-
- /**
- * Argument for specifying index of character in the text which contains
- * {@link android.text.style.ClickableSpan} the click action is
- * related to. If there is more than one {@link android.text.style.ClickableSpan} assigned for
- * the range the character is in only the first span would be clicked.
- * <p>
- * <strong>Type:</strong> int<br>
- * <strong>Actions:</strong>
- * {@link AccessibilityAction#ACTION_CLICK}
- * </p>
- *
- * @see AccessibilityAction#ACTION_CLICK
- */
- public static final String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT =
- "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
-
- /**
* Argument for which movement granularity to be used when traversing the node text.
* <p>
* <strong>Type:</strong> int<br>
/**
* Action that clicks on the node info.
- *
- * <p>
- * If a specific {@link android.text.style.ClickableSpan} within node's text content is
- * supposed to be clicked, then one of {@link #ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT} or
- * {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT} arguments should be specified.
- * If both arguments are set {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT} would
- * be ignored.<br>
- *
- * {@link #ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT} specifies index of corresponding
- * {@link android.text.style.ClickableSpan} in {@link android.text.SpannableString}.<br>
- *
- * {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT} specifies index of character
- * that could contain one or more spans.
- * </p>
- *
- * <p>
- * <strong>Optional arguments:</strong>
- * {@link #ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT},
- * {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT}<br>
- * <strong>Example:</strong> Perform click on 3rd {@link android.text.style.ClickableSpan}
- * inside {@link android.text.SpannableString} in node's text.
- * <code><pre><p>
- * Bundle arguments = new Bundle();
- * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT, 3);
- * info.performAction(AccessibilityAction.ACTION_CLICK.getId(), arguments);
- * </code></pre></p>
- * </p>
*/
public static final AccessibilityAction ACTION_CLICK =
new AccessibilityAction(
private boolean performAccessibilityActionClick(Bundle arguments) {
boolean handled = false;
- boolean processed = false;
if (!isEnabled()) {
return false;
}
- if (arguments != null && arguments.containsKey(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT)) {
- int spanIndex = arguments.getInt(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT, -1);
- if (spanIndex >= 0 && hasSpannableText()) {
- ClickableSpan[] spans = ((Spannable) mText).getSpans(0,
- mText.length(), ClickableSpan.class);
- if (spans != null && spans.length > spanIndex && spans[spanIndex] != null) {
- // Simulate View.onTouchEvent for an ACTION_UP event
- if (isFocusable() && !isFocused()) {
- requestFocus();
- }
- spans[spanIndex].onClick(this);
- handled = true;
- }
- }
- processed = true;
- }
-
- if (!processed && arguments != null && arguments.containsKey(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT)) {
- int characterIndex = arguments.getInt(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT, -1);
- if (characterIndex >= 0 && hasSpannableText()) {
- ClickableSpan[] spans = ((Spannable) mText).getSpans(characterIndex,
- characterIndex, ClickableSpan.class);
- // click only on the first span to keep parity with onTouch() implementation
- if (spans != null && spans.length > 0 && spans[0] != null) {
- // Simulate View.onTouchEvent for an ACTION_UP event
- if (isFocusable() && !isFocused()) {
- requestFocus();
- }
- spans[0].onClick(this);
- handled = true;
- }
- }
- processed = true;
- }
-
- if (!processed && (isClickable() || isLongClickable())) {
+ if (isClickable() || isLongClickable()) {
// Simulate View.onTouchEvent for an ACTION_UP event
if (isFocusable() && !isFocused()) {
requestFocus();
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
*/
public class FastXmlSerializer implements XmlSerializer {
private static final String ESCAPE_TABLE[] = new String[] {
- null, null, null, null, null, null, null, null, // 0-7
- null, null, null, null, null, null, null, null, // 8-15
- null, null, null, null, null, null, null, null, // 16-23
- null, null, null, null, null, null, null, null, // 24-31
- null, null, """, null, null, null, "&", null, // 32-39
- null, null, null, null, null, null, null, null, // 40-47
- null, null, null, null, null, null, null, null, // 48-55
- null, null, null, null, "<", null, ">", null, // 56-63
+ "�", "", "", "", "", "", "", "", // 0-7
+ "", "	", " ", "", "", " ", "", "", // 8-15
+ "", "", "", "", "", "", "", "", // 16-23
+ "", "", "", "", "", "", "", "", // 24-31
+ null, null, """, null, null, null, "&", null, // 32-39
+ null, null, null, null, null, null, null, null, // 40-47
+ null, null, null, null, null, null, null, null, // 48-55
+ null, null, null, null, "<", null, ">", null, // 56-63
};
private static final int BUFFER_LEN = 8192;
throw new IllegalArgumentException();
if (true) {
try {
- mCharset = Charset.forName(encoding).newEncoder();
+ mCharset = Charset.forName(encoding).newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
} catch (IllegalCharsetNameException e) {
throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
encoding).initCause(e));
super(Preconditions.checkNotNull(popup).mContext);
this.mPopup = popup;
setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
+ setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
}
@Override
<protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
<protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
- <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
<protected-broadcast android:name="com.android.server.action.NETWORK_STATS_POLL" />
<protected-broadcast android:name="com.android.server.action.NETWORK_STATS_UPDATED" />
<protected-broadcast android:name="com.android.server.NetworkTimeUpdateService.action.POLL" />
<protected-broadcast android:name="android.net.wifi.PASSPOINT_ICON_RECEIVED" />
<protected-broadcast android:name="com.android.server.notification.CountdownConditionProvider" />
- <!-- @hide UCE service Notification -->
+ <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
+ <protected-broadcast android:name="com.android.ims.IMS_INCOMING_CALL" />
<protected-broadcast android:name="com.android.ims.internal.uce.UCE_SERVICE_UP" />
+ <protected-broadcast android:name="com.android.intent.action.IMS_FEATURE_CHANGED" />
+ <protected-broadcast android:name="com.android.intent.action.IMS_CONFIG_CHANGED" />
+
+ <protected-broadcast android:name="com.android.internal.location.ALARM_WAKEUP" />
+ <protected-broadcast android:name="com.android.internal.location.ALARM_TIMEOUT" />
+ <protected-broadcast android:name="android.intent.action.GLOBAL_BUTTON" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<string name="safeMode" msgid="2788228061547930246">"Бяспечны рэжым"</string>
<string name="android_system_label" msgid="6577375335728551336">"Сістэма Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Пераключыцца на асабісты"</string>
- <string name="managed_profile_label" msgid="5289992269827577857">"Пераключыцца на рабочы"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Пераключыцца на працоўны"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Кантакты"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"атрымліваць доступ да вашых кантактаў"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Месцазнаходжанне"</string>
<string name="select_input_method" msgid="8547250819326693584">"Aldatu teklatua"</string>
<string name="show_ime" msgid="2506087537466597099">"Erakutsi pantailan teklatu fisikoa aktibo dagoen bitartean"</string>
<string name="hardware" msgid="194658061510127999">"Erakutsi teklatu birtuala"</string>
- <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguratu teklatua fisikoa"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguratu teklatu fisikoa"</string>
<string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Hizkuntza eta diseinua hautatzeko, sakatu hau"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"内容已隐藏(根据政策规定)"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
- <string name="user_owner_label" msgid="1119010402169916617">"切换为“个人”"</string>
- <string name="managed_profile_label" msgid="5289992269827577857">"切换为“工作”"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"切换到“个人”"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"切换到“工作”"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"通讯录"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"访问您的通讯录"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string>
<string name="package_installed_device_owner" msgid="8420696545959087545">"已由管理员安装"</string>
<string name="package_updated_device_owner" msgid="8856631322440187071">"由您单位的管理员更新"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"已被管理员删除"</string>
- <string name="battery_saver_description" msgid="1960431123816253034">"为了延长电池的续航时间,节电助手会降低设备的性能,并限制振动、位置信息服务和大部分后台流量。对于电子邮件、聊天工具等依赖于同步功能的应用,可能要打开这类应用时才能收到新信息。\n\n节电助手会在设备充电时自动关闭。"</string>
+ <string name="battery_saver_description" msgid="1960431123816253034">"为了延长电池的续航时间,省电模式会降低设备的性能,并限制振动、位置信息服务和大部分后台流量。对于电子邮件、聊天工具等依赖于同步功能的应用,可能要打开这类应用时才能收到新信息。\n\n省电模式会在设备充电时自动关闭。"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d 分钟(到<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">1 分钟(到<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
package com.android.internal.util;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.util.Xml;
+
import junit.framework.TestCase;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
/**
* Tests for {@link FastXmlSerializer}
*/
+@SmallTest
public class FastXmlSerializerTest extends TestCase {
+ private static final String TAG = "FastXmlSerializerTest";
+
+ private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH TRUE.
+
+ private static final String ROOT_TAG = "root";
+ private static final String ATTR = "attr";
+
public void testEmptyText() throws Exception {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<string name=\"meow\"></string>\n", stream.toString());
}
+
+ private boolean checkPreserved(String description, String str) {
+ boolean ok = true;
+ byte[] data;
+ try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(baos, StandardCharsets.UTF_16.name());
+ out.startDocument(null, true);
+
+ out.startTag(null, ROOT_TAG);
+ out.attribute(null, ATTR, str);
+ out.text(str);
+ out.endTag(null, ROOT_TAG);
+
+ out.endDocument();
+ baos.flush();
+ data = baos.toByteArray();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to serialize: " + description, e);
+ return false;
+ }
+
+ if (ENABLE_DUMP) {
+ Log.d(TAG, "Dump:");
+ Log.d(TAG, new String(data));
+ }
+
+ try (final ByteArrayInputStream baos = new ByteArrayInputStream(data)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(baos, StandardCharsets.UTF_16.name());
+
+ int type;
+ String tag = null;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
+ tag = parser.getName();
+ if (ROOT_TAG.equals(tag)) {
+ String read = parser.getAttributeValue(null, ATTR);
+ if (!str.equals(read)) {
+ Log.e(TAG, "Attribute not preserved: " + description
+ + " input=\"" + str + "\", but read=\"" + read + "\"");
+ ok = false;
+ }
+ }
+ }
+ if (type == XmlPullParser.TEXT && ROOT_TAG.equals(tag)) {
+ String read = parser.getText();
+ if (!str.equals(parser.getText())) {
+ Log.e(TAG, "Text not preserved: " + description
+ + " input=\"" + str + "\", but read=\"" + read + "\"");
+ ok = false;
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to parse: " + description, e);
+ return false;
+ }
+ return ok;
+ }
+
+ private boolean check(String description, String str) throws Exception {
+ boolean ok = false;
+ ok |= checkPreserved(description, str);
+ ok |= checkPreserved(description + " wrapped with spaces" ," " + str + " ");
+ return ok;
+ }
+
+ @LargeTest
+ public void testAllCharacters() throws Exception {
+ boolean ok = true;
+ for (int i = 0; i < 0xffff; i++) {
+ if (0xd800 <= i && i <= 0xdfff) {
+ // Surrogate pair characters.
+ continue;
+ }
+ ok &= check("char: " + i, String.valueOf((char) i));
+ }
+ // Dangling surrogate pairs. We can't preserve them.
+ assertFalse(check("+ud800", "\ud800"));
+ assertFalse(check("+udc00", "\udc00"));
+
+ for (int i = 0xd800; i < 0xdc00; i ++) {
+ for (int j = 0xdc00; j < 0xe000; j++) {
+ ok &= check("char: " + i, String.valueOf((char) i) + String.valueOf((char) j));
+ }
+ }
+ assertTrue("Some tests failed. See logcat for details.", ok);
+ }
}
void closeDevice();
// connects the input port pfd to the specified output port
- void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
+ // Returns the PID of the called process.
+ int connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
MidiDeviceInfo getDeviceInfo();
void setDeviceInfo(in MidiDeviceInfo deviceInfo);
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
}
try {
IBinder token = new Binder();
- mDeviceServer.connectPorts(token, pfd, outputPortNumber);
- // close our copy of the file descriptor
- IoUtils.closeQuietly(pfd);
+ int calleePid = mDeviceServer.connectPorts(token, pfd, outputPortNumber);
+ // If the service is a different Process then it will duplicate the pfd
+ // and we can safely close this one.
+ // But if the service is in the same Process then closing the pfd will
+ // kill the connection. So don't do that.
+ if (calleePid != Process.myPid()) {
+ // close our copy of the file descriptor
+ IoUtils.closeQuietly(pfd);
+ }
+
return new MidiConnection(token, inputPort);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in connectPorts");
}
@Override
- public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
+ public int connectPorts(IBinder token, ParcelFileDescriptor pfd,
int outputPortNumber) {
MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber];
synchronized (mPortClients) {
mPortClients.put(token, client);
}
+ return Process.myPid(); // for caller to detect same process ID
}
@Override
break;
}
- info.mThumbCompressedSize = image_data.thumbnail_length;
+ info.mThumbCompressedSize = image_data.thumbnail.length;
info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
info.mImagePixWidth = image_data.full_width;
info.mImagePixHeight = image_data.full_height;
break;
}
- if (image_data.thumbnail_length == 0) {
+ if (image_data.thumbnail.length == 0) {
// No thumbnail.
break;
}
- result = malloc(image_data.thumbnail_length);
+ result = malloc(image_data.thumbnail.length);
if (result) {
piex::Error err = stream.get()->GetData(
- image_data.thumbnail_offset,
- image_data.thumbnail_length,
+ image_data.thumbnail.offset,
+ image_data.thumbnail.length,
(std::uint8_t *)result);
if (err == piex::Error::kOk) {
- outThumbSize = image_data.thumbnail_length;
+ outThumbSize = image_data.thumbnail.length;
} else {
free(result);
}
--- /dev/null
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/item_doc_background" />
+ <stroke
+ android:width="1dp"
+ android:color="#ff9f9f9f" />
+ <corners
+ android:bottomRightRadius="3dp"
+ android:bottomLeftRadius="3dp"
+ android:topLeftRadius="3dp"
+ android:topRightRadius="3dp"/>
+</shape>
--- /dev/null
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|left"
+ android:background="@drawable/drag_shadow_background">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/root_icon_size"
+ android:layout_height="@dimen/root_icon_size"
+ android:scaleType="centerInside"
+ android:contentDescription="@null"
+ android:duplicateParentState="true"/>
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:textAlignment="viewStart"
+ android:textColor="@color/item_title"
+ android:paddingStart="8dp"/>
+
+</LinearLayout>
<dimen name="drag_shadow_size">120dp</dimen>
<dimen name="grid_item_elevation">2dp</dimen>
<dimen name="max_drawer_width">280dp</dimen>
+
+ <dimen name="drag_shadow_width">160dp</dimen>
+ <dimen name="drag_shadow_height">48dp</dimen>
+
</resources>
<item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
</plurals>
+ <!-- Label text showing user how many items are being dragged. Can be one or more elements. -->
+ <plurals name="elements_dragged">
+ <item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> item</item>
+ <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> items</item>
+ </plurals>
+
<!-- Dialog text shown to users when asking if they want to delete a file (a confirmation) -->
<string name="delete_filename_confirmation_message">Delete \"<xliff:g id="name" example="cat.jpg">%1$s</xliff:g>\"?</string>
<!-- Dialog text shown to users when asking if they want to delete a folder (a confirmation) -->
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import android.widget.Toolbar;
import android.widget.TextView;
+import android.widget.Toolbar;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.DirectoryLoader;
DocumentInfo.fromDirectoryCursor(cursor));
}
- private Drawable getDragShadowIcon(List<DocumentInfo> docs) {
- if (docs.size() == 1) {
- final DocumentInfo doc = docs.get(0);
- return mIconHelper.getDocumentIcon(getActivity(), doc.authority, doc.documentId,
- doc.mimeType, doc.icon);
- }
- return getActivity().getDrawable(R.drawable.ic_doc_generic);
- }
+ private static class DragShadowBuilder extends View.DragShadowBuilder {
+
+ private final Context mContext;
+ private final IconHelper mIconHelper;
+ private final LayoutInflater mInflater;
+ private final View mShadowView;
+ private final TextView mTitle;
+ private final ImageView mIcon;
+ private final int mWidth;
+ private final int mHeight;
- private class DrawableShadowBuilder extends View.DragShadowBuilder {
+ public DragShadowBuilder(Context context, IconHelper iconHelper, List<DocumentInfo> docs) {
+ mContext = context;
+ mIconHelper = iconHelper;
+ mInflater = LayoutInflater.from(context);
- private final Drawable mShadow;
+ mWidth = mContext.getResources().getDimensionPixelSize(R.dimen.drag_shadow_width);
+ mHeight= mContext.getResources().getDimensionPixelSize(R.dimen.drag_shadow_height);
- private final int mShadowDimension;
+ mShadowView = mInflater.inflate(R.layout.drag_shadow_layout, null);
+ mTitle = (TextView) mShadowView.findViewById(android.R.id.title);
+ mIcon = (ImageView) mShadowView.findViewById(android.R.id.icon);
- public DrawableShadowBuilder(Drawable shadow) {
- mShadow = shadow;
- mShadowDimension = getResources().getDimensionPixelSize(
- R.dimen.drag_shadow_size);
- mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension);
+ mTitle.setText(getTitle(docs));
+ mIcon.setImageDrawable(getIcon(docs));
+ }
+
+ private Drawable getIcon(List<DocumentInfo> docs) {
+ if (docs.size() == 1) {
+ final DocumentInfo doc = docs.get(0);
+ return mIconHelper.getDocumentIcon(mContext, doc.authority, doc.documentId,
+ doc.mimeType, doc.icon);
+ }
+ return mContext.getDrawable(R.drawable.ic_doc_generic);
+ }
+
+ private String getTitle(List<DocumentInfo> docs) {
+ if (docs.size() == 1) {
+ final DocumentInfo doc = docs.get(0);
+ return doc.displayName;
+ }
+ return Shared.getQuantityString(mContext, R.plurals.elements_dragged, docs.size());
}
@Override
public void onProvideShadowMetrics(
Point shadowSize, Point shadowTouchPoint) {
- shadowSize.set(mShadowDimension, mShadowDimension);
- shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2);
+ shadowSize.set(mWidth, mHeight);
+ shadowTouchPoint.set(mWidth, mHeight);
}
@Override
public void onDrawShadow(Canvas canvas) {
- mShadow.draw(canvas);
+ Rect r = canvas.getClipBounds();
+ // Calling measure is necessary in order for all child views to get correctly laid out.
+ mShadowView.measure(
+ View.MeasureSpec.makeMeasureSpec(r.right- r.left, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(r.top- r.bottom, View.MeasureSpec.EXACTLY));
+ mShadowView.layout(r.left, r.top, r.right, r.bottom);
+ mShadowView.draw(canvas);
}
}
-
/**
* Abstract task providing support for loading documents *off*
* the main thread. And if it isn't obvious, creating a list
}
v.startDragAndDrop(
mClipper.getClipDataForDocuments(docs),
- new DrawableShadowBuilder(getDragShadowIcon(docs)),
+ new DragShadowBuilder(getActivity(), mIconHelper, docs),
getDisplayState().stack.peek(),
View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
View.DRAG_FLAG_GLOBAL_URI_WRITE
android:singleLine="true"
android:ellipsize="end"
android:visibility="visible"
- android:inputType="textNoSuggestions">
+ android:inputType="number"
+ android:digits="0123456789 ,-">
</com.android.printspooler.widget.CustomErrorEditText>
</LinearLayout>
import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
import android.text.TextWatcher;
import android.util.ArrayMap;
import android.util.ArraySet;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks,
PrintErrorFragment.OnActionListener, PageAdapter.ContentCallbacks,
private static final int MIN_COPIES = 1;
private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);
- private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+");
-
- private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
- "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
-
- private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
- "[\\s]*[0-9]+[\\-]?[\\s]*[0-9]*[\\s]*?(([,])"
- + "[\\s]*[0-9]+[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+");
-
private boolean mIsOptionsUiBound = false;
private final PrinterAvailabilityDetector mPrinterAvailabilityDetector =
new PrinterAvailabilityDetector();
- private final SimpleStringSplitter mStringCommaSplitter = new SimpleStringSplitter(',');
-
private final OnFocusChangeListener mSelectAllOnFocusListener = new SelectAllOnFocusListener();
private PrintSpoolerProvider mSpoolerProvider;
cancelPrint();
}
} else if (view == mMoreOptionsButton) {
- // The selected pages is only applied once the user leaves the text field. A click
- // on this button, does not count as leaving.
- updateSelectedPagesFromTextField();
+ if (mPageRangeEditText.getError() == null) {
+ // The selected pages is only applied once the user leaves the text field. A click
+ // on this button, does not count as leaving.
+ updateSelectedPagesFromTextField();
+ }
if (mCurrentPrinter != null) {
startAdvancedPrintOptionsActivity(mCurrentPrinter);
}
if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
- List<PageRange> pageRanges = new ArrayList<>();
- mStringCommaSplitter.setString(mPageRangeEditText.getText().toString());
-
- while (mStringCommaSplitter.hasNext()) {
- String range = mStringCommaSplitter.next().trim();
- if (TextUtils.isEmpty(range)) {
- continue;
- }
- final int dashIndex = range.indexOf('-');
- final int fromIndex;
- final int toIndex;
-
- if (dashIndex > 0) {
- fromIndex = Integer.parseInt(range.substring(0, dashIndex).trim()) - 1;
- // It is possible that the dash is at the end since the input
- // verification can has to allow the user to keep entering if
- // this would lead to a valid input. So we handle this.
- if (dashIndex < range.length() - 1) {
- String fromString = range.substring(dashIndex + 1, range.length()).trim();
- toIndex = Integer.parseInt(fromString) - 1;
- } else {
- toIndex = fromIndex;
- }
- } else {
- fromIndex = toIndex = Integer.parseInt(range) - 1;
- }
-
- PageRange pageRange = new PageRange(Math.min(fromIndex, toIndex),
- Math.max(fromIndex, toIndex));
- pageRanges.add(pageRange);
- }
-
- PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
- pageRanges.toArray(pageRangesArray);
+ PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+ final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0;
- return PageRangeUtils.normalize(pageRangesArray);
+ return PageRangeUtils.parsePageRanges(mPageRangeEditText.getText(), pageCount);
}
return PageRange.ALL_PAGES_ARRAY;
editText.setSelection(editText.getText().length());
}
- if (view == mPageRangeEditText && !hasFocus) {
+ if (view == mPageRangeEditText && !hasFocus && mPageRangeEditText.getError() == null) {
updateSelectedPagesFromTextField();
}
}
@Override
public void afterTextChanged(Editable editable) {
final boolean hadErrors = hasErrors();
- String text = editable.toString();
- if (TextUtils.isEmpty(text)) {
- if (mPageRangeEditText.getError() == null) {
- mPageRangeEditText.setError("");
- updateOptionsUi();
- }
- return;
- }
+ PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+ final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0;
+ PageRange[] ranges = PageRangeUtils.parsePageRanges(editable, pageCount);
- String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
- if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
+ if (ranges.length == 0) {
if (mPageRangeEditText.getError() == null) {
mPageRangeEditText.setError("");
updateOptionsUi();
return;
}
- PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
- final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0;
-
- // The range
- Matcher matcher = PATTERN_DIGITS.matcher(text);
- while (matcher.find()) {
- String numericString = text.substring(matcher.start(), matcher.end()).trim();
- if (TextUtils.isEmpty(numericString)) {
- continue;
- }
- final int pageIndex = Integer.parseInt(numericString);
- if (pageIndex < 1 || pageIndex > pageCount) {
- if (mPageRangeEditText.getError() == null) {
- mPageRangeEditText.setError("");
- updateOptionsUi();
- }
- return;
- }
- }
-
- // We intentionally do not catch the case of the from page being
- // greater than the to page. When computing the requested pages
- // we just swap them if necessary.
-
if (mPageRangeEditText.getError() != null) {
mPageRangeEditText.setError(null);
updateOptionsUi();
import android.app.Activity;
import android.app.LoaderManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.Loader;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
private static final String KEY_NOT_FIRST_CREATE = "KEY_NOT_FIRST_CREATE";
- /** If there are any enabled print services */
- private boolean mHasEnabledPrintServices;
+ /** The currently enabled print services by their ComponentName */
+ private ArrayMap<ComponentName, PrintServiceInfo> mEnabledPrintServices;
private PrinterRegistry mPrinterRegistry;
setContentView(R.layout.select_printer_activity);
+ mEnabledPrintServices = new ArrayMap<>();
+
mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
LOADER_ID_PRINT_REGISTRY_INT);
}
TextView titleView = (TextView) findViewById(R.id.title);
View progressBar = findViewById(R.id.progress_bar);
- if (!mHasEnabledPrintServices) {
+ if (mEnabledPrintServices.size() > 0) {
titleView.setText(R.string.print_no_print_services);
progressBar.setVisibility(View.GONE);
} else if (adapter.getUnfilteredCount() <= 0) {
@Override
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
- List<PrintServiceInfo> data) {
- if (data == null || data.isEmpty()) {
- mHasEnabledPrintServices = false;
- } else {
- mHasEnabledPrintServices = true;
+ List<PrintServiceInfo> services) {
+ mEnabledPrintServices.clear();
+
+ if (services != null && !services.isEmpty()) {
+ final int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ PrintServiceInfo service = services.get(i);
+
+ mEnabledPrintServices.put(service.getComponentName(), service);
+ }
}
onPrintServicesUpdate();
CharSequence title = printer.getName();
Drawable icon = printer.loadIcon(SelectPrinterActivity.this);
- CharSequence printServiceLabel;
- try {
- PackageInfo packageInfo = getPackageManager().getPackageInfo(
- printer.getId().getServiceName().getPackageName(), 0);
+ PrintServiceInfo service = mEnabledPrintServices.get(printer.getId().getServiceName());
- printServiceLabel = packageInfo.applicationInfo.loadLabel(getPackageManager());
- } catch (NameNotFoundException e) {
- printServiceLabel = null;
+ CharSequence printServiceLabel = null;
+ if (service != null) {
+ printServiceLabel = service.getResolveInfo().loadLabel(getPackageManager())
+ .toString();
}
CharSequence description = printer.getDescription();
import android.print.PageRange;
import android.print.PrintDocumentInfo;
+import android.util.Pair;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
}
/**
+ * Return the next position after {@code pos} that is not a space character.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ *
+ * @return The position of the first space character
+ */
+ private static int readWhiteSpace(CharSequence s, int pos) {
+ while (pos < s.length() && s.charAt(pos) == ' ') {
+ pos++;
+ }
+
+ return pos;
+ }
+
+ /**
+ * Read a number from a string at a certain position.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ *
+ * @return The position after the number + the number read or null if the number was not found
+ */
+ private static Pair<Integer, Integer> readNumber(CharSequence s, int pos) {
+ Integer result = 0;
+ while (pos < s.length() && s.charAt(pos) >= '0' && s.charAt(pos) <= '9') {
+ // Number cannot start with 0
+ if (result == 0 && s.charAt(pos) == '0') {
+ break;
+ }
+ result = result * 10 + (s.charAt(pos) - '0');
+ // Abort on overflow
+ if (result < 0) {
+ break;
+ }
+ pos++;
+ }
+
+ // 0 is not a valid page number
+ if (result == 0) {
+ return new Pair<>(pos, null);
+ } else {
+ return new Pair<>(pos, result);
+ }
+ }
+
+ /**
+ * Read a single character from a string at a certain position.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ * @param expectedChar The character to read
+ *
+ * @return The position after the character + the character read or null if the character was
+ * not found
+ */
+ private static Pair<Integer, Character> readChar(CharSequence s, int pos, char expectedChar) {
+ if (pos < s.length() && s.charAt(pos) == expectedChar) {
+ return new Pair<>(pos + 1, expectedChar);
+ } else {
+ return new Pair<>(pos, null);
+ }
+ }
+
+ /**
+ * Read a page range character from a string at a certain position.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ * @param maxPageNumber The highest page number to accept.
+ *
+ * @return The position after the page range + the page range read or null if the page range was
+ * not found
+ */
+ private static Pair<Integer, PageRange> readRange(CharSequence s, int pos, int maxPageNumber) {
+ Pair<Integer, Integer> retInt;
+ Pair<Integer, Character> retChar;
+
+ Character comma;
+ if (pos == 0) {
+ // When we reading the first range, we do not want to have a comma
+ comma = ',';
+ } else {
+ retChar = readChar(s, pos, ',');
+ pos = retChar.first;
+ comma = retChar.second;
+ }
+
+ pos = readWhiteSpace(s, pos);
+
+ retInt = readNumber(s, pos);
+ pos = retInt.first;
+ Integer start = retInt.second;
+
+ pos = readWhiteSpace(s, pos);
+
+ retChar = readChar(s, pos, '-');
+ pos = retChar.first;
+ Character separator = retChar.second;
+
+ pos = readWhiteSpace(s, pos);
+
+ retInt = readNumber(s, pos);
+ pos = retInt.first;
+ Integer end = retInt.second;
+
+ pos = readWhiteSpace(s, pos);
+
+ if (comma != null &&
+ // range, maybe unbounded
+ ((separator != null && (start != null || end != null)) ||
+ // single page
+ (separator == null && start != null && end == null))) {
+ if (start == null) {
+ start = 1;
+ }
+
+ if (end == null) {
+ if (separator == null) {
+ end = start;
+ } else {
+ end = maxPageNumber;
+ }
+ }
+
+ if (start <= end && start >= 1 && end <= maxPageNumber) {
+ return new Pair<>(pos, new PageRange(start - 1, end - 1));
+ }
+ }
+
+ return new Pair<>(pos, null);
+ }
+
+ /**
+ * Parse a string into an array of page ranges.
+ *
+ * @param s The string to parse
+ * @param maxPageNumber The highest page number to accept.
+ *
+ * @return The parsed ranges or null if the string could not be parsed.
+ */
+ public static PageRange[] parsePageRanges(CharSequence s, int maxPageNumber) {
+ ArrayList<PageRange> ranges = new ArrayList<>();
+
+ int pos = 0;
+ while (pos < s.length()) {
+ Pair<Integer, PageRange> retRange = readRange(s, pos, maxPageNumber);
+
+ if (retRange.second == null) {
+ ranges.clear();
+ break;
+ }
+
+ ranges.add(retRange.second);
+ pos = retRange.first;
+ }
+
+ return PageRangeUtils.normalize(ranges.toArray(new PageRange[ranges.size()]));
+ }
+
+ /**
* Offsets a the start and end of page ranges with the given value.
*
* @param pageRanges The page ranges to offset.
android:name="com.android.systemui.tv.pip.PipOverlayActivity"
android:exported="true"
android:theme="@style/PipTheme"
- android:launchMode="singleTop"
android:taskAffinity=""
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:resizeableActivity="true"
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_close_white" />
+ <item android:drawable="@drawable/ic_close_white"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_close_white"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_close_white" />
</selector>
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+ <item android:drawable="@drawable/ic_fullscreen_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_fullscreen_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
</selector>
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_pause_white_24dp" />
+ <item android:drawable="@drawable/ic_pause_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_pause_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_pause_white_24dp" />
</selector>
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
+ <item android:drawable="@drawable/ic_play_arrow_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_play_arrow_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
</selector>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Početak"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nedavni ekrani"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Nazad"</string>
- <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
- <skip />
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Obavještenja"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Skracenice tastature"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Promijeni način unosa"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacije"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomoć"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Preglednik"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Prikazati sa kontrolama jačine zvuka"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne ometaj"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Prečica za dugmad za Jačinu zvuka"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"میانبرهای صفحهکلید"</string>
<string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"تغییر روش ورودی"</string>
<string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"برنامهها"</string>
- <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"همیار"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"دستیار"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"مرورگر"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"مخاطبین"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"رایانامه"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Inicio"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recentes"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Volver"</string>
- <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
- <skip />
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificacións"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Atallos de teclado"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Cambiar de método de entrada"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicacións"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistente"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar cos controis de volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Non molestar"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Atallo dos botóns de volume"</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Enheten forblir låst til du låser den opp manuelt"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"Motta varsler raskere"</string>
<string name="hidden_notifications_text" msgid="2326409389088668981">"Se dem før du låser opp"</string>
- <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nei, takk"</string>
+ <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nei takk"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Konfigurer"</string>
<string name="zen_mode_and_condition" msgid="4462471036429759903">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="3179845345429841822">"Avslutt nå"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skjermen er låst"</string>
<string name="screen_pinning_description" msgid="3577937698406151604">"På denne måten blir skjermen synlig frem til du låser den opp. Trykk og hold inne Tilbake for å låse opp."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Skjønner"</string>
- <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei, takk"</string>
+ <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei takk"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igjen neste gang du slår den på i innstillingene."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Ecran de pornire"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recente"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Înapoi"</string>
- <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
- <skip />
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificări"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Comenzi rapide de la tastatură"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Comutați metoda de introducere a textului"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicații"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistent"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Agendă"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzică"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Afișează cu comenzile de volum"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Nu deranja"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Comandă rapidă din butoanele de volum"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Главный экран"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Недавние"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
- <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
- <skip />
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Уведомления"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Быстрые клавиши"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Сменить способ ввода"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Приложения"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Помощник"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузер"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакты"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Эл. почта"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Чат"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка."</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календарь"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Показывать при нажатии кнопок регулировки громкости"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не беспокоить"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Кнопки регулировки громкости"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
<string name="battery_low_title" msgid="6456385927409742437">"电池电量偏低"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>。节电助手已开启。"</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>。省电模式已开启。"</string>
<string name="invalid_charger" msgid="4549105996740522523">"不支持USB充电功能。\n只能使用随附的充电器充电。"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电。"</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"仅限使用设备随附的充电器。"</string>
<string name="battery_low_why" msgid="4553600287639198111">"设置"</string>
- <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"要开启节电助手吗?"</string>
+ <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"要开启省电模式吗?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"开启"</string>
- <string name="battery_saver_start_action" msgid="5576697451677486320">"开启节电助手"</string>
+ <string name="battery_saver_start_action" msgid="5576697451677486320">"开启省电模式"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"设置"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"WLAN"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"自动旋转屏幕"</string>
<string name="user_remove_user_title" msgid="4681256956076895559">"是否移除用户?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"此用户的所有应用和数据均将被删除。"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"移除"</string>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"节电助手已开启"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"省电模式已开启"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"降低性能并限制后台流量"</string>
- <string name="battery_saver_notification_action_text" msgid="109158658238110382">"关闭节电助手"</string>
+ <string name="battery_saver_notification_action_text" msgid="109158658238110382">"关闭省电模式"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>将开始截取您的屏幕上显示的所有内容。"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"不再显示"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"全部清除"</string>
<string name="color_revert_title" msgid="4746666545480534663">"确认设置"</string>
<string name="color_revert_message" msgid="9116001069397996691">"部分颜色设置可能会导致此设备无法使用。请点击“确定”确认这些颜色设置,否则,系统将在 10 秒后重置这些设置。"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"电池使用情况"</string>
- <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用节电助手"</string>
- <string name="battery_detail_switch_title" msgid="6285872470260795421">"节电助手"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用省电模式"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"省电模式"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低性能并限制后台流量"</string>
<string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g>按钮"</string>
<string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"主屏幕"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"最近"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"返回"</string>
- <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
- <skip />
- <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
- <skip />
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"通知"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"键盘快捷键"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"切换输入法"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"应用"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"辅助应用"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"浏览器"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"通讯录"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"电子邮件"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即时通讯"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音乐"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日历"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"与音量控件一起显示"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"请勿打扰"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"音量按钮快捷键"</string>
<!-- Extra space around the PIP and its outline in PIP onboarding activity -->
<dimen name="tv_pip_bounds_space">3dp</dimen>
+ <!-- Extra space around the PIP control button icon to match with the focused circle -->
+ <dimen name="tv_pip_button_icon_padding">5dp</dimen>
</resources>
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.tv.animations.FocusAnimationHolder;
import com.android.systemui.recents.tv.views.RecentsTvView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
import com.android.systemui.statusbar.BaseStatusBar;
private boolean mIgnoreAltTabRelease;
private RecentsTvView mRecentsView;
- private FocusAnimationHolder mRecentsFocusAnimationHolder;
private View mPipView;
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- mRecentsFocusAnimationHolder.startFocusLoseAnimation();
- mPipRecentsOverlayManager.requestFocus(
- mTaskStackViewAdapter.getItemCount() > 0);
- } else {
- mRecentsFocusAnimationHolder.startFocusGainAnimation();
- }
+ handlePipViewFocusChange(hasFocus);
}
};
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- mRecentsFocusAnimationHolder = new FocusAnimationHolder(mRecentsView);
mPipView = findViewById(R.id.pip);
// Place mPipView at the PIP bounds for fine tuned focus handling.
- Rect pipBounds = mPipManager.getPipBounds();
+ Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
lp.width = pipBounds.width();
lp.height = pipBounds.height();
if (mPipManager.isPipShown()) {
mPipView.setVisibility(View.VISIBLE);
mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
- mPipView.requestFocus();
+ if (mPipView.hasFocus()) {
+ // This can happen only if the activity is resumed. Ask for reset.
+ handlePipViewFocusChange(true);
+ } else {
+ mPipView.requestFocus();
+ }
} else {
mPipView.setVisibility(View.GONE);
mPipRecentsOverlayManager.removePipRecentsOverlayView();
- mRecentsFocusAnimationHolder.reset();
+ }
+ }
+
+ /**
+ * Handles the PIP view's focus change.
+ * This starts the relevant recents row animation
+ * and give focus to the recents overlay if needed.
+ */
+ private void handlePipViewFocusChange(boolean hasFocus) {
+ mRecentsView.startRecentsRowFocusAnimation(!hasFocus);
+ if (hasFocus) {
+ // When PIP view has focus, recents overlay view will takes the focus
+ // as if it's the part of the Recents UI.
+ mPipRecentsOverlayManager.requestFocus(
+ mTaskStackViewAdapter.getItemCount() > 0);
}
}
}
+++ /dev/null
-/*
- * 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.recents.tv.animations;
-
-import android.content.res.Resources;
-import android.view.View;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.tv.views.TaskCardView;
-
-/**
- * Collections of Recents row's animation depending on the PIP's focus.
- */
-public class FocusAnimationHolder {
- private final float DIM_ALPHA = 0.5f;
-
- private View mRecentsRowView;
- private int mCardYDelta;
- private long mDuration;
-
- public FocusAnimationHolder(View recentsRowView) {
- mRecentsRowView = recentsRowView;
-
- Resources res = recentsRowView.getResources();
- mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
- mDuration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
- }
-
- public void startFocusGainAnimation() {
- mRecentsRowView.animate()
- .setDuration(mDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(1f)
- .translationY(0);
- }
-
- public void startFocusLoseAnimation() {
- mRecentsRowView.animate()
- .setDuration(mDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(DIM_ALPHA)
- .translationY(mCardYDelta);
- }
-
- public void reset() {
- mRecentsRowView.setTransitionAlpha(1f);
- mRecentsRowView.setTranslationY(0);
- }
-}
--- /dev/null
+/*
+ * 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.recents.tv.animations;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+/**
+ * Recents row's focus animation with PIP controls.
+ */
+public class RecentsRowFocusAnimationHolder {
+ private static final float DIM_ALPHA = 0.5f;
+
+ private View mView;
+ private View mTitleView;
+
+ private AnimatorSet mFocusGainAnimatorSet;
+ private AnimatorSet mFocusLoseAnimatorSet;
+
+ public RecentsRowFocusAnimationHolder(View view, View titleView) {
+ mView = view;
+ mTitleView = titleView;
+
+ Resources res = view.getResources();
+ int duration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
+
+ mFocusGainAnimatorSet = new AnimatorSet();
+ mFocusGainAnimatorSet.playTogether(
+ ObjectAnimator.ofFloat(mView, "alpha", 1f),
+ ObjectAnimator.ofFloat(mTitleView, "alpha", 1f));
+ mFocusGainAnimatorSet.setDuration(duration);
+ mFocusGainAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+
+ mFocusLoseAnimatorSet = new AnimatorSet();
+ mFocusLoseAnimatorSet.playTogether(
+ ObjectAnimator.ofFloat(mView, "alpha", DIM_ALPHA),
+ ObjectAnimator.ofFloat(mTitleView, "alpha", 0f));
+ mFocusLoseAnimatorSet.setDuration(duration);
+ mFocusLoseAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ }
+
+ /**
+ * Returns the Recents row's focus change animation.
+ */
+ public Animator getFocusChangeAnimator(boolean hasFocus) {
+ return hasFocus ? mFocusGainAnimatorSet : mFocusLoseAnimatorSet;
+ }
+
+ /**
+ * Resets the views to the initial state immediately.
+ */
+ public void reset() {
+ mView.setAlpha(1f);
+ mTitleView.setAlpha(1f);
+ }
+}
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import java.util.ArrayList;
import java.util.List;
private TaskStack mStack;
private TaskStackHorizontalGridView mTaskStackHorizontalView;
private View mEmptyView;
+ private RecentsRowFocusAnimationHolder mEmptyViewFocusAnimationHolder;
private boolean mAwaitingFirstLayout = true;
private Rect mSystemInsets = new Rect();
private RecentsTvTransitionHelper mTransitionHelper;
LayoutInflater inflater = LayoutInflater.from(context);
mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
+ mEmptyViewFocusAnimationHolder = new RecentsRowFocusAnimationHolder(mEmptyView, null);
+
mHandler = new Handler();
mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
}
mTaskStackHorizontalView.setStack(stack);
}
-
if (stack.getStackTaskCount() > 0) {
hideEmptyView();
} else {
public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
if (mTaskStackHorizontalView != null) {
// Iterate the stack views and try and find the given task.
- List<TaskCardView> taskViews = mTaskStackHorizontalView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int j = 0; j < taskViewCount; j++) {
- TaskCardView tv = taskViews.get(j);
- if (tv.getTask() == task) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
- return true;
- }
+ if (mTaskStackHorizontalView.getChildViewForTask(task) != null) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
+ return true;
}
}
return false;
}
/**
+ * Starts the focus change animation.
+ */
+ public void startRecentsRowFocusAnimation(boolean hasFocus) {
+ if (mEmptyView.getVisibility() == View.VISIBLE) {
+ mEmptyViewFocusAnimationHolder.getFocusChangeAnimator(hasFocus).start();
+ } else {
+ mTaskStackHorizontalView.startRecentsRowFocusAnimation(hasFocus);
+ }
+ }
+
+ /**
* Hides the task stack and shows the empty view.
*/
public void showEmptyView() {
mEmptyView.setVisibility(View.VISIBLE);
- mEmptyView.bringToFront();
+ mTaskStackHorizontalView.setVisibility(View.GONE);
}
/**
* Shows the task stack and hides the empty view.
*/
public void hideEmptyView() {
- mEmptyView.setVisibility(View.INVISIBLE);
+ mEmptyView.setVisibility(View.GONE);
+ mTaskStackHorizontalView.setVisibility(View.VISIBLE);
}
/**
import android.util.TypedValue;
import android.view.Display;
import android.view.KeyEvent;
+import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.systemui.R;
import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
+import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
import com.android.systemui.recents.model.Task;
private ViewFocusAnimator mViewFocusAnimator;
private DismissAnimationsHolder mDismissAnimationsHolder;
+ private RecentsRowFocusAnimationHolder mRecentsRowFocusAnimationHolder;
public TaskCardView(Context context) {
this(context, null);
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
mDismissAnimationsHolder = new DismissAnimationsHolder(this);
+ View title = findViewById(R.id.card_info_field);
+ mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
}
public void init(Task task) {
mDismissAnimationsHolder.startDismissAnimation(listener);
}
+ public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() {
+ return mRecentsRowFocusAnimationHolder;
+ }
+
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
*/
package com.android.systemui.recents.tv.views;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.AttributeSet;
import android.view.View;
* Horizontal Grid View Implementation to show the Task Stack for TV.
*/
public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
-
+ private static final int ANIMATION_DELAY_MS = 50;
+ private static final int MSG_START_RECENT_ROW_FOCUS_ANIMATION = 100;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_START_RECENT_ROW_FOCUS_ANIMATION) {
+ startRecentsRowFocusAnimation(msg.arg1 == 1);
+ }
+ }
+ };
private TaskStack mStack;
- private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
private Task mFocusedTask;
+ private AnimatorSet mRecentsRowFocusAnimation;
public TaskStackHorizontalGridView(Context context) {
this(context, null);
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
}
+
/**
* Resets this view for reuse.
*/
public void reset() {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder().reset();
+ }
+ if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
+ mRecentsRowFocusAnimation.cancel();
+ }
+ mHandler.removeCallbacksAndMessages(null);
requestLayout();
}
* @return Child view for given task
*/
public TaskCardView getChildViewForTask(Task task) {
- List<TaskCardView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskCardView tv = taskViews.get(i);
+ for (int i = 0; i < getChildCount(); i++) {
+ TaskCardView tv = (TaskCardView) getChildAt(i);
if (tv.getTask() == task) {
return tv;
}
return null;
}
- public List<TaskCardView> getTaskViews() {
- return mTaskViews;
+ /**
+ * Starts the focus change animation.
+ */
+ public void startRecentsRowFocusAnimation(final boolean hasFocus) {
+ if (getChildCount() == 0) {
+ // Animation request may happen before view is attached.
+ // Post again with small dealy so animation can be run again later.
+ if (getAdapter().getItemCount() > 0) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_START_RECENT_ROW_FOCUS_ANIMATION, hasFocus ? 1 : 0),
+ ANIMATION_DELAY_MS);
+ }
+ return;
+ }
+ if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
+ mRecentsRowFocusAnimation.cancel();
+ }
+ Animator animator = ((TaskCardView) getChildAt(0)).getRecentsRowFocusAnimationHolder()
+ .getFocusChangeAnimator(hasFocus);
+ mRecentsRowFocusAnimation = new AnimatorSet();
+ AnimatorSet.Builder builder = mRecentsRowFocusAnimation.play(animator);
+ for (int i = 1; i < getChildCount(); i++) {
+ builder.with(((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder()
+ .getFocusChangeAnimator(hasFocus));
+ }
+ mRecentsRowFocusAnimation.start();
}
@Override
- public void onStackTaskAdded(TaskStack stack, Task newTask){
+ public void onStackTaskAdded(TaskStack stack, Task newTask) {
getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
}
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
- getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
-
- @Override
- public void onGlobalLayout() {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
- mHandle.getLeft() + mHandle.getWidth(),
- mHandle.getTop() + mHandle.getHeight()));
- }
- });
}
@Override
return super.onApplyWindowInsets(insets);
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (changed) {
+ mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
+ mHandle.getRight(), mHandle.getBottom()));
+ }
+ }
+
public void setWindowManager(DividerWindowManager windowManager) {
mWindowManager = windowManager;
}
*/
private void showPipOverlay() {
if (DEBUG) Log.d(TAG, "showPipOverlay()");
- Intent intent = new Intent(mContext, PipOverlayActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(PINNED_STACK_ID);
- mContext.startActivity(intent, options.toBundle());
+ PipOverlayActivity.showPipOverlay(mContext);
}
/**
void onPipActivityClosed();
/** Invoked when the PIP menu gets shown. */
void onShowPipMenu();
- /** Invoked when the PIPed activity is returned back to the fullscreen. */
+ /** Invoked when the PIPed activity is about to return back to the fullscreen. */
void onMoveToFullscreen();
/** Invoked when we are above to start resizing the Pip. */
void onPipResizeAboutToStart();
private final PipManager mPipManager = PipManager.getInstance();
private PipControlsView mPipControlsView;
+ private boolean mRestorePipSizeWhenClose;
@Override
protected void onCreate(Bundle bundle) {
mPipManager.addListener(this);
mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
+ mRestorePipSizeWhenClose = true;
+ }
+
+ private void restorePipAndFinish() {
+ if (mRestorePipSizeWhenClose) {
+ // When PIP menu activity is closed, restore to the default position.
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
+ }
+ finish();
}
@Override
public void onPause() {
super.onPause();
- finish();
+ restorePipAndFinish();
}
@Override
}
@Override
+ public void onBackPressed() {
+ restorePipAndFinish();
+ }
+
+ @Override
public void onPipEntered() { }
@Override
@Override
public void onMoveToFullscreen() {
+ // Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds.
+ // This conflicts with restoring PIP position, so disable it.
+ mRestorePipSizeWhenClose = false;
finish();
}
public class PipOverlayActivity extends Activity implements PipManager.Listener {
private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
+ /**
+ * A flag to ensure the single instance of PipOverlayActivity to prevent it from restarting.
+ * Note that {@link PipManager} moves the PIPed activity to fullscreen if the activity is
+ * restarted. It's because the activity may be started by the Launcher or an intent again,
+ * but we don't want do so for the PipOverlayActivity.
+ */
+ private static boolean sActivityCreated;
+
private final PipManager mPipManager = PipManager.getInstance();
private final Handler mHandler = new Handler();
private View mGuideOverlayView;
}
};
+ /**
+ * Shows PIP overlay UI only if it's not there.
+ */
+ static void showPipOverlay(Context context) {
+ if (!sActivityCreated) {
+ Intent intent = new Intent(context, PipOverlayActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchStackId(PINNED_STACK_ID);
+ context.startActivity(intent, options.toBundle());
+ }
+ }
+
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
+ sActivityCreated = true;
setContentView(R.layout.tv_pip_overlay);
mGuideOverlayView = findViewById(R.id.guide_overlay);
mPipManager.addListener(this);
@Override
protected void onDestroy() {
super.onDestroy();
+ sActivityCreated = false;
mHandler.removeCallbacksAndMessages(null);
mPipManager.removeListener(this);
mPipManager.resumePipResizing(
* Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
* is focused.
* This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
- * @param hasRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
+ * @param allowRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
*/
- public void requestFocus(boolean hasRecentsFocusable) {
+ public void requestFocus(boolean allowRecentsFocusable) {
if (!mIsRecentsShown || mIsPipFocusedInRecent) {
return;
}
mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
mPipControlsView.requestFocus();
mPipControlsView.startFocusGainAnimation();
- mRecentsView.setVisibility(hasRecentsFocusable ? View.VISIBLE : View.GONE);
+ mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
}
/**
}
private void waitForCallback(String callback) {
- for (int i = 0; i < 25; i++) {
+ for (int i = 0; i < 50; i++) {
if (mCallbacks.contains(callback)) {
mCallbacks.remove(callback);
return;
}
});
try {
- lock.wait(5000);
+ lock.wait(10000);
} catch (InterruptedException e) {
}
}
}
final boolean showBadge;
final Intent onClickIntent;
- if (provider.maskedBySuspendedPackage) {
- final long identity = Binder.clearCallingIdentity();
- try {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (provider.maskedBySuspendedPackage) {
UserInfo userInfo = mUserManager.getUserInfo(providerUserId);
showBadge = userInfo.isManagedProfile();
onClickIntent = mDevicePolicyManagerInternal.createPackageSuspendedDialogIntent(
providerPackage, providerUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else if (provider.maskedByQuietProfile) {
- showBadge = true;
- onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
- providerUserId);
- } else /* provider.maskedByLockedProfile */ {
- showBadge = true;
- onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
- providerUserId);
- if (onClickIntent != null) {
- onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ } else if (provider.maskedByQuietProfile) {
+ showBadge = true;
+ onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
+ providerUserId);
+ } else /* provider.maskedByLockedProfile */ {
+ showBadge = true;
+ onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
+ providerUserId);
+ if (onClickIntent != null) {
+ onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
+ | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
for (int j = 0; j < widgetCount; j++) {
usageRestrictions.put(usage, r);
}
}
+ notifyWatchersOfChange(code);
}
@Override
pruneUserRestrictionsForToken(token, userHandle);
}
+ notifyWatchersOfChange(code);
+ }
+
+ private void notifyWatchersOfChange(int code) {
final ArrayList<Callback> clonedCallbacks;
synchronized (this) {
ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
// }
updateTcpBufferSizes(networkAgent);
- final boolean flushDns = updateRoutes(newLp, oldLp, netId);
- updateDnses(newLp, oldLp, netId, flushDns);
+ updateRoutes(newLp, oldLp, netId);
+ updateDnses(newLp, oldLp, netId);
updateClat(newLp, oldLp, networkAgent);
if (isDefaultNetwork(networkAgent)) {
return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
}
- private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId,
- boolean flush) {
- if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
- Collection<InetAddress> dnses = newLp.getDnsServers();
- if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
- try {
- mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
- newLp.getDomains());
- } catch (Exception e) {
- loge("Exception in setDnsServersForNetwork: " + e);
- }
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
- if (defaultNai != null && defaultNai.network.netId == netId) {
- setDefaultDnsSystemProperties(dnses);
- }
- flushVmDnsCache();
- } else if (flush) {
- try {
- mNetd.flushNetworkDnsCache(netId);
- } catch (Exception e) {
- loge("Exception in flushNetworkDnsCache: " + e);
- }
- flushVmDnsCache();
+ private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
+ if (oldLp != null && newLp.isIdenticalDnses(oldLp)) {
+ return; // no updating necessary
+ }
+
+ Collection<InetAddress> dnses = newLp.getDnsServers();
+ if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
+ try {
+ mNetd.setDnsServersForNetwork(
+ netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
+ } catch (Exception e) {
+ loge("Exception in setDnsServersForNetwork: " + e);
+ }
+ final NetworkAgentInfo defaultNai = getDefaultNetwork();
+ if (defaultNai != null && defaultNai.network.netId == netId) {
+ setDefaultDnsSystemProperties(dnses);
}
+ flushVmDnsCache();
}
private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
}
@Override
- public void flushNetworkDnsCache(int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("resolver", "flushnet", netId);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
public void setFirewallEnabled(boolean enabled) {
enforceSystemUid();
try {
}
private void scheduleSendRankingUpdate() {
- mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
- Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
- mHandler.sendMessage(m);
+ if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+ Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
+ mHandler.sendMessage(m);
+ }
}
private void handleSendRankingUpdate() {
Log.i(TAG, "Extracting app " + curr + " of " + total + ": " + pkg.packageName);
}
+ if (!isFirstBoot()) {
+ try {
+ ActivityManagerNative.getDefault().showBootMessage(
+ mContext.getResources().getString(R.string.android_upgrading_apk,
+ curr, total), true);
+ } catch (RemoteException e) {
+ }
+ }
+
if (PackageDexOptimizer.canOptimizePackage(pkg)) {
// If the cache was pruned, any compiled odex files will likely be out of date
// and would have to be patched (would be SELF_PATCHOAT, which is deprecated).
@Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
try {
- getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+ WindowState focusedWindow = getFocusedWindow();
+ if (focusedWindow != null && focusedWindow.mClient != null) {
+ getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+ }
} catch (RemoteException e) {
}
}
}
}
+ /**
+ * @return {@code true} if the package is installed and set as always-on, {@code false} if it is
+ * not installed and therefore not available.
+ *
+ * @throws SecurityException if the caller is not a profile or device owner.
+ * @throws UnsupportedException if the package does not support being set as always-on.
+ */
@Override
public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage)
throws SecurityException {
final int userId = mInjector.userHandleGetCallingUserId();
final long token = mInjector.binderClearCallingIdentity();
- try{
+ try {
+ if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
+ return false;
+ }
ConnectivityManager connectivityManager = (ConnectivityManager)
mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- return connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage);
+ if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage)) {
+ throw new UnsupportedOperationException();
+ }
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
+ return true;
}
@Override