Merge "A11y: Show title if ticker is not available" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index be9193e..5e4b2d4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20728,20 +20728,20 @@
field public static final int VP8Level_Version2 = 4; // 0x4
field public static final int VP8Level_Version3 = 8; // 0x8
field public static final int VP8ProfileMain = 1; // 0x1
- field public static final int VP9Level1 = 0; // 0x0
- field public static final int VP9Level11 = 1; // 0x1
- field public static final int VP9Level2 = 2; // 0x2
- field public static final int VP9Level21 = 4; // 0x4
- field public static final int VP9Level3 = 8; // 0x8
- field public static final int VP9Level31 = 16; // 0x10
- field public static final int VP9Level4 = 32; // 0x20
- field public static final int VP9Level41 = 64; // 0x40
- field public static final int VP9Level5 = 128; // 0x80
- field public static final int VP9Level51 = 256; // 0x100
- field public static final int VP9Level52 = 512; // 0x200
- field public static final int VP9Level6 = 1024; // 0x400
- field public static final int VP9Level61 = 2048; // 0x800
- field public static final int VP9Level62 = 4096; // 0x1000
+ field public static final int VP9Level1 = 1; // 0x1
+ field public static final int VP9Level11 = 2; // 0x2
+ field public static final int VP9Level2 = 4; // 0x4
+ field public static final int VP9Level21 = 8; // 0x8
+ field public static final int VP9Level3 = 16; // 0x10
+ field public static final int VP9Level31 = 32; // 0x20
+ field public static final int VP9Level4 = 64; // 0x40
+ field public static final int VP9Level41 = 128; // 0x80
+ field public static final int VP9Level5 = 256; // 0x100
+ field public static final int VP9Level51 = 512; // 0x200
+ field public static final int VP9Level52 = 1024; // 0x400
+ field public static final int VP9Level6 = 2048; // 0x800
+ field public static final int VP9Level61 = 4096; // 0x1000
+ field public static final int VP9Level62 = 8192; // 0x2000
field public static final int VP9Profile0 = 1; // 0x1
field public static final int VP9Profile1 = 2; // 0x2
field public static final int VP9Profile2 = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index b787d86..bde3a8c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -22248,20 +22248,20 @@
field public static final int VP8Level_Version2 = 4; // 0x4
field public static final int VP8Level_Version3 = 8; // 0x8
field public static final int VP8ProfileMain = 1; // 0x1
- field public static final int VP9Level1 = 0; // 0x0
- field public static final int VP9Level11 = 1; // 0x1
- field public static final int VP9Level2 = 2; // 0x2
- field public static final int VP9Level21 = 4; // 0x4
- field public static final int VP9Level3 = 8; // 0x8
- field public static final int VP9Level31 = 16; // 0x10
- field public static final int VP9Level4 = 32; // 0x20
- field public static final int VP9Level41 = 64; // 0x40
- field public static final int VP9Level5 = 128; // 0x80
- field public static final int VP9Level51 = 256; // 0x100
- field public static final int VP9Level52 = 512; // 0x200
- field public static final int VP9Level6 = 1024; // 0x400
- field public static final int VP9Level61 = 2048; // 0x800
- field public static final int VP9Level62 = 4096; // 0x1000
+ field public static final int VP9Level1 = 1; // 0x1
+ field public static final int VP9Level11 = 2; // 0x2
+ field public static final int VP9Level2 = 4; // 0x4
+ field public static final int VP9Level21 = 8; // 0x8
+ field public static final int VP9Level3 = 16; // 0x10
+ field public static final int VP9Level31 = 32; // 0x20
+ field public static final int VP9Level4 = 64; // 0x40
+ field public static final int VP9Level41 = 128; // 0x80
+ field public static final int VP9Level5 = 256; // 0x100
+ field public static final int VP9Level51 = 512; // 0x200
+ field public static final int VP9Level52 = 1024; // 0x400
+ field public static final int VP9Level6 = 2048; // 0x800
+ field public static final int VP9Level61 = 4096; // 0x1000
+ field public static final int VP9Level62 = 8192; // 0x2000
field public static final int VP9Profile0 = 1; // 0x1
field public static final int VP9Profile1 = 2; // 0x2
field public static final int VP9Profile2 = 4; // 0x4
diff --git a/api/test-current.txt b/api/test-current.txt
index 4a8d5f6..9141506 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20796,20 +20796,20 @@
field public static final int VP8Level_Version2 = 4; // 0x4
field public static final int VP8Level_Version3 = 8; // 0x8
field public static final int VP8ProfileMain = 1; // 0x1
- field public static final int VP9Level1 = 0; // 0x0
- field public static final int VP9Level11 = 1; // 0x1
- field public static final int VP9Level2 = 2; // 0x2
- field public static final int VP9Level21 = 4; // 0x4
- field public static final int VP9Level3 = 8; // 0x8
- field public static final int VP9Level31 = 16; // 0x10
- field public static final int VP9Level4 = 32; // 0x20
- field public static final int VP9Level41 = 64; // 0x40
- field public static final int VP9Level5 = 128; // 0x80
- field public static final int VP9Level51 = 256; // 0x100
- field public static final int VP9Level52 = 512; // 0x200
- field public static final int VP9Level6 = 1024; // 0x400
- field public static final int VP9Level61 = 2048; // 0x800
- field public static final int VP9Level62 = 4096; // 0x1000
+ field public static final int VP9Level1 = 1; // 0x1
+ field public static final int VP9Level11 = 2; // 0x2
+ field public static final int VP9Level2 = 4; // 0x4
+ field public static final int VP9Level21 = 8; // 0x8
+ field public static final int VP9Level3 = 16; // 0x10
+ field public static final int VP9Level31 = 32; // 0x20
+ field public static final int VP9Level4 = 64; // 0x40
+ field public static final int VP9Level41 = 128; // 0x80
+ field public static final int VP9Level5 = 256; // 0x100
+ field public static final int VP9Level51 = 512; // 0x200
+ field public static final int VP9Level52 = 1024; // 0x400
+ field public static final int VP9Level6 = 2048; // 0x800
+ field public static final int VP9Level61 = 4096; // 0x1000
+ field public static final int VP9Level62 = 8192; // 0x2000
field public static final int VP9Profile0 = 1; // 0x1
field public static final int VP9Profile1 = 2; // 0x2
field public static final int VP9Profile2 = 4; // 0x4
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d440017..ff8cf66 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3430,6 +3430,8 @@
public static final int FLAG_AND_LOCKED = 1 << 1;
/** {@hide} */
public static final int FLAG_AND_UNLOCKED = 1 << 2;
+ /** {@hide} */
+ public static final int FLAG_AND_UNLOCKING_OR_UNLOCKED = 1 << 3;
/**
* Return whether the given user is actively running. This means that
@@ -3449,26 +3451,6 @@
}
/** {@hide} */
- public boolean isUserRunningAndLocked(int userId) {
- try {
- return ActivityManagerNative.getDefault().isUserRunning(userId,
- ActivityManager.FLAG_AND_LOCKED);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** {@hide} */
- public boolean isUserRunningAndUnlocked(int userId) {
- try {
- return ActivityManagerNative.getDefault().isUserRunning(userId,
- ActivityManager.FLAG_AND_UNLOCKED);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** {@hide} */
public boolean isVrModePackageEnabled(ComponentName component) {
try {
return ActivityManagerNative.getDefault().isVrModePackageEnabled(component);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f7c0b4c..6d805ed 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4617,16 +4617,7 @@
// onConfigurationChanged
int diff = activity.mCurrentConfig.diff(newConfig);
if (diff != 0) {
- // If this activity doesn't handle any of the config changes then don't bother
- // calling onConfigurationChanged as we're going to destroy it.
- // Except in the case where the configuration changed on the activity manager side,
- // but wasn't big enough to cause a resource change so the activity wasn't destroyed.
- // In this case we still want to change the configuration of the activity but not
- // report it to the app.
- if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
- || !reportToActivity) {
- shouldChangeConfig = true;
- }
+ shouldChangeConfig = true;
}
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7d74a12..fa32848 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -72,8 +72,13 @@
public static final int S_IWOTH = 00002;
public static final int S_IXOTH = 00001;
- /** Regular expression for safe filenames: no spaces or metacharacters */
- private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
+ /** Regular expression for safe filenames: no spaces or metacharacters.
+ *
+ * Use a preload holder so that FileUtils can be compile-time initialized.
+ */
+ private static class NoImagePreloadHolder {
+ public static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
+ }
private static final File[] EMPTY = new File[0];
@@ -243,7 +248,7 @@
// Note, we check whether it matches what's known to be safe,
// rather than what's known to be unsafe. Non-ASCII, control
// characters, etc. are all unsafe by default.
- return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
+ return NoImagePreloadHolder.SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dd53cbb..d55201f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -36,7 +36,6 @@
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.storage.StorageManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.view.WindowManager.LayoutParams;
@@ -984,10 +983,27 @@
/** {@hide} */
public boolean isUserUnlocked(@UserIdInt int userId) {
- // TODO: eventually pivot this back to look at ActivityManager state,
- // but there is race where we can start a non-encryption-aware launcher
- // before that lifecycle has entered the running unlocked state.
- return mContext.getSystemService(StorageManager.class).isUserKeyUnlocked(userId);
+ try {
+ return ActivityManagerNative.getDefault().isUserRunning(userId,
+ ActivityManager.FLAG_AND_UNLOCKED);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /** {@hide} */
+ public boolean isUserUnlockingOrUnlocked(UserHandle user) {
+ return isUserUnlockingOrUnlocked(user.getIdentifier());
+ }
+
+ /** {@hide} */
+ public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
+ try {
+ return ActivityManagerNative.getDefault().isUserRunning(userId,
+ ActivityManager.FLAG_AND_UNLOCKING_OR_UNLOCKED);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f68e227..fbf7b26 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -1062,11 +1063,20 @@
}
/** {@hide} */
- public boolean isUserKeyUnlocked(int userId) {
+ public static boolean isUserKeyUnlocked(int userId) {
+ final IMountService mount = IMountService.Stub
+ .asInterface(ServiceManager.getService("mount"));
+ if (mount == null) {
+ Slog.w(TAG, "Early during boot, assuming locked");
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
try {
- return mMountService.isUserKeyUnlocked(userId);
+ return mount.isUserKeyUnlocked(userId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ throw e.rethrowAsRuntimeException();
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 8a0759f..893eb37 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -824,6 +824,8 @@
UserHandle user, ContentValues values) {
final ContentResolver resolver = context.getContentResolver();
+ // Since we're doing this operation on behalf of an app, we only
+ // want to use the actual "unlocked" state.
final Uri uri = ContentProvider.maybeAddUserId(
userManager.isUserUnlocked(user) ? CONTENT_URI : SHADOW_CONTENT_URI,
user.getIdentifier());
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b1bf355..d98e217 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6869,14 +6869,6 @@
"hdmi_control_auto_device_off_enabled";
/**
- * Whether to use the DHCP client from Lollipop and earlier instead of the newer Android DHCP
- * client.
- * (0 = false, 1 = true)
- * @hide
- */
- public static final String LEGACY_DHCP_CLIENT = "legacy_dhcp_client";
-
- /**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)
* @hide
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4737e9b..2d3b6ab 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -284,6 +284,13 @@
* currently set for that origin. The host application should invoke the
* specified callback with the desired permission state. See
* {@link GeolocationPermissions} for details.
+ *
+ * <p>Note that for applications targeting Android N and later SDKs
+ * (API level > {@link android.os.Build.VERSION_CODES#M})
+ * this method is only called for requests originating from secure
+ * origins such as https. On non-secure origins geolocation requests
+ * are automatically denied.</p>
+ *
* @param origin The origin of the web content attempting to use the
* Geolocation API.
* @param callback The callback to use to set the permission state for the
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ada7731..f54edf1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -255,6 +255,16 @@
* is loading.
* </p>
*
+ * <h3>HTML5 Geolocation API support</h3>
+ *
+ * <p>For applications targeting Android N and later releases
+ * (API level > {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only supported on
+ * secure origins such as https. For such applications requests to geolocation api on non-secure
+ * origins are automatically denied without invoking the corresponding
+ * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
+ * method.
+ * </p>
+ *
* <h3>Layout size</h3>
* <p>
* It is recommended to set the WebView layout height to a fixed value or to
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index b6200a1..c21f1df 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -38,6 +38,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.RadialTimePickerView.OnValueSelectedListener;
import com.android.internal.R;
import com.android.internal.widget.NumericTextView;
@@ -48,8 +49,7 @@
/**
* A delegate implementing the radial clock-based TimePicker.
*/
-class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate implements
- RadialTimePickerView.OnValueSelectedListener {
+class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
/**
* Delay in milliseconds before valid but potentially incomplete, for
* example "1" but not "12", keyboard edits are propagated from the
@@ -88,8 +88,8 @@
private boolean mIsEnabled = true;
private boolean mAllowAutoAdvance;
- private int mInitialHourOfDay;
- private int mInitialMinute;
+ private int mCurrentHour;
+ private int mCurrentMinute;
private boolean mIs24Hour;
private boolean mIsAmPmAtStart;
@@ -189,8 +189,7 @@
mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(R.id.radial_picker);
mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
-
- setupListeners();
+ mRadialTimePickerView.setOnValueSelectedListener(mOnValueSelectedListener);
mAllowAutoAdvance = true;
@@ -324,28 +323,24 @@
}
private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) {
- mInitialHourOfDay = hourOfDay;
- mInitialMinute = minute;
+ mCurrentHour = hourOfDay;
+ mCurrentMinute = minute;
mIs24Hour = is24HourView;
updateUI(index);
}
- private void setupListeners() {
- mRadialTimePickerView.setOnValueSelectedListener(this);
- }
-
private void updateUI(int index) {
updateHeaderAmPm();
- updateHeaderHour(mInitialHourOfDay, false);
+ updateHeaderHour(mCurrentHour, false);
updateHeaderSeparator();
- updateHeaderMinute(mInitialMinute, false);
+ updateHeaderMinute(mCurrentMinute, false);
updateRadialPicker(index);
mDelegator.invalidate();
}
private void updateRadialPicker(int index) {
- mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24Hour);
+ mRadialTimePickerView.initialize(mCurrentHour, mCurrentMinute, mIs24Hour);
setCurrentItemShowing(index, false, true);
}
@@ -358,7 +353,7 @@
final boolean isAmPmAtStart = dateTimePattern.startsWith("a");
setAmPmAtStart(isAmPmAtStart);
- updateAmPmLabelStates(mInitialHourOfDay < 12 ? AM : PM);
+ updateAmPmLabelStates(mCurrentHour < 12 ? AM : PM);
}
}
@@ -388,15 +383,25 @@
*/
@Override
public void setHour(int hour) {
- if (mInitialHourOfDay != hour) {
- mInitialHourOfDay = hour;
- updateHeaderHour(hour, true);
- updateHeaderAmPm();
- mRadialTimePickerView.setCurrentHour(hour);
- mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
- mDelegator.invalidate();
- onTimeChanged();
+ setHourInternal(hour, false, true);
+ }
+
+ private void setHourInternal(int hour, boolean isFromPicker, boolean announce) {
+ if (mCurrentHour == hour) {
+ return;
}
+
+ mCurrentHour = hour;
+ updateHeaderHour(hour, announce);
+ updateHeaderAmPm();
+
+ if (!isFromPicker) {
+ mRadialTimePickerView.setCurrentHour(hour);
+ mRadialTimePickerView.setAmOrPm(hour < 12 ? AM : PM);
+ }
+
+ mDelegator.invalidate();
+ onTimeChanged();
}
/**
@@ -421,13 +426,23 @@
*/
@Override
public void setMinute(int minute) {
- if (mInitialMinute != minute) {
- mInitialMinute = minute;
- updateHeaderMinute(minute, true);
- mRadialTimePickerView.setCurrentMinute(minute);
- mDelegator.invalidate();
- onTimeChanged();
+ setMinuteInternal(minute, false);
+ }
+
+ private void setMinuteInternal(int minute, boolean isFromPicker) {
+ if (mCurrentMinute == minute) {
+ return;
}
+
+ mCurrentMinute = minute;
+ updateHeaderMinute(minute, true);
+
+ if (!isFromPicker) {
+ mRadialTimePickerView.setCurrentMinute(minute);
+ }
+
+ mDelegator.invalidate();
+ onTimeChanged();
}
/**
@@ -448,7 +463,7 @@
public void setIs24Hour(boolean is24Hour) {
if (mIs24Hour != is24Hour) {
mIs24Hour = is24Hour;
- mInitialHourOfDay = getHour();
+ mCurrentHour = getHour();
updateHourFormat();
updateUI(mRadialTimePickerView.getCurrentItemShowing());
@@ -563,34 +578,6 @@
}
/**
- * Called by the picker for updating the header display.
- */
- @Override
- public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
- switch (pickerIndex) {
- case HOUR_INDEX:
- if (mAllowAutoAdvance && autoAdvance) {
- updateHeaderHour(newValue, false);
- setCurrentItemShowing(MINUTE_INDEX, true, false);
- mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes);
- } else {
- updateHeaderHour(newValue, true);
- }
- break;
- case MINUTE_INDEX:
- updateHeaderMinute(newValue, true);
- break;
- case AMPM_INDEX:
- updateAmPmLabelStates(newValue);
- break;
- }
-
- if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
- }
- }
-
- /**
* Converts hour-of-day (0-23) time into a localized hour number.
* <p>
* The localized value may be in the range (0-23), (1-24), (0-11), or
@@ -702,11 +689,43 @@
private void setAmOrPm(int amOrPm) {
updateAmPmLabelStates(amOrPm);
- if (mRadialTimePickerView.setAmOrPm(amOrPm) && mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
+ if (mRadialTimePickerView.setAmOrPm(amOrPm)) {
+ mCurrentHour = getHour();
+
+ if (mOnTimeChangedListener != null) {
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
+ }
}
}
+ /** Listener for RadialTimePickerView interaction. */
+ private final OnValueSelectedListener mOnValueSelectedListener = new OnValueSelectedListener() {
+ @Override
+ public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
+ switch (pickerIndex) {
+ case HOUR_INDEX:
+ final boolean isTransition = mAllowAutoAdvance && autoAdvance;
+ setHourInternal(newValue, true, !isTransition);
+ if (isTransition) {
+ setCurrentItemShowing(MINUTE_INDEX, true, false);
+ mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes);
+ }
+ break;
+ case MINUTE_INDEX:
+ setMinuteInternal(newValue, true);
+ break;
+ case AMPM_INDEX:
+ updateAmPmLabelStates(newValue);
+ break;
+ }
+
+ if (mOnTimeChangedListener != null) {
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
+ }
+ }
+ };
+
+ /** Listener for keyboard interaction. */
private final OnValueChangedListener mDigitEnteredListener = new OnValueChangedListener() {
@Override
public void onValueChanged(NumericTextView view, int value,
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 7bd64e5..c6b6a7fb 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -45,6 +45,7 @@
sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ sPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
sPackageFilt.addDataScheme("package");
sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
@@ -275,6 +276,9 @@
public void onFinishPackageChanges() {
}
+ public void onPackageDataCleared(String packageName, int uid) {
+ }
+
public int getChangingUserId() {
return mChangeUserId;
}
@@ -365,6 +369,12 @@
}
onPackageModified(pkg);
}
+ } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
+ String pkg = getPackageName(intent);
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+ if (pkg != null) {
+ onPackageDataCleared(pkg, uid);
+ }
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
mChangeType = PACKAGE_TEMPORARY_CHANGE;
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index b44baa2..ccdb024 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -24,6 +24,8 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
@@ -165,7 +167,17 @@
checkToolbarInitialized();
mContentRectOnScreen.set(mContentRect);
- mContentRectOnScreen.offset(mViewPositionOnScreen[0], mViewPositionOnScreen[1]);
+
+ // Offset the content rect into screen coordinates, taking into account any transformations
+ // that may be applied to the originating view or its ancestors.
+ final ViewParent parent = mOriginatingView.getParent();
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).getChildVisibleRect(
+ mOriginatingView, mContentRectOnScreen, null /* offset */);
+ mContentRectOnScreen.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]);
+ } else {
+ mContentRectOnScreen.offset(mViewPositionOnScreen[0], mViewPositionOnScreen[1]);
+ }
if (isContentRectWithinBounds()) {
mFloatingToolbarVisibilityHelper.setOutOfBounds(false);
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 1848fe8..bc12391 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -592,9 +592,11 @@
private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
refreshViewPort();
- int x = contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2;
- // Update x so that the toolbar isn't rendered behind the nav bar in landscape.
- x = Math.max(0, Math.min(x, mViewPortOnScreen.right - mPopupWindow.getWidth()));
+ // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
+ // landscape.
+ final int x = Math.min(
+ contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+ mViewPortOnScreen.right - mPopupWindow.getWidth());
final int y;
@@ -684,7 +686,8 @@
int rootViewTopOnWindow = mTmpCoords[1];
int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
- mCoordsOnWindow.set(x - windowLeftOnScreen, y - windowTopOnScreen);
+ mCoordsOnWindow.set(
+ Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
}
/**
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4d86a92..14c17f3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2530,6 +2530,9 @@
<java-symbol type="string" name="profile_encrypted_message" />
<java-symbol type="drawable" name="ic_user_secure" />
+ <java-symbol type="string" name="android_upgrading_notification_title" />
+ <java-symbol type="string" name="android_upgrading_notification_body" />
+
<java-symbol type="string" name="usb_mtp_launch_notification_title" />
<java-symbol type="string" name="usb_mtp_launch_notification_description" />
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 8a7d39b..a26850f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -172,6 +172,10 @@
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save-except-idle package="com.android.providers.downloads" />
+ <!-- These are the standard packages that are white-listed to always have internet
+ access while in data mode, even if they aren't in the foreground. -->
+ <allow-in-data-usage-save package="com.android.providers.downloads" />
+
<!-- These are the packages that are white-listed to be able to run as system user -->
<system-user-whitelisted-app package="com.android.settings" />
diff --git a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..47b1d34
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..6af4d70
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..de3eec2
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..98549b3
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..7a3d7ed
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..52e3918
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..a2d00cc
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..273728a
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..f61d5b9
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg
Binary files differ
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index adb31b8..545923f 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -24,11 +24,11 @@
- from: /tools/debugging/debugging-projects-cmdline.html
to: /tools/debugging/index.html
- from: /sdk/compatibility-library.html
- to: /tools/support-library/index.html
+ to: /go/libraries/support-library/index.html
- from: /tools/extras/support-library.html
- to: /tools/support-library/index.html
+ to: /go/libraries/support-library/index.html
- from: /training/basics/fragments/support-lib.html
- to: /tools/support-library/setup.html
+ to: /go/libraries/support-library/setup.html
- from: /sdk/eclipse-adt.html
to: /tools/sdk/eclipse-adt.html
- from: /sdk/RELEASENOTES.html
@@ -72,13 +72,11 @@
- from: /tools/publishing/app-signing-eclipse.html
to: /tools/help/adt.html
- from: /tools/help/uiautomator/.*
- to: /tools/testing-support-library/index.html
+ to: /go/libraries/testing-support/library/
- from: /tools/testing/testing_ui.html
to: /training/testing/ui-testing/index.html
- from: /tools/testing/activity_test.html
to: /training/testing/ui-testing/index.html
-- from: /tools/data-binding/index.html
- to: /tools/data-binding/guide.html
- from: /tools/testing/what_to_test.html
to: /distribute/essentials/quality/core.html
- from: /tools/testing/testing_accessibility.html
@@ -725,3 +723,14 @@
to: https://commondatastorage.googleapis.com/androiddevelopers/shareables/
- from: /downloads/
to: https://commondatastorage.googleapis.com/androiddevelopers/
+
+# Redirects for the new go/libraries/ area
+
+- from: /tools/support-library
+ to: /go/libraries/support-library
+
+- from: /tools/data-binding/...
+ to: /go/libraries/data-binding
+
+- from: /tools/testing-support-library
+ to: /go/libraries/testing-support-library
diff --git a/docs/html/about/_book.yaml b/docs/html/about/_book.yaml
index 04150bd..fdbe53f 100644
--- a/docs/html/about/_book.yaml
+++ b/docs/html/about/_book.yaml
@@ -16,13 +16,19 @@
path: /about/versions/android-5.1.html
- title: Android 5.0 APIs
path: /about/versions/android-5.0.html
- custom_link_attributes:
- - es-lang="API de Android 5.0"
- - ja-lang="Android 5.0 API"
- - ko-lang="Android 5.0 API"
- - ru-lang="API для Android 5.0"
- - zh-cn-lang="Android 5.0 API"
- - zh-tw-lang="Android 5.0 API"
+ path_attributes:
+ - name: es-lang
+ value: API de Android 5.0
+ - name: ja-lang
+ value: Android 5.0 API
+ - name: ko-lang
+ value: Android 5.0 API
+ - name: ru-lang
+ value: API для Android 5.0
+ - name: zh-cn-lang
+ value: Android 5.0 API
+ - name: zh-tw-lang
+ value: Android 5.0 API
- title: Android 5.0 Changes
path: /about/versions/android-5.0-changes.html
diff --git a/docs/html/design/_book.yaml b/docs/html/design/_book.yaml
index 4575475..df5406f 100644
--- a/docs/html/design/_book.yaml
+++ b/docs/html/design/_book.yaml
@@ -4,24 +4,35 @@
section:
- title: Design Principles
path: /design/get-started/principles.html
- custom_link_attributes:
- - es-lang="Principios de diseño para Android"
- - ja-lang="Android デザイン指針"
- - ko-lang="Android 디자인 원칙"
- - pt-br-lang="Princípios de projeto para Android"
- - ru-lang="Принципы проектирования Android"
- - zh-cn-lang="Android 设计原则"
- - zh-tw-lang="Android 設計原則"
+ path_attributes:
+ - name: es-lang
+ value: Principios de diseño para Android
+ - name: ja-lang
+ value: Android デザイン指針
+ - name: ko-lang
+ value: Android 디자인 원칙
+ - name: pt-br-lang
+ value: Princípios de projeto para Android
+ - name: ru-lang
+ value: Принципы проектирования Android
+ - name: zh-cn-lang
+ value: Android 设计原则
+ - name: zh-tw-lang
+ value: Android 設計原則
- title: New in Android
path: /design/patterns/new.html
- title: Material for Android
path: /design/material/index.html
- custom_link_attributes:
- - ja-lang="マテリアル デザイン"
- - ko-lang="머티어리얼 디자인"
- - zh-cn-lang="材料设计"
- - zh-tw-lang="材料設計"
+ path_attributes:
+ - name: ja-lang
+ value: マテリアル デザイン
+ - name: ko-lang
+ value: 머티어리얼 디자인
+ - name: zh-cn-lang
+ value: 材料设计
+ - name: zh-tw-lang
+ value: 材料設計
- title: Devices
path: /design/devices.html
@@ -64,24 +75,38 @@
path: /design/style/devices-displays.html
- title: Navigation
path: /design/patterns/navigation.html
- custom_link_attributes:
- - es-lang="Navegación con los botones Back y Up"
- - ja-lang="Back と Up を使用したナビゲーション"
- - ko-lang="뒤로 및 위로 탐색 기능이 포함된 탐색"
- - pt-br-lang="Navegação com Voltar e Para cima"
- - ru-lang="Навигация с помощью кнопок \"Назад\" и \"Вверх\""
- - zh-cn-lang="使用返回和向上导航"
- - zh-tw-lang="使用 [返回] 及 [上一層] 導覽"
+ path_attributes:
+ - name: es-lang
+ value: Navegación con los botones Back y Up
+ - name: ja-lang
+ value: Back と Up を使用したナビゲーション
+ - name: ko-lang
+ value: 뒤로 및 위로 탐색 기능이 포함된 탐색
+ - name: pt-br-lang
+ value: Navegação com Voltar e Para cima
+ - name: ru-lang
+ value: Навигация с помощью кнопок "Назад" и "Вверх"
+ - name: zh-cn-lang
+ value: 使用返回和向上导航
+ - name: zh-tw-lang
+ value: 使用 [返回] 及 [上一層] 導覽
- title: Notifications
path: /design/patterns/notifications.html
- custom_link_attributes:
- - es-lang="Notificaciones"
- - ja-lang="通知"
- - ko-lang="알림"
- - pt-br-lang="Notificações"
- - ru-lang="Уведомления"
- - zh-cn-lang="通知"
- - zh-tw-lang="通知"
+ path_attributes:
+ - name: es-lang
+ value: Notificaciones
+ - name: ja-lang
+ value: 通知
+ - name: ko-lang
+ value: 알림
+ - name: pt-br-lang
+ value: Notificações
+ - name: ru-lang
+ value: Уведомления
+ - name: zh-cn-lang
+ value: 通知
+ - name: zh-tw-lang
+ value: 通知
- title: Widgets
path: /design/patterns/widgets.html
- title: Swipe Views
@@ -90,14 +115,21 @@
path: /design/patterns/fullscreen.html
- title: Confirming & Acknowledging
path: /design/patterns/confirming-acknowledging.html
- custom_link_attributes:
- - es-lang="Confirmación y reconocimiento"
- - ja-lang="確認と通知"
- - ko-lang="확인 및 승인하기"
- - pt-br-lang="Confirmação e reconhecimento"
- - ru-lang="Подтверждение и уведомление"
- - zh-cn-lang="确认和确知"
- - zh-tw-lang="確認及確認完成"
+ path_attributes:
+ - name: es-lang
+ value: Confirmación y reconocimiento
+ - name: ja-lang
+ value: 確認と通知
+ - name: ko-lang
+ value: 확인 및 승인하기
+ - name: pt-br-lang
+ value: Confirmação e reconhecimento
+ - name: ru-lang
+ value: Подтверждение и уведомление
+ - name: zh-cn-lang
+ value: 确认和确知
+ - name: zh-tw-lang
+ value: 確認及確認完成
- title: Pure Android
path: /design/patterns/pure-android.html
- title: Compatibility
diff --git a/docs/html/distribute/essentials/_book.yaml b/docs/html/distribute/essentials/_book.yaml
index e8b7811..6a2c8f5 100644
--- a/docs/html/distribute/essentials/_book.yaml
+++ b/docs/html/distribute/essentials/_book.yaml
@@ -1,13 +1,15 @@
toc:
- title: Core App Quality
path: /distribute/essentials/quality/core.html
- custom_link_attributes:
- - zh-cn-lang="应用的核心质量"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 应用的核心质量
- title: Tablet App Quality
path: /distribute/essentials/quality/tablets.html
- custom_link_attributes:
- - zh-cn-lang="平板电脑应用的质量"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 平板电脑应用的质量
- title: Wear App Quality
path: /distribute/essentials/quality/wear.html
@@ -20,31 +22,36 @@
- title: Launch Checklist
path: /distribute/tools/launch-checklist.html
- custom_link_attributes:
- - zh-cn-lang="发布检查清单"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 发布检查清单
- title: Localization Checklist
path: /distribute/tools/localization-checklist.html
- custom_link_attributes:
- - zh-cn-lang="本地化检查清单"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 本地化检查清单
- title: Brand Guidelines
path: /distribute/tools/promote/brand.html
- custom_link_attributes:
- - zh-cn-lang="品牌指南"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 品牌指南
- title: Device Art Generator
path: /distribute/tools/promote/device-art.html
- title: Google Play Badges
path: https://play.google.com/intl/en_us/badges/
- custom_link_attributes:
- - zh-cn-lang="Google Play 徽章生成器"
+ path_attributes:
+ - name: zh-cn-lang
+ value: Google Play 徽章生成器
- title: Linking to Your Products
path: /distribute/tools/promote/linking.html
- custom_link_attributes:
- - zh-cn-lang="链接到您的商品"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 链接到您的商品
- title: Alternative Distribution
path: /distribute/tools/open-distribution.html
diff --git a/docs/html/distribute/googleplay/_book.yaml b/docs/html/distribute/googleplay/_book.yaml
index 009d1c07..ede18f4 100644
--- a/docs/html/distribute/googleplay/_book.yaml
+++ b/docs/html/distribute/googleplay/_book.yaml
@@ -1,75 +1,119 @@
toc:
- title: The Google Play Opportunity
path: /distribute/googleplay/about.html
- custom_link_attributes:
- - es-lang="La oportunidad de Google Play"
- - ja-lang="Google Play の可能性"
- - ko-lang="Google Play 활용 기회"
- - pt-br-lang="A oportunidade do Google Play"
- - ru-lang="Возможности Google Play"
- - zh-cn-lang="Google Play 蕴藏的机会"
- - zh-tw-lang="Google Play商機"
+ path_attributes:
+ - name: es-lang
+ value: La oportunidad de Google Play
+ - name: ja-lang
+ value: Google Play の可能性
+ - name: ko-lang
+ value: Google Play 활용 기회
+ - name: pt-br-lang
+ value: A oportunidade do Google Play
+ - name: ru-lang
+ value: Возможности Google Play
+ - name: zh-cn-lang
+ value: Google Play 蕴藏的机会
+ - name: zh-tw-lang
+ value: Google Play商機
- title: Get Started with Publishing
path: /distribute/googleplay/start.html
- custom_link_attributes:
- - es-lang="Comienza a publicar"
- - ja-lang="アプリを公開する"
- - ko-lang="게시 시작하기"
- - pt-br-lang="Introdução à publicação"
- - ru-lang="Первые шаги в публикациях"
- - zh-cn-lang="开始发布"
- - zh-tw-lang="開始發行"
+ path_attributes:
+ - name: es-lang
+ value: Comienza a publicar
+ - name: ja-lang
+ value: アプリを公開する
+ - name: ko-lang
+ value: 게시 시작하기
+ - name: pt-br-lang
+ value: Introdução à publicação
+ - name: ru-lang
+ value: Первые шаги в публикациях
+ - name: zh-cn-lang
+ value: 开始发布
+ - name: zh-tw-lang
+ value: 開始發行
- title: Developer Console
path: /distribute/googleplay/developer-console.html
- custom_link_attributes:
- - es-lang="Consola para desarrolladores"
- - ja-lang="デベロッパー コンソール"
- - ko-lang="개발자 콘솔"
- - pt-br-lang="Console do Desenvolvedor"
- - ru-lang="Консоль разработчика"
- - zh-cn-lang="开发者控制台"
+ path_attributes:
+ - name: es-lang
+ value: Consola para desarrolladores
+ - name: ja-lang
+ value: デベロッパー コンソール
+ - name: ko-lang
+ value: 개발자 콘솔
+ - name: pt-br-lang
+ value: Console do Desenvolvedor
+ - name: ru-lang
+ value: Консоль разработчика
+ - name: zh-cn-lang
+ value: 开发者控制台
- title: Distribute to Android Wear
path: /distribute/googleplay/wear.html
- custom_link_attributes:
- - es-lang="Distribución para Android Wear"
- - ja-lang="Android Wear への配布"
- - ko-lang="Android Wear에 배포"
- - pt-br-lang="Distribuindo para Android Wear"
- - ru-lang="Распространение приложений Android Wear"
- - zh-cn-lang="分发到 Android Wear"
- - zh-tw-lang="散佈至 Android Wear"
+ path_attributes:
+ - name: es-lang
+ value: Distribución para Android Wear
+ - name: ja-lang
+ value: Android Wear への配布
+ - name: ko-lang
+ value: Android Wear에 배포
+ - name: pt-br-lang
+ value: Distribuindo para Android Wear
+ - name: ru-lang
+ value: Распространение приложений Android Wear
+ - name: zh-cn-lang
+ value: 分发到 Android Wear
+ - name: zh-tw-lang
+ value: 散佈至 Android Wear
- title: Distribute to Android TV
path: /distribute/googleplay/tv.html
- custom_link_attributes:
- - es-lang="Distribución para Android TV"
- - ja-lang="Android TV への配布"
- - ko-lang="Android TV에 배포"
- - pt-br-lang="Distribuindo para Android TV"
- - ru-lang="Распространение приложений в Android TV"
- - zh-cn-lang="分发到 Android TV"
- - zh-tw-lang="散佈至 Android 電視"
+ path_attributes:
+ - name: es-lang
+ value: Distribución para Android TV
+ - name: ja-lang
+ value: Android TV への配布
+ - name: ko-lang
+ value: Android TV에 배포
+ - name: pt-br-lang
+ value: Distribuindo para Android TV
+ - name: ru-lang
+ value: Распространение приложений в Android TV
+ - name: zh-cn-lang
+ value: 分发到 Android TV
+ - name: zh-tw-lang
+ value: 散佈至 Android 電視
- title: Distribute to Android Auto
path: /distribute/googleplay/auto.html
- custom_link_attributes:
- - es-lang="Distribución para Android Auto"
- - ja-lang="Android Auto への配布"
- - ko-lang="Android Auto에 배포"
- - pt-br-lang="Distribuindo para o Android Auto"
- - ru-lang="Распространение приложений для Android Auto"
- - zh-cn-lang="分发到 Android Auto"
- - zh-tw-lang="散佈至 Android Auto"
+ path_attributes:
+ - name: es-lang
+ value: Distribución para Android Auto
+ - name: ja-lang
+ value: Android Auto への配布
+ - name: ko-lang
+ value: Android Auto에 배포
+ - name: pt-br-lang
+ value: Distribuindo para o Android Auto
+ - name: ru-lang
+ value: Распространение приложений для Android Auto
+ - name: zh-cn-lang
+ value: 分发到 Android Auto
+ - name: zh-tw-lang
+ value: 散佈至 Android Auto
- title: Designed for Families
path: /distribute/googleplay/families/about.html
- custom_link_attributes:
- - es-lang="Diseñado para la familia"
- - ru-lang="Для всей семьи"
- - zh-cn-lang="为家庭设计"
+ path_attributes:
+ - name: es-lang
+ value: Diseñado para la familia
+ - name: ru-lang
+ value: Для всей семьи
+ - name: zh-cn-lang
+ value: 为家庭设计
- title: Google Play for Work
path: /distribute/googleplay/work/about.html
@@ -90,11 +134,18 @@
- title: Find Success on Google Play
path: /distribute/googleplay/guide.html
- custom_link_attributes:
- - es-lang="Cómo tener éxito en Google Play"
- - ja-lang="Google Play で成功を手にする"
- - ko-lang="Google Play에서 성공 모색"
- - pt-br-lang="Obtendo sucesso no Google Play"
- - ru-lang="Найдите свой путь к успеху в Google Play"
- - zh-cn-lang="在 Google Play 上取得成功"
- - zh-tw-lang="在 Google Play 上尋找成功"
+ path_attributes:
+ - name: es-lang
+ value: Cómo tener éxito en Google Play
+ - name: ja-lang
+ value: Google Play で成功を手にする
+ - name: ko-lang
+ value: Google Play에서 성공 모색
+ - name: pt-br-lang
+ value: Obtendo sucesso no Google Play
+ - name: ru-lang
+ value: Найдите свой путь к успеху в Google Play
+ - name: zh-cn-lang
+ value: 在 Google Play 上取得成功
+ - name: zh-tw-lang
+ value: 在 Google Play 上尋找成功
diff --git a/docs/html/distribute/tools/_book.yaml b/docs/html/distribute/tools/_book.yaml
index 57e03ba..4064c34 100644
--- a/docs/html/distribute/tools/_book.yaml
+++ b/docs/html/distribute/tools/_book.yaml
@@ -1,31 +1,36 @@
toc:
- title: Launch Checklist
path: /distribute/tools/launch-checklist.html
- custom_link_attributes:
- - zh-cn-lang="发布检查清单"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 发布检查清单
- title: Localization Checklist
path: /distribute/tools/localization-checklist.html
- custom_link_attributes:
- - zh-cn-lang="本地化检查清单"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 本地化检查清单
- title: Device Art Generator
path: /distribute/tools/promote/device-art.html
- title: Google Play Badges
path: /distribute/tools/promote/badges.html
- custom_link_attributes:
- - zh-cn-lang="Google Play 徽章生成器"
+ path_attributes:
+ - name: zh-cn-lang
+ value: Google Play 徽章生成器
- title: Linking to Your Products
path: /distribute/tools/promote/linking.html
- custom_link_attributes:
- - zh-cn-lang="链接到您的商品"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 链接到您的商品
- title: Brand Guidelines
path: /distribute/tools/promote/brand.html
- custom_link_attributes:
- - zh-cn-lang="品牌指南"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 品牌指南
- title: Alternative Distribution
path: /distribute/tools/open-distribution.html
diff --git a/docs/html/go/libraries/_book.yaml b/docs/html/go/libraries/_book.yaml
new file mode 100644
index 0000000..209dae5
--- /dev/null
+++ b/docs/html/go/libraries/_book.yaml
@@ -0,0 +1,17 @@
+toc:
+- title: Support Library
+ path: /tools/support-library/index.html
+ section:
+ - title: Features
+ path: /tools/support-library/features.html
+ - title: Setup
+ path: /tools/support-library/setup.html
+
+- title: Data Binding Library
+ path: /tools/data-binding/guide.html
+
+- title: Testing Support Library
+ path: /tools/testing-support-library/index.html
+ section:
+ - title: API Reference
+ path: /reference/android/support/test/package-summary.html
diff --git a/docs/html/tools/data-binding/guide.jd b/docs/html/go/libraries/data-binding/index.jd
similarity index 99%
rename from docs/html/tools/data-binding/guide.jd
rename to docs/html/go/libraries/data-binding/index.jd
index 8bbd833..ca8784e 100644
--- a/docs/html/tools/data-binding/guide.jd
+++ b/docs/html/go/libraries/data-binding/index.jd
@@ -1,4 +1,5 @@
-page.title=Data Binding Guide
+page.title=Data Binding Library
+page.metaDescription=The Data Binding Library enables you to write declarative layouts.
page.tags="databinding", "layouts"
@jd:body
diff --git a/docs/html/go/libraries/index.jd b/docs/html/go/libraries/index.jd
new file mode 100644
index 0000000..2831d15
--- /dev/null
+++ b/docs/html/go/libraries/index.jd
@@ -0,0 +1,14 @@
+page.title=Android Libraries
+
+@jd:body
+
+<p>This section describes several useful Android libraries that are not
+included with the Android Framework.</p>
+
+<div class="dynamic-grid">
+ <div class="resource-widget resource-flow-layout landing col-12"
+ data-query="collection:go/libraries"
+ data-cardSizes="6x6"
+ data-maxResults="6">
+ </div>
+</div>
diff --git a/docs/html/tools/support-library/features.jd b/docs/html/go/libraries/support-library/features.jd
similarity index 98%
rename from docs/html/tools/support-library/features.jd
rename to docs/html/go/libraries/support-library/features.jd
index a5f343e..089357a 100755
--- a/docs/html/tools/support-library/features.jd
+++ b/docs/html/go/libraries/support-library/features.jd
@@ -34,11 +34,11 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}tools/support-library/index.html#revisions">
+ <li><a href="{@docRoot}go/libraries/support-library/index.html#revisions">
Support Library Revisions</a></li>
- <li><a href="{@docRoot}tools/support-library/setup.html">
+ <li><a href="{@docRoot}go/libraries/support-library/setup.html">
Support Library Setup</a></li>
- <li><a href="{@docRoot}tools/testing-support-library/index.html">
+ <li><a href="{@docRoot}go/libraries/testing-support-library/index.html">
Testing Support Library</a></li>
</ol>
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/go/libraries/support-library/index.jd
similarity index 93%
rename from docs/html/tools/support-library/index.jd
rename to docs/html/go/libraries/support-library/index.jd
index 3afd7ac..61f0bc2 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/go/libraries/support-library/index.jd
@@ -1,4 +1,5 @@
page.title=Support Library
+page.metaDescription=The Android Support Library offers backward-compatible versions of a number of features that are not built into the framework.
@jd:body
@@ -8,7 +9,11 @@
<h2>In this document</h2>
<ol>
<li><a href="#overview">Overview</a></li>
- <li><a href="#revisions">Revisions</a></li>
+ <li><a href="#backward">Backward Compatibility</a></li>
+ <li><a href="#layout-patterns">Support for General Layout Patterns</a></li>
+ <li><a href="#form-factors">Support for Different Form Factors</a></li>
+ <li><a href="#utils">General Utilities</a></li>
+ <li><a href="#revisions">Support Library Revision History</a></li>
</ol>
<h2>See also</h2>
@@ -22,39 +27,161 @@
</div>
</div>
-<p>The Android Support Library package is a set of code libraries that provide
- backward-compatible versions of Android framework APIs as well as features that are only available
- through the library APIs. Each Support Library is backward-compatible to a specific Android API
- level. This design means that your applications can use the libraries' features and still be
- compatible with devices running Android 1.6 (API level 4) and up.</p>
-
-<p>This guide provides information about what features are enabled by the Support Libraries,
- how to use them in your development environment and information about library releases.</p>
-
+<p>
+ The Android Support Library offers a number of features that are not built
+ into the framework. These libraries offer backward-compatible versions of
+ new features, provide useful UI elements that are not included in the
+ framework, and provide a range of utilities that apps can draw on.
+</p>
<h2 id="overview">Overview</h2>
-<p>Including the Support Libraries in your Android project is considered a best practice for
- application developers, depending on the range of platform versions your app is targeting
- and the APIs that it uses. Using the features the libraries provide can help you improve the look
- of your application, increase performance and broaden the reach of your application to more users.
- If you use the Android
- <a href="{@docRoot}tools/projects/templates.html">code template</a> tools, you will notice that
- all the Android application templates include one or more of the Support Libraries by default.</p>
+<p>
+ In many cases, a feature may be valuable to many app developers, but not
+ appropriate to include in the Android framework itself. For example, an app
+ might only need a feature for specialized use cases, such as to smooth the
+ transition between different versions of the Android system.
+</p>
-<p>The Support Libraries each target a base Android API level and each provides a different set
- of features. In order to effectively use the libraries, it is important to consider what features
- you want to support and understand what features are supported by each library at what Android
- API level. To get started, review the
- <a href="{@docRoot}tools/support-library/features.html">Support Library Features</a> guide.
- After that, go to the
- <a href="{@docRoot}tools/support-library/setup.html">Support Library Setup</a> topic to
- learn how to incorporate the Support Libraries into your application. For more details
- about Support Library APIs, see the {@link android.support.v4.app android.support}
- packages in the API reference.</p>
+<p>
+ To address these situations, the Android SDK includes several libraries
+ collectively called the <em>Android Support Library</em>. App developers
+ can include any of these libraries if they want to incorporate the
+ library functionality into their apps.
+</p>
+
+<p>
+ Support libraries provide a range of different features:
+</p>
+
+<ul>
+ <li>
+ <a href="#backward">Backward-compatible</a> versions of framework
+ components.
+ </li>
+
+ <li>UI elements to implement the recommended Android <a href=
+ "#layout-patterns">layout patterns</a>.
+ </li>
+
+ <li>Support for different <a href="#form-factors">form factors</a>.
+ </li>
+
+ <li>Miscellaneous <a href="#utils">utility</a> functions.
+ </li>
+</ul>
+
+<h2 id="backward">Backward Compatibility</h2>
+
+<div class="figure" style="width:300px">
+ <img src="{@docRoot}images/tools/support-library/appbar-kitkat.png"
+ srcset="{@docRoot}images/tools/support-library/appbar-kitkat.png 1x,
+ {@docRoot}images/tools/support-library/appbar-kitkat_2x.png 2x"
+ alt="" width="300">
+ <p class="img-caption">
+ <strong>Figure 1.</strong> Because this app uses support library UI
+ elements its interface incorporates material design principles, even though
+ it is running on Android 4.4, which does not include native support for
+ material design.
+ </p>
+</div>
+
+<p>
+ Support libraries allow apps running on older versions of the Android
+ platform to support features made available on newer versions of the
+ platform. For example, an app running on a version of Android lower than 5.0
+ (API level 21) that relies on framework classes cannot display
+ material-design elements, as that version of the Android framework doesn't
+ support material design. However, if the app incorporates the Support
+ Library's <a href="{@docRoot}tools/support-library/features.html">appcompat
+ library</a>, the app has access to many of the features available in API
+ level 21, including support for material design. As a result, your app can
+ deliver a more consistent experience across a broader range of platform
+ versions.
+</p>
-<h2 id="revisions">Revisions</h2>
+<p>
+ In some cases, the support library version of a class depends as much as
+ possible on the functionality that the framework provides. In these cases,
+ if an app calls one of the support class's methods, the support library's
+ behavior depends on what version of Android the app is running on. If the
+ framework provides the necessary functionality, the support library calls on
+ the framework to perform the task. If the app is running on an earlier
+ version of Android, and the framework doesn't expose the needed
+ functionality, the support library may try to provide the functionality
+ itself, or may act as a no-op. In either case, the app generally doesn't
+ need to check what version of Android it's running on; instead, the app
+ can rely on the support library to do those checks and choose appropriate
+ behavior. Generally, classes whose names end in
+ <code>…Compat</code> (like {@link android.support.v4.app.ActivityCompat})
+ behave this way.
+</p>
+
+<p>
+ In other cases, the support library class provides a complete, standalone
+ version of a framework class that does not rely on the availability of
+ any framework APIs. These
+ methods provide consistent behavior across all supported platforms.
+</p>
+
+<p>
+ In either case, the app does not need to check the system version at run
+ time. The app can rely on the support library class to do the appropriate
+ system checks, and modify its behavior as necessary.
+</p>
+
+<h2 id="layout-patterns">Support for General Layout Patterns</h2>
+
+<p>
+ Support libraries provide user interface elements not offered by
+ the Android framework. For example, the Android Support Library offers additional
+ layout classes, like {@link android.support.v4.widget.DrawerLayout}. These
+ classes follow recommended Android design practices; for example, the Design
+ Library follows the principles of material design in a way that works across
+ many versions of Android.
+</p>
+
+<p>
+ By using these support library classes, you can avoid having to reinvent the
+ wheel; if your app has a particular user interface requirement, you can draw
+ on existing code, which provides a user interface that users will already be
+ familiar with. These elements also help you build an app that looks and feels
+ like a part of the Android ecosystem. For example, many apps need to display
+ arbitrarily long lists of elements, and need to be able to quickly and
+ efficiently reuse those elements as the list changes; this might be a list of
+ emails, contacts, music albums, and so on. Those apps can use the support
+ library {@link android.support.v7.widget.RecyclerView} widget to display the
+ list. This saves the app developer from having to develop the list from
+ scratch, and also ensures that the user will see a list that looks and
+ behaves like lists in other apps.
+</p>
+
+<h2 id="form-factors">Support for Different Form Factors</h2>
+
+<p>
+ The Android SDK provides libraries for a number of different form factors,
+ such as TV and wearables. An app can depend on the appropriate support
+ library to provide functionality across a wide range of platform versions,
+ and can provide content on external screens, speakers, and other destination
+ devices.
+</p>
+
+<h2 id="utils">General Utilities</h2>
+
+<p>
+ The Android Support Library provides backward-compatible utility functions.
+ Apps can use these utility functions to provide an appropriate user
+ experience across a wide range of Android system versions. For example,
+ support library permission methods behave appropriately depending on what
+ platform version your app is running on. If the platform supports the
+ runtime permissions model, these methods request the appropriate permission
+ from the user; on platform versions that do not support the runtime
+ permissions model, the methods check whether the appropriate permission was
+ granted at install time.
+</p>
+
+<h2 id="revisions">Support Library Revision History</h2>
<p>This section provides details about the Support Library package releases.</p>
diff --git a/docs/html/tools/support-library/setup.jd b/docs/html/go/libraries/support-library/setup.jd
similarity index 100%
rename from docs/html/tools/support-library/setup.jd
rename to docs/html/go/libraries/support-library/setup.jd
diff --git a/docs/html/tools/testing-support-library/index.jd b/docs/html/go/libraries/testing-support-library/index.jd
similarity index 99%
rename from docs/html/tools/testing-support-library/index.jd
rename to docs/html/go/libraries/testing-support-library/index.jd
index 35a3c7d..f69a35b7 100644
--- a/docs/html/tools/testing-support-library/index.jd
+++ b/docs/html/go/libraries/testing-support-library/index.jd
@@ -1,4 +1,5 @@
page.title=Testing Support Library
+page.metaDescription=The Android Testing Support Library provides an extensive framework for testing Android apps.
page.image=images/tools/studio-test-module.png
@jd:body
diff --git a/docs/html/google/_book.yaml b/docs/html/google/_book.yaml
index 01efa4f..92357e9 100644
--- a/docs/html/google/_book.yaml
+++ b/docs/html/google/_book.yaml
@@ -1,17 +1,20 @@
toc:
- title: Google Play In-app Billing
path: /google/play/billing/index.html
- custom_link_attributes:
- - zh-cn-lang="应用内结算"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 应用内结算
section:
- title: Overview
path: /google/play/billing/billing_overview.html
- custom_link_attributes:
- - zh-cn-lang="应用内结算概述"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 应用内结算概述
- title: Version 3 API
path: /google/play/billing/api.html
- custom_link_attributes:
- - zh-cn-lang="应用内结算 API"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 应用内结算 API
section:
- title: Implementing the API
path: /google/play/billing/billing_integrate.html
@@ -23,29 +26,39 @@
path: /google/play/billing/billing_promotions.html
- title: Security and Design
path: /google/play/billing/billing_best_practices.html
- custom_link_attributes:
- - zh-cn-lang="安全性和设计"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 安全性和设计
- title: Testing In-app Billing
path: /google/play/billing/billing_testing.html
- custom_link_attributes:
- - zh-cn-lang="测试应用内结算"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 测试应用内结算
- title: Administering In-app Billing
path: /google/play/billing/billing_admin.html
- custom_link_attributes:
- - zh-cn-lang="管理应用内结算"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 管理应用内结算
- title: Version Notes
path: /google/play/billing/versions.html
- title: Filters on Google Play
path: /google/play/filters.html
- custom_link_attributes:
- - es-lang="Filtros en Google Play"
- - ja-lang="Google Play 上のフィルタ"
- - ko-lang="Google Play 필터"
- - pt-br-lang="Filtros no Google Play"
- - ru-lang="Фильтры в Google Play"
- - zh-cn-lang="Google Play 上的筛选器"
- - zh-tw-lang="Google Play 上的篩選器"
+ path_attributes:
+ - name: es-lang
+ value: Filtros en Google Play
+ - name: ja-lang
+ value: Google Play 上のフィルタ
+ - name: ko-lang
+ value: Google Play 필터
+ - name: pt-br-lang
+ value: Filtros no Google Play
+ - name: ru-lang
+ value: Фильтры в Google Play
+ - name: zh-cn-lang
+ value: Google Play 上的筛选器
+ - name: zh-tw-lang
+ value: Google Play 上的篩選器
- title: Google Play Developer API
path: /google/play/developer-api.html
diff --git a/docs/html/guide/_book.yaml b/docs/html/guide/_book.yaml
index 5017376..4163f0f 100644
--- a/docs/html/guide/_book.yaml
+++ b/docs/html/guide/_book.yaml
@@ -365,6 +365,12 @@
- title: App Install Location
path: /guide/topics/data/install-location.html
+
+- title: Libraries
+ path: /go/libraries/index.html
+ section:
+ - include: /go/libraries/_book.yaml
+
- title: Administration
path: /guide/topics/admin/index.html
section:
@@ -387,14 +393,21 @@
- title: Best Practices
path: /guide/practices/index.html
- custom_link_attributes:
- - de-lang="Bewährte Verfahren"
- - es-lang="Prácticas recomendadas"
- - fr-lang="Meilleures pratiques"
- - it-lang="Best practice"
- - ja-lang="ベスト プラクティス"
- - zh-cn-lang="最佳实践"
- - zh-tw-lang="最佳實務"
+ path_attributes:
+ - name: de-lang
+ value: Bewährte Verfahren
+ - name: es-lang
+ value: Prácticas recomendadas
+ - name: fr-lang
+ value: Meilleures pratiques
+ - name: it-lang
+ value: Best practice
+ - name: ja-lang
+ value: ベスト プラクティス
+ - name: zh-cn-lang
+ value: 最佳实践
+ - name: zh-tw-lang
+ value: 最佳實務
section:
- title: Supporting Multiple Screens
path: /guide/practices/screens_support.html
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 549a1b6..9257a76 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -121,7 +121,7 @@
<li><a href="<?cs var:toroot ?>guide/topics/resources/overview.html">
<span class="en">Overview</span>
</a></li>
- <li><a href="<?cs var:toroot ?>guide/topics/resources/providing-resources.html">
+ <li><a href="<?cs var:toroot ?>guide/topics/resources/providing-resources.html">
<span class="en">Providing Resources</span>
</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/resources/accessing-resources.html">
@@ -129,10 +129,13 @@
</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/resources/runtime-changes.html">
<span class="en">Handling Runtime Changes</span>
- </a></li>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/topics/resources/localization.html">
<span class="en">Localization</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/resources/complex-xml-resources.html">
+ <span class="en">Complex XML Resources</span>
+ </a></li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/resources/available-resources.html">
<span class="en">Resource Types</span>
diff --git a/docs/html/guide/topics/resources/complex-xml-resources.jd b/docs/html/guide/topics/resources/complex-xml-resources.jd
new file mode 100644
index 0000000..66dcb58
--- /dev/null
+++ b/docs/html/guide/topics/resources/complex-xml-resources.jd
@@ -0,0 +1,117 @@
+page.title=Inline Complex XML Resources
+parent.title=Application Resources
+parent.link=index.html
+@jd:body
+
+<p>Certain resource types are a composition of multiple complex resources represented by XML files.
+One example is an animated vector drawable, which is a drawable resource encapsulating a vector
+drawable and an animation. This requires the use of at least three XML files.</p>
+
+<dl>
+
+<dt><code>res/drawable/avd.xml</code></dt>
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/vectordrawable" >
+ <target
+ android:name="rotationGroup"
+ android:animation="@anim/rotation" />
+</animated-vector>
+</pre>
+</dd>
+
+<dt><code>res/drawable/vectordrawable.xml</code></dt>
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="64dp"
+ android:width="64dp"
+ android:viewportHeight="600"
+ android:viewportWidth="600" >
+
<group
+ android:name="rotationGroup"
+ android:pivotX="300.0"
+ android:pivotY="300.0"
+ android:rotation="45.0" >
+ <path
+ android:fillColor="#000000"
+ android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
+
</group>
+</vector>
+</pre>
+</dd>
+
+<dt><code>res/anim/rotation.xml</code></dt>
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<objectAnimator xmlns:android="http://schemas.android.com/apk/android"
+ android:duration="6000"
+ android:propertyName="rotation"
+ android:valueFrom="0"
+
android:valueTo="360" />
+</pre>
+</dd>
+
+</dl>
+
+<p>There are a lot of files here just to make a single animated vector drawable!
+If the vector drawable and animations are re-used elsewhere, this is the best way to implement an
+animated vector drawable. If they’re only ever used for this animated vector drawable, then there is
+a more compact way to implement them.</p>
+
+<p>Using AAPT’s inline resource format, you can define all three resources in the same XML file.
+Since we’re making an animated vector drawable, we put the file under <code>res/drawable/</code>.</p>
+
+<dl>
+
+<dt><code>res/drawable/avd.xml</code></dt>
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ <strong>xmlns:aapt="http://schemas.android.com/aapt"</strong> >
+
+ <strong><aapt:attr name="android:drawable" ></strong>
+ <vector
+ android:height="64dp"
+ android:width="64dp"
+ android:viewportHeight="600"
+ android:viewportWidth="600" >
+
<group
+ android:name="rotationGroup"
+ android:pivotX="300.0"
+ android:pivotY="300.0"
+ android:rotation="45.0" >
+ <path
+ android:fillColor="#000000"
+ android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
+
</group>
+ </vector>
+ <strong><aapt:attr /></strong>
+
+ <target
+ android:name="rotationGroup" />
+ <strong><aapt:attr name="android:animation" ></strong>
+ <objectAnimator
+ android:duration="6000"
+ android:propertyName="rotation"
+ android:valueFrom="0"
+
android:valueTo="360" />
+ <strong><aapt:attr></strong>
+</animated-vector>
+</pre>
+</dd>
+
+</dl>
+
+<p>The XML tag <code><aapt:attr ></code> tells AAPT that the tag’s child shall be treated as a
+resource and extracted into its own resource file. The value in the attribute name specifies where
+to use the inline resource within the parent tag.</p>
+
+<p>AAPT will generate resource files and names for all of the inline resources.
+Applications built using this inline format are compatible with all versions of Android.</p>
+
diff --git a/docs/html/guide/topics/resources/overview.jd b/docs/html/guide/topics/resources/overview.jd
index c3bd0bf..c496797 100644
--- a/docs/html/guide/topics/resources/overview.jd
+++ b/docs/html/guide/topics/resources/overview.jd
@@ -9,6 +9,7 @@
<li><a href="accessing-resources.html">Accessing Resources</a></li>
<li><a href="runtime-changes.html">Handling Runtime Changes</a></li>
<li><a href="localization.html">Localization</a></li>
+ <li><a href="complex-xml-resources.html">Complex XML Resources</a></li>
</ol>
<h2>Reference</h2>
@@ -79,6 +80,9 @@
<dd>A bottom-up guide to localizing your application using alternative resources. While this is
just one specific use of alternative resources, it is very important in order to reach more
users.</dd>
+ <dt><strong><a href="complex-xml-resources.html">Complex XML Resources</a></strong></dt>
+ <dd>An XML format for building complex resources like animated vector drawables in a single
+XML file.</dd>
<dt><strong><a href="available-resources.html">Resource Types</a></strong></dt>
<dd>A reference of various resource types you can provide, describing their XML elements,
attributes, and syntax. For example, this reference shows you how to create a resource for
diff --git a/docs/html/images/tools/support-library/appbar-kitkat.png b/docs/html/images/tools/support-library/appbar-kitkat.png
new file mode 100644
index 0000000..c89fd87
--- /dev/null
+++ b/docs/html/images/tools/support-library/appbar-kitkat.png
Binary files differ
diff --git a/docs/html/images/tools/support-library/appbar-kitkat_2x.png b/docs/html/images/tools/support-library/appbar-kitkat_2x.png
new file mode 100644
index 0000000..c9718e6
--- /dev/null
+++ b/docs/html/images/tools/support-library/appbar-kitkat_2x.png
Binary files differ
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index ad66f79..b1d7efc 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -4456,6 +4456,14 @@
"https://support.google.com/googleplay/answer/2651410"
]
},
+ "go/libraries": {
+ "title": "",
+ "resources": [
+ "go/libraries/support-library/index.html",
+ "go/libraries/testing-support-library/index.html",
+ "go/libraries/data-binding/index.html"
+ ]
+ },
"autolanding": {
"title": "",
"resources": [
diff --git a/docs/html/ndk/guides/_book.yaml b/docs/html/ndk/guides/_book.yaml
index fdcfe46..287a92d 100644
--- a/docs/html/ndk/guides/_book.yaml
+++ b/docs/html/ndk/guides/_book.yaml
@@ -61,5 +61,14 @@
- title: OpenSL ES for Android
path: /ndk/guides/audio/opensl-for-android.html
-- title: Graphics
+- title: Vulkan
path: /ndk/guides/graphics/index.html
+ section:
+ - title: Getting Started
+ path: /ndk/guides/graphics/getting-started.html
+ - title: Design Guidelines
+ path: /ndk/guides/graphics/design-notes.html
+ - title: Shader Compilers
+ path: /ndk/guides/graphics/shader-compilers.html
+ - title: Validation Layers
+ path: /ndk/guides/graphics/validation-layer.html
diff --git a/docs/html/preview/_book.yaml b/docs/html/preview/_book.yaml
index 9f53866..cac6d13 100644
--- a/docs/html/preview/_book.yaml
+++ b/docs/html/preview/_book.yaml
@@ -1,96 +1,342 @@
toc:
- title: Program Overview
path: /preview/overview.html
- custom_link_attributes:
- - es-lang="Información general del programa"
- - ja-lang="プログラム概要"
- - ko-lang="프로그램 개요"
- - pt-br-lang="Visão geral do programa"
- - ru-lang="Обзор программы"
- - zh-cn-lang="计划概览"
- - zh-tw-lang="程式總覽"
+ path_attributes:
+ - name: es-lang
+ value: Información general del programa
+ - name: in-lang
+ value: Ikhtisar Program
+ - name: ja-lang
+ value: プログラム概要
+ - name: ko-lang
+ value: 프로그램 개요
+ - name: pt-br-lang
+ value: Visão geral do programa
+ - name: ru-lang
+ value: Обзор программы
+ - name: vi-lang
+ value: Tổng quan về Chương trình
+ - name: zh-cn-lang
+ value: 计划概览
+ - name: zh-tw-lang
+ value: 程式總覽
- title: Support and Release Notes
path: /preview/support.html
- title: Set Up to Develop
path: /preview/setup-sdk.html
- custom_link_attributes:
- - es-lang="Configurar el SDK de la versión preliminar"
- - ja-lang="Preview SDK のセットアップ"
- - ko-lang="미리 보기 SDK 설정하기"
- - pt-br-lang="Configuração do Preview SDK"
- - ru-lang="Настройка пакета SDK Preview"
- - zh-cn-lang="设置预览版 SDK"
- - zh-tw-lang="設定預覽版 SDK"
+ path_attributes:
+ - name: es-lang
+ value: Configurar el SDK de la versión preliminar
+ - name: in-lang
+ value: Menyiapkan Preview
+ - name: ja-lang
+ value: Preview SDK のセットアップ
+ - name: ko-lang
+ value: 미리 보기 SDK 설정하기
+ - name: pt-br-lang
+ value: Configuração do Preview SDK
+ - name: ru-lang
+ value: Настройка пакета SDK Preview
+ - name: vi-lang
+ value: Kiểm thử trên Thiết bị
+ - name: zh-cn-lang
+ value: 设置预览版 SDK
+ - name: zh-tw-lang
+ value: 設定預覽版 SDK
- title: Test on a Device
path: /preview/download.html
+ path_attributes:
+ - name: es-lang
+ value: Pruebe en un dispositivo
+ - name: in-lang
+ value: Menguji pada Perangkat
+ - name: ja-lang
+ value: デバイス上でテストする
+ - name: ko-lang
+ value: 기기에서 테스트
+ - name: pt-br-lang
+ value: Testar em um dispositivo
+ - name: ru-lang
+ value: Тестирование на устройстве
+ - name: vi-lang
+ value: Kiểm thử trên Thiết bị
+ - name: zh-cn-lang
+ value: 在设备上测试
+ - name: zh-tw-lang
+ value: 在裝置上測試
- title: Behavior Changes
path: /preview/behavior-changes.html
- custom_link_attributes:
- - es-lang="Cambios en los comportamientos"
- - ja-lang="動作の変更点"
- - ko-lang="동작 변경"
- - pt-br-lang="Mudanças de comportamento"
- - ru-lang="Изменения в работе"
- - zh-cn-lang="行为变更"
- - zh-tw-lang="行為變更"
+ path_attributes:
+ - name: es-lang
+ value: Cambios en los comportamientos
+ - name: in-lang
+ value: Perubahan Perilaku
+ - name: ja-lang
+ value: 動作の変更点
+ - name: ko-lang
+ value: 동작 변경
+ - name: pt-br-lang
+ value: Mudanças de comportamento
+ - name: ru-lang
+ value: Изменения в работе
+ - name: vi-lang
+ value: Các thay đổi Hành vi
+ - name: zh-cn-lang
+ value: 行为变更
+ - name: zh-tw-lang
+ value: 行為變更
section:
- title: Background Optimizations
path: /preview/features/background-optimization.html
+ path_attributes:
+ - name: es-lang
+ value: Optimizaciones en segundo plano
+ - name: in-lang
+ value: Optimisasi Latar Belakang
+ - name: ja-lang
+ value: バックグラウンド処理の最適化
+ - name: ko-lang
+ value: 백그라운드 최적화
+ - name: pt-br-lang
+ value: Otimizações em segundo plano
+ - name: ru-lang
+ value: Оптимизация фоновых процессов
+ - name: vi-lang
+ value: Tối ưu hóa Chạy ngầm
+ - name: zh-cn-lang
+ value: 后台优化
+ - name: zh-tw-lang
+ value: 背景最佳化
- title: Language and Locale
path: /preview/features/multilingual-support.html
+ path_attributes:
+ - name: es-lang
+ value: Idioma y configuración regional
+ - name: in-lang
+ value: Bahasa dan Lokal
+ - name: ja-lang
+ value: 言語とロケール
+ - name: ko-lang
+ value: 언어 및 로케일
+ - name: pt-br-lang
+ value: Idioma e localidade
+ - name: ru-lang
+ value: Язык и языковой стандарт
+ - name: vi-lang
+ value: Ngôn ngữ và Bản địa
+ - name: zh-cn-lang
+ value: 语言和区域设置
+ - name: zh-tw-lang
+ value: 語言和地區設定
- title: Android N for Developers
path: /preview/api-overview.html
- custom_link_attributes:
- - es-lang="Información general de la API"
- - ja-lang="API の概要"
- - ko-lang="API 개요"
- - pt-br-lang="Visão geral da API"
- - ru-lang="Обзор API-интерфейсов"
- - zh-cn-lang="API 概览"
- - zh-tw-lang="API 總覽"
+ path_attributes:
+ - name: es-lang
+ value: Información general de la API
+ - name: in-lang
+ value: Android N untuk Pengembang
+ - name: ja-lang
+ value: API の概要
+ - name: ko-lang
+ value: API 개요
+ - name: pt-br-lang
+ value: Visão geral da API
+ - name: ru-lang
+ value: Обзор API-интерфейсов
+ - name: vi-lang
+ value: Android N cho Nhà phát triển
+ - name: zh-cn-lang
+ value: API 概览
+ - name: zh-tw-lang
+ value: API 總覽
section:
- title: Multi-Window Support
path: /preview/features/multi-window.html
+ path_attributes:
+ - name: es-lang
+ value: Compatibilidad con ventanas múltiples
+ - name: in-lang
+ value: Dukungan Multi-Jendela
+ - name: ja-lang
+ value: マルチ ウィンドウのサポート
+ - name: ko-lang
+ value: 다중 창 지원
+ - name: pt-br-lang
+ value: Suporte a várias janelas
+ - name: ru-lang
+ value: Поддержка многооконного режима
+ - name: vi-lang
+ value: Hỗ trợ đa cửa sổ
+ - name: zh-cn-lang
+ value: 多窗口支持
+ - name: zh-tw-lang
+ value: 多視窗支援
- title: Notifications
path: /preview/features/notification-updates.html
+ path_attributes:
+ - name: es-lang
+ value: Notificaciones
+ - name: in-lang
+ value: Pemberitahuan
+ - name: ja-lang
+ value: 通知
+ - name: ko-lang
+ value: 알림
+ - name: pt-br-lang
+ value: Notificações
+ - name: ru-lang
+ value: Уведомления
+ - name: vi-lang
+ value: Thông báo
+ - name: zh-cn-lang
+ value: 通知
+ - name: zh-tw-lang
+ value: 通知
- title: Data Saver
path: /preview/features/data-saver.html
- title: TV Recording
path: /preview/features/tv-recording-api.html
+ path_attributes:
+ - name: es-lang
+ value: Grabación de TV
+ - name: in-lang
+ value: Perekaman TV
+ - name: ja-lang
+ value: TV の録画
+ - name: ko-lang
+ value: TV 녹화
+ - name: pt-br-lang
+ value: Gravação para TV
+ - name: ru-lang
+ value: Запись ТВ
+ - name: vi-lang
+ value: Ghi lại TV
+ - name: zh-cn-lang
+ value: TV 录制
+ - name: zh-tw-lang
+ value: 電視錄製
- title: Network Security Configuration
path: /preview/features/security-config.html
+ path_attributes:
+ - name: es-lang
+ value: Configuración de seguridad de la red
+ - name: ja-lang
+ value: ネットワーク セキュリティ構成
+ - name: ko-lang
+ value: 네트워크 보안 구성
+ - name: pt-br-lang
+ value: Configurações de segurança de rede
+ - name: ru-lang
+ value: Конфигурация сетевой безопасности
+ - name: vi-lang
+ value: Cấu hình Bảo mật mạng
+ - name: zh-cn-lang
+ value: 网络安全配置
+ - name: zh-tw-lang
+ value: 網路安全性設定
- title: ICU4J Support
path: /preview/features/icu4j-framework.html
+ path_attributes:
+ - name: es-lang
+ value: API de ICU4J del framework de Android
+ - name: in-lang
+ value: ICU4J Android Framework API
+ - name: ja-lang
+ value: ICU4J Android フレームワーク API
+ - name: ko-lang
+ value: ICU4J Android 프레임워크 API
+ - name: pt-br-lang
+ value: APIs de estrutura do Android para ICU4J
+ - name: ru-lang
+ value: API-интерфейсы ICU4J в платформе Android
+ - name: vi-lang
+ value: API Khuôn khổ Android ICU4J
+ - name: zh-cn-lang
+ value: ICU4J Android 框架 API
+ - name: zh-tw-lang
+ value: ICU4J Android 架構 API
- title: Java 8 Language Features
path: /preview/j8-jack.html
+ path_attributes:
+ - name: es-lang
+ value: Funciones del lenguaje Java 8
+ - name: in-lang
+ value: Fitur Bahasa Java 8
+ - name: ja-lang
+ value: Java 8 の機能
+ - name: ko-lang
+ value: Java 8 언어 기능
+ - name: pt-br-lang
+ value: Recursos de linguagem do Java 8
+ - name: ru-lang
+ value: Возможности языка Java 8
+ - name: vi-lang
+ value: Tính năng của Ngôn ngữ Java 8
+ - name: zh-cn-lang
+ value: Java 8 语言功能
+ - name: zh-tw-lang
+ value: Java 8 語言功能
- title: Android for Work Updates
path: /preview/features/afw.html
- title: Scoped Directory Access
path: /preview/features/scoped-folder-access.html
+ path_attributes:
+ - name: es-lang
+ value: Acceso a directorios determinados
+ - name: ja-lang
+ value: 特定のディレクトリへのアクセス
+ - name: ko-lang
+ value: 범위가 지정된 디렉터리 액세스
+ - name: pt-br-lang
+ value: Acesso a diretórios com escopo
+ - name: ru-lang
+ value: Доступ к выделенным каталогам
+ - name: vi-lang
+ value: Truy cập Thư mục theo Phạm vi
+ - name: zh-cn-lang
+ value: 作用域目录访问
+ - name: zh-tw-lang
+ value: 限定範圍目錄存取
- title: Samples
path: /preview/samples.html
- custom_link_attributes:
- - es-lang="Ejemplos"
- - ja-lang="サンプル"
- - ko-lang="샘플"
- - pt-br-lang="Exemplos"
- - ru-lang="Примеры"
- - zh-cn-lang="示例"
- - zh-tw-lang="範例"
+ path_attributes:
+ - name: es-lang
+ value: Ejemplos
+ - name: in-lang
+ value: Contoh
+ - name: ja-lang
+ value: サンプル
+ - name: ko-lang
+ value: 샘플
+ - name: pt-br-lang
+ value: Exemplos
+ - name: ru-lang
+ value: Примеры
+ - name: zh-cn-lang
+ value: 示例
+ - name: zh-tw-lang
+ value: 範例
- title: License Agreement
path: /preview/license.html
- custom_link_attributes:
- - es-lang="Contrato de licencia"
- - ja-lang="使用許諾契約"
- - ko-lang="라이선스 계약"
- - pt-br-lang="Contrato de licença"
- - ru-lang="Лицензионное соглашение"
- - zh-cn-lang="许可协议"
- - zh-tw-lang="授權協議"
+ path_attributes:
+ - name: es-lang
+ value: Contrato de licencia
+ - name: ja-lang
+ value: 使用許諾契約
+ - name: ko-lang
+ value: 라이선스 계약
+ - name: pt-br-lang
+ value: Contrato de licença
+ - name: ru-lang
+ value: Лицензионное соглашение
+ - name: zh-cn-lang
+ value: 许可协议
+ - name: zh-tw-lang
+ value: 授權協議
diff --git a/docs/html/tools/_book.yaml b/docs/html/tools/_book.yaml
index 5395cc8..fb257f3 100644
--- a/docs/html/tools/_book.yaml
+++ b/docs/html/tools/_book.yaml
@@ -2,10 +2,8 @@
- title: Download
path: /sdk/index.html
section:
- - title: Installing the SDK
+ - title: Install Android Studio
path: /sdk/installing/index.html
- - title: Adding SDK Packages
- path: /sdk/installing/adding-packages.html
- title: Workflow
path: /tools/workflow/index.html
@@ -27,13 +25,15 @@
path: /tools/debugging/index.html
- title: Publishing
path: /tools/publishing/publishing_overview.html
- custom_link_attributes:
- - zh-cn-lang="发布概述"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 发布概述
section:
- title: Preparing for Release
path: /tools/publishing/preparing.html
- custom_link_attributes:
- - zh-cn-lang="准备发布"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 准备发布
- title: Versioning Your Apps
path: /tools/publishing/versioning.html
- title: Signing Your Apps
@@ -65,7 +65,9 @@
path: /tools/debugging/improving-w-lint.html
- title: Improving Code Inspection with Annotations
path: /tools/debugging/annotations.html
- - title: Deep Link and App Indexing API Support
+ - title: Shrink Your Code and Resources
+ path: /tools/help/proguard.html
+ - title: Supporting URLs and App Indexing in Android Studio
path: /tools/help/app-link-indexing.html
- title: UI Tools
path: /tools/studio/ui-tools.html
@@ -82,6 +84,11 @@
path: /tools/help/image-asset-studio.html
- title: AVD Manager
path: /tools/devices/managing-avds.html
+ - title: Android Emulator
+ path: /tools/devices/emulator.html
+ section:
+ - title: Android Emulator Command-Line Features
+ path: /tools/help/emulator.html
- title: Debugging Tools
path: /tools/debugging/debugging-studio.html
section:
@@ -152,15 +159,8 @@
path: /tools/debugging/debugging-log.html
- title: mksdcard
path: /tools/help/mksdcard.html
- - title: ProGuard
- path: /tools/help/proguard.html
- title: Tracer for OpenGL ES
path: /tools/help/gltracer.html
- - title: Virtual Device Emulator
- path: /tools/devices/emulator.html
- section:
- - title: Command Reference
- path: /tools/help/emulator.html
- title: zipalign
path: /tools/help/zipalign.html
diff --git a/docs/html/training/_book.yaml b/docs/html/training/_book.yaml
index cd4df43..8f8fab2 100644
--- a/docs/html/training/_book.yaml
+++ b/docs/html/training/_book.yaml
@@ -4,8 +4,9 @@
section:
- title: Building Your First App
path: /training/basics/firstapp/index.html
- custom_link_attributes:
- - description="After you've installed the Android SDK, start with this class to learn the basics about Android app development."
+ path_attributes:
+ - name: description
+ value: After you've installed the Android SDK, start with this class to learn the basics about Android app development.
section:
- title: Creating an Android Project
path: /training/basics/firstapp/creating-project.html
@@ -17,8 +18,9 @@
path: /training/basics/firstapp/starting-activity.html
- title: Supporting Different Devices
path: /training/basics/supporting-devices/index.html
- custom_link_attributes:
- - description="How to build your app with alternative resources that provide an optimized user experience on multiple device form factors using a single APK."
+ path_attributes:
+ - name: description
+ value: How to build your app with alternative resources that provide an optimized user experience on multiple device form factors using a single APK.
section:
- title: Supporting Different Languages
path: /training/basics/supporting-devices/languages.html
@@ -28,137 +30,215 @@
path: /training/basics/supporting-devices/platforms.html
- title: Managing the Activity Lifecycle
path: /training/basics/activity-lifecycle/index.html
- custom_link_attributes:
- - ja-lang="アクティビティのライフサイクル 管理"
- - ko-lang="액티비티 수명 주기 관리하기"
- - pt-br-lang="Como gerenciar o ciclo de vida da atividade"
- - ru-lang="Управление жизненным циклом операций"
- - zh-cn-lang="管理活动生命周期"
- - zh-tw-lang="管理應用行為顯示生命週期"
- - description="How Android activities live and die and how to create a seamless user experience by implementing lifecycle callback methods."
+ path_attributes:
+ - name: ja-lang
+ value: アクティビティのライフサイクル 管理
+ - name: ko-lang
+ value: 액티비티 수명 주기 관리하기
+ - name: pt-br-lang
+ value: Como gerenciar o ciclo de vida da atividade
+ - name: ru-lang
+ value: Управление жизненным циклом операций
+ - name: zh-cn-lang
+ value: 管理活动生命周期
+ - name: zh-tw-lang
+ value: 管理應用行為顯示生命週期
+ - name: description
+ value: How Android activities live and die and how to create a seamless user experience by implementing lifecycle callback methods.
section:
- title: Starting an Activity
path: /training/basics/activity-lifecycle/starting.html
- custom_link_attributes:
- - ja-lang="アクティビティを開始する"
- - ko-lang="액티비티 시작하기"
- - pt-br-lang="Iniciando uma atividade"
- - ru-lang="Запуск операции"
- - zh-cn-lang="开始活动"
- - zh-tw-lang="啟動應用行為顯示"
+ path_attributes:
+ - name: ja-lang
+ value: アクティビティを開始する
+ - name: ko-lang
+ value: 액티비티 시작하기
+ - name: pt-br-lang
+ value: Iniciando uma atividade
+ - name: ru-lang
+ value: Запуск операции
+ - name: zh-cn-lang
+ value: 开始活动
+ - name: zh-tw-lang
+ value: 啟動應用行為顯示
- title: Pausing and Resuming an Activity
path: /training/basics/activity-lifecycle/pausing.html
- title: Stopping and Restarting an Activity
path: /training/basics/activity-lifecycle/stopping.html
- custom_link_attributes:
- - ja-lang="アクティビティの一時停止と再開"
- - ko-lang="액티비티 일시정지 및 재개하기"
- - pt-br-lang="Pausando e reiniciando uma atividade"
- - ru-lang="Приостановка и возобновление операции"
- - zh-cn-lang="暂停和继续活动"
- - zh-tw-lang="暫停並繼續應用行為顯示"
+ path_attributes:
+ - name: ja-lang
+ value: アクティビティの一時停止と再開
+ - name: ko-lang
+ value: 액티비티 일시정지 및 재개하기
+ - name: pt-br-lang
+ value: Pausando e reiniciando uma atividade
+ - name: ru-lang
+ value: Приостановка и возобновление операции
+ - name: zh-cn-lang
+ value: 暂停和继续活动
+ - name: zh-tw-lang
+ value: 暫停並繼續應用行為顯示
- title: Recreating an Activity
path: /training/basics/activity-lifecycle/recreating.html
- custom_link_attributes:
- - ja-lang="アクティビティを再作成する"
- - ko-lang="액티비티 재생성하기"
- - pt-br-lang="Recriando uma atividade"
- - ru-lang="Воссоздание операции"
- - zh-cn-lang="重新创建活动"
- - zh-tw-lang="重新建立應用行為顯示"
+ path_attributes:
+ - name: ja-lang
+ value: アクティビティを再作成する
+ - name: ko-lang
+ value: 액티비티 재생성하기
+ - name: pt-br-lang
+ value: Recriando uma atividade
+ - name: ru-lang
+ value: Воссоздание операции
+ - name: zh-cn-lang
+ value: 重新创建活动
+ - name: zh-tw-lang
+ value: 重新建立應用行為顯示
- title: Building a Dynamic UI with Fragments
path: /training/basics/fragments/index.html
- custom_link_attributes:
- - description="How to build a user interface for your app that is flexible enough to present multiple UI components on large screens and a more constrained set of UI components on smaller screens—essential for building a single APK for both phones and tablets."
+ path_attributes:
+ - name: description
+ value: How to build a user interface for your app that is flexible enough to present multiple UI components on large screens and a more constrained set of UI components on smaller screens—essential for building a single APK for both phones and tablets.
section:
- title: Creating a Fragment
path: /training/basics/fragments/creating.html
- title: Building a Flexible UI
path: /training/basics/fragments/fragment-ui.html
- custom_link_attributes:
- - zh-cn-lang="构建灵活的界面"
+ path_attributes:
+ - name: zh-cn-lang
+ value: 构建灵活的界面
- title: Communicating with Other Fragments
path: /training/basics/fragments/communicating.html
- title: Saving Data
path: /training/basics/data-storage/index.html
- custom_link_attributes:
- - ja-lang="データの保存"
- - ko-lang="데이터 저장하기"
- - pt-br-lang="Salvando dados"
- - ru-lang="Сохранение данных"
- - zh-cn-lang="保存数据"
- - zh-tw-lang="儲存資料"
- - description="How to save data on the device, whether it's temporary files, downloaded app assets, user media, structured data, or something else."
+ path_attributes:
+ - name: ja-lang
+ value: データの保存
+ - name: ko-lang
+ value: 데이터 저장하기
+ - name: pt-br-lang
+ value: Salvando dados
+ - name: ru-lang
+ value: Сохранение данных
+ - name: zh-cn-lang
+ value: 保存数据
+ - name: zh-tw-lang
+ value: 儲存資料
+ - name: description
+ value: How to save data on the device, whether it's temporary files, downloaded app assets, user media, structured data, or something else.
section:
- title: Saving Key-Value Sets
path: /training/basics/data-storage/shared-preferences.html
- custom_link_attributes:
- - ja-lang="キー値セットを保存する"
- - ko-lang="키-값 세트 저장하기"
- - pt-br-lang="Salvando conjuntos de valor-chave"
- - ru-lang="Сохранение наборов \"ключ-значение\""
- - zh-cn-lang="保存键值集"
- - zh-tw-lang="儲存索引鍵值組"
+ path_attributes:
+ - name: ja-lang
+ value: キー値セットを保存する
+ - name: ko-lang
+ value: 키-값 세트 저장하기
+ - name: pt-br-lang
+ value: Salvando conjuntos de valor-chave
+ - name: ru-lang
+ value: Сохранение наборов "ключ-значение"
+ - name: zh-cn-lang
+ value: 保存键值集
+ - name: zh-tw-lang
+ value: 儲存索引鍵值組
- title: Saving Files
path: /training/basics/data-storage/files.html
- custom_link_attributes:
- - ja-lang="ファイルを保存する"
- - ko-lang="파일 저장하기"
- - pt-br-lang="Salvando arquivos"
- - ru-lang="Сохранение файлов"
- - zh-cn-lang="保存文件"
- - zh-tw-lang="儲存檔案"
+ path_attributes:
+ - name: ja-lang
+ value: ファイルを保存する
+ - name: ko-lang
+ value: 파일 저장하기
+ - name: pt-br-lang
+ value: Salvando arquivos
+ - name: ru-lang
+ value: Сохранение файлов
+ - name: zh-cn-lang
+ value: 保存文件
+ - name: zh-tw-lang
+ value: 儲存檔案
- title: Saving Data in SQL Databases
path: /training/basics/data-storage/databases.html
- custom_link_attributes:
- - ja-lang="SQL データベースにデータを保存する"
- - ko-lang="SQL 데이터베이스에 데이터 저장하기"
- - pt-br-lang="Salvando dados em bancos de dados do SQL"
- - ru-lang="Сохранение данных в базах данных SQL"
- - zh-cn-lang="在 SQL 数据库中保存数据"
- - zh-tw-lang="在 SQL 資料庫中儲存資料"
+ path_attributes:
+ - name: ja-lang
+ value: SQL データベースにデータを保存する
+ - name: ko-lang
+ value: SQL 데이터베이스에 데이터 저장하기
+ - name: pt-br-lang
+ value: Salvando dados em bancos de dados do SQL
+ - name: ru-lang
+ value: Сохранение данных в базах данных SQL
+ - name: zh-cn-lang
+ value: 在 SQL 数据库中保存数据
+ - name: zh-tw-lang
+ value: 在 SQL 資料庫中儲存資料
- title: Interacting with Other Apps
path: /training/basics/intents/index.html
- custom_link_attributes:
- - ja-lang="他のアプリとの相互操作"
- - ko-lang="액티비티 수명 주기 관리하기"
- - pt-br-lang="Interagindo com outros aplicativos"
- - ru-lang="Взаимодействие с другими приложениями"
- - zh-cn-lang="与其他应用交互"
- - zh-tw-lang="與其他應用程式互動"
- - description="How to build a user experience that leverages other apps available on the device to perform advanced user tasks, such as capture a photo or view an address on a map."
+ path_attributes:
+ - name: ja-lang
+ value: 他のアプリとの相互操作
+ - name: ko-lang
+ value: 액티비티 수명 주기 관리하기
+ - name: pt-br-lang
+ value: Interagindo com outros aplicativos
+ - name: ru-lang
+ value: Взаимодействие с другими приложениями
+ - name: zh-cn-lang
+ value: 与其他应用交互
+ - name: zh-tw-lang
+ value: 與其他應用程式互動
+ - name: description
+ value: How to build a user experience that leverages other apps available on the device to perform advanced user tasks, such as capture a photo or view an address on a map.
section:
- title: Sending the User to Another App
path: /training/basics/intents/sending.html
- custom_link_attributes:
- - ja-lang="別のアプリにユーザーを送る"
- - ko-lang="다른 앱으로 사용자 보내기"
- - pt-br-lang="Enviando o usuário para outro aplicativo"
- - ru-lang="Направление пользователя в другое приложение"
- - zh-cn-lang="向另一个应用发送用户"
- - zh-tw-lang="將使用者傳送至其他應用程式"
+ path_attributes:
+ - name: ja-lang
+ value: 別のアプリにユーザーを送る
+ - name: ko-lang
+ value: 다른 앱으로 사용자 보내기
+ - name: pt-br-lang
+ value: Enviando o usuário para outro aplicativo
+ - name: ru-lang
+ value: Направление пользователя в другое приложение
+ - name: zh-cn-lang
+ value: 向另一个应用发送用户
+ - name: zh-tw-lang
+ value: 將使用者傳送至其他應用程式
- title: Getting a Result from the Activity
path: /training/basics/intents/result.html
- custom_link_attributes:
- - ja-lang="アクティビティから結果を取得する"
- - ko-lang="액티비티로부터 결과 가져오기"
- - pt-br-lang="Obtendo resultados de uma atividade"
- - ru-lang="Получение результата операции"
- - zh-cn-lang="获取活动的结果"
- - zh-tw-lang="從應用行為顯示取得結果"
+ path_attributes:
+ - name: ja-lang
+ value: アクティビティから結果を取得する
+ - name: ko-lang
+ value: 액티비티로부터 결과 가져오기
+ - name: pt-br-lang
+ value: Obtendo resultados de uma atividade
+ - name: ru-lang
+ value: Получение результата операции
+ - name: zh-cn-lang
+ value: 获取活动的结果
+ - name: zh-tw-lang
+ value: 從應用行為顯示取得結果
- title: Allowing Other Apps to Start Your Activity
path: /training/basics/intents/filters.html
- custom_link_attributes:
- - ja-lang="他のアプリからのアクティビティの開始を許可する"
- - ko-lang="다른 앱이 자신의 액티비티를 시작하도록 허용하기"
- - pt-br-lang="Permitindo que outros aplicativos iniciem sua atividade"
- - ru-lang="Разрешение другим приложениям на запуск вашей операции"
- - zh-cn-lang="允许其他应用开始您的活动"
- - zh-tw-lang="允許其他應用程式啟動您的應用行為顯示"
+ path_attributes:
+ - name: ja-lang
+ value: 他のアプリからのアクティビティの開始を許可する
+ - name: ko-lang
+ value: 다른 앱이 자신의 액티비티를 시작하도록 허용하기
+ - name: pt-br-lang
+ value: Permitindo que outros aplicativos iniciem sua atividade
+ - name: ru-lang
+ value: Разрешение другим приложениям на запуск вашей операции
+ - name: zh-cn-lang
+ value: 允许其他应用开始您的活动
+ - name: zh-tw-lang
+ value: 允許其他應用程式啟動您的應用行為顯示
- title: Working with System Permissions
path: /training/permissions/index.html
- custom_link_attributes:
- - description="How to declare that your app needs access to features and resources outside of its 'sandbox', and how to request those privileges at runtime."
+ path_attributes:
+ - name: description
+ value: How to declare that your app needs access to features and resources outside of its 'sandbox', and how to request those privileges at runtime.
section:
- title: Declaring Permissions
path: /training/permissions/declaring.html
@@ -172,8 +252,9 @@
section:
- title: Sharing Simple Data
path: /training/sharing/index.html
- custom_link_attributes:
- - description="How to take your app interaction to the next level by sharing information with other apps, receive information back, and provide a simple and scalable way to perform Share actions with user content."
+ path_attributes:
+ - name: description
+ value: How to take your app interaction to the next level by sharing information with other apps, receive information back, and provide a simple and scalable way to perform Share actions with user content.
section:
- title: Sending Simple Data to Other Apps
path: /training/sharing/send.html
@@ -183,8 +264,9 @@
path: /training/sharing/shareaction.html
- title: Sharing Files
path: /training/secure-file-sharing/index.html
- custom_link_attributes:
- - description="How to provide secure access to a file associated with your app using a content URI and temporary access permissions."
+ path_attributes:
+ - name: description
+ value: How to provide secure access to a file associated with your app using a content URI and temporary access permissions.
section:
- title: Setting Up File Sharing
path: /training/secure-file-sharing/setup-sharing.html
@@ -196,8 +278,9 @@
path: /training/secure-file-sharing/retrieve-info.html
- title: Sharing Files with NFC
path: /training/beam-files/index.html
- custom_link_attributes:
- - description="How to transfer files between devices using the NFC Android Beam feature."
+ path_attributes:
+ - name: description
+ value: How to transfer files between devices using the NFC Android Beam feature.
section:
- title: Sending Files to Another Device
path: /training/beam-files/send-files.html
@@ -209,8 +292,9 @@
section:
- title: Managing Audio Playback
path: /training/managing-audio/index.html
- custom_link_attributes:
- - description="How to respond to hardware audio key presses, request audio focus when playing audio, and respond appropriately to changes in audio focus."
+ path_attributes:
+ - name: description
+ value: How to respond to hardware audio key presses, request audio focus when playing audio, and respond appropriately to changes in audio focus.
section:
- title: Controlling Your App's Volume and Playback
path: /training/managing-audio/volume-playback.html
@@ -220,8 +304,9 @@
path: /training/managing-audio/audio-output.html
- title: Capturing Photos
path: /training/camera/index.html
- custom_link_attributes:
- - description="How to leverage existing camera apps on the user's device to capture photos or control the camera hardware directly and build your own camera app."
+ path_attributes:
+ - name: description
+ value: How to leverage existing camera apps on the user's device to capture photos or control the camera hardware directly and build your own camera app.
section:
- title: Taking Photos Simply
path: /training/camera/photobasics.html
@@ -231,8 +316,9 @@
path: /training/camera/cameradirect.html
- title: Printing Content
path: /training/printing/index.html
- custom_link_attributes:
- - description="How to print photos, HTML documents, and custom documents from your app."
+ path_attributes:
+ - name: description
+ value: How to print photos, HTML documents, and custom documents from your app.
section:
- title: Photos
path: /training/printing/photos.html
@@ -246,8 +332,9 @@
section:
- title: Displaying Bitmaps Efficiently
path: /training/displaying-bitmaps/index.html
- custom_link_attributes:
- - description="How to load and process bitmaps while keeping your user interface responsive and avoid exceeding memory limits."
+ path_attributes:
+ - name: description
+ value: How to load and process bitmaps while keeping your user interface responsive and avoid exceeding memory limits.
section:
- title: Loading Large Bitmaps Efficiently
path: /training/displaying-bitmaps/load-bitmap.html
@@ -261,8 +348,9 @@
path: /training/displaying-bitmaps/display-bitmap.html
- title: Displaying Graphics with OpenGL ES
path: /training/graphics/opengl/index.html
- custom_link_attributes:
- - description="How to create OpenGL graphics within the Android app framework and respond to touch input."
+ path_attributes:
+ - name: description
+ value: How to create OpenGL graphics within the Android app framework and respond to touch input.
section:
- title: Building an OpenGL ES Environment
path: /training/graphics/opengl/environment.html
@@ -278,8 +366,9 @@
path: /training/graphics/opengl/touch.html
- title: Animating Views Using Scenes and Transitions
path: /training/transitions/index.html
- custom_link_attributes:
- - description="How to animate state changes in a view hierarchy using transitions."
+ path_attributes:
+ - name: description
+ value: How to animate state changes in a view hierarchy using transitions.
section:
- title: The Transitions Framework
path: /training/transitions/overview.html
@@ -291,8 +380,9 @@
path: /training/transitions/custom-transitions.html
- title: Adding Animations
path: /training/animation/index.html
- custom_link_attributes:
- - description="How to add transitional animations to your user interface."
+ path_attributes:
+ - name: description
+ value: How to add transitional animations to your user interface.
section:
- title: Crossfading Two Views
path: /training/animation/crossfade.html
@@ -310,8 +400,9 @@
section:
- title: Connecting Devices Wirelessly
path: /training/connect-devices-wirelessly/index.html
- custom_link_attributes:
- - description="How to find and connect to local devices using Network Service Discovery and how to create peer-to-peer connections with Wi-Fi."
+ path_attributes:
+ - name: description
+ value: How to find and connect to local devices using Network Service Discovery and how to create peer-to-peer connections with Wi-Fi.
section:
- title: Using Network Service Discovery
path: /training/connect-devices-wirelessly/nsd.html
@@ -321,8 +412,9 @@
path: /training/connect-devices-wirelessly/nsd-wifi-direct.html
- title: Performing Network Operations
path: /training/basics/network-ops/index.html
- custom_link_attributes:
- - description="How to create a network connection, monitor the connection for changes in connectivity, and perform transactions with XML data."
+ path_attributes:
+ - name: description
+ value: How to create a network connection, monitor the connection for changes in connectivity, and perform transactions with XML data.
section:
- title: Connecting to the Network
path: /training/basics/network-ops/connecting.html
@@ -332,8 +424,9 @@
path: /training/basics/network-ops/xml.html
- title: Transferring Data Without Draining the Battery
path: /training/efficient-downloads/index.html
- custom_link_attributes:
- - description="How to minimize your app's impact on the battery when performing downloads and other network transactions."
+ path_attributes:
+ - name: description
+ value: How to minimize your app's impact on the battery when performing downloads and other network transactions.
section:
- title: Optimizing Downloads for Efficient Network Access
path: /training/efficient-downloads/efficient-network-access.html
@@ -345,8 +438,9 @@
path: /training/efficient-downloads/connectivity_patterns.html
- title: Syncing to the Cloud
path: /training/backup/index.html
- custom_link_attributes:
- - description="How to sync and back up app and user data to remote web services in the cloud and how to restore the data back to multiple devices."
+ path_attributes:
+ - name: description
+ value: How to sync and back up app and user data to remote web services in the cloud and how to restore the data back to multiple devices.
section:
- title: Configuring Auto Backup
path: /training/backup/autosyncapi.html
@@ -354,12 +448,14 @@
path: /training/backup/backupapi.html
- title: Resolving Cloud Save Conflicts
path: /training/cloudsave/conflict-res.html
- custom_link_attributes:
- - description="How to design a robust conflict resolution strategy for apps that save data to the cloud."
+ path_attributes:
+ - name: description
+ value: How to design a robust conflict resolution strategy for apps that save data to the cloud.
- title: Transferring Data Using Sync Adapters
path: /training/sync-adapters/index.html
- custom_link_attributes:
- - description="How to transfer data between the cloud and the device using the Android sync adapter framework"
+ path_attributes:
+ - name: description
+ value: How to transfer data between the cloud and the device using the Android sync adapter framework
section:
- title: Creating a Stub Authenticator
path: /training/sync-adapters/creating-authenticator.html
@@ -371,8 +467,9 @@
path: /training/sync-adapters/running-sync-adapter.html
- title: Transmitting Network Data Using Volley
path: /training/volley/index.html
- custom_link_attributes:
- - description="How to perform fast, scalable UI operations over the network using Volley"
+ path_attributes:
+ - name: description
+ value: How to perform fast, scalable UI operations over the network using Volley
section:
- title: Sending a Simple Request
path: /training/volley/simple.html
@@ -388,8 +485,9 @@
section:
- title: Making Your App Location-Aware
path: /training/location/index.html
- custom_link_attributes:
- - description="How to add location-aware features to your app by getting the user's current location."
+ path_attributes:
+ - name: description
+ value: How to add location-aware features to your app by getting the user's current location.
section:
- title: Getting the Last Known Location
path: /training/location/retrieve-current.html
@@ -403,16 +501,18 @@
path: /training/location/geofencing.html
- title: Adding Maps
path: /training/maps/index.html
- custom_link_attributes:
- - description="How to add maps and mapping information to your app."
+ path_attributes:
+ - name: description
+ value: How to add maps and mapping information to your app.
- title: Building Apps with User Info & Sign-In
path: /training/building-userinfo.html
section:
- title: Accessing Contacts Data
path: /training/contacts-provider/index.html
- custom_link_attributes:
- - description="How to use Android's central address book, the Contacts Provider, to display contacts and their details and modify contact information."
+ path_attributes:
+ - name: description
+ value: How to use Android's central address book, the Contacts Provider, to display contacts and their details and modify contact information.
section:
- title: Retrieving a List of Contacts
path: /training/contacts-provider/retrieve-names.html
@@ -424,16 +524,18 @@
path: /training/contacts-provider/display-contact-badge.html
- title: Adding Sign-In
path: /training/sign-in/index.html
- custom_link_attributes:
- - description="How to add user sign-in functionality to your app."
+ path_attributes:
+ - name: description
+ value: How to add user sign-in functionality to your app.
- title: Building Apps for Wearables
path: /training/building-wearables.html
section:
- title: Adding Wearable Features to Notifications
path: /training/wearables/notifications/index.html
- custom_link_attributes:
- - description="How to build handheld notifications that are synced to and look great on wearables."
+ path_attributes:
+ - name: description
+ value: How to build handheld notifications that are synced to and look great on wearables.
section:
- title: Creating a Notification
path: /training/wearables/notifications/creating.html
@@ -445,8 +547,9 @@
path: /training/wearables/notifications/stacks.html
- title: Creating Wearable Apps
path: /training/wearables/apps/index.html
- custom_link_attributes:
- - description="How to build apps that run directly on wearables."
+ path_attributes:
+ - name: description
+ value: How to build apps that run directly on wearables.
section:
- title: Creating and Running a Wearable App
path: /training/wearables/apps/creating.html
@@ -462,8 +565,9 @@
path: /training/wearables/apps/bt-debugging.html
- title: Creating Custom UIs
path: /training/wearables/ui/index.html
- custom_link_attributes:
- - description="How to create custom user interfaces for wearable apps."
+ path_attributes:
+ - name: description
+ value: How to create custom user interfaces for wearable apps.
section:
- title: Defining Layouts
path: /training/wearables/ui/layouts.html
@@ -479,8 +583,9 @@
path: /training/wearables/ui/exit.html
- title: Sending and Syncing Data
path: /training/wearables/data-layer/index.html
- custom_link_attributes:
- - description="How to sync data between handhelds and wearables."
+ path_attributes:
+ - name: description
+ value: How to sync data between handhelds and wearables.
section:
- title: Accessing the Wearable Data Layer
path: /training/wearables/data-layer/accessing.html
@@ -494,8 +599,9 @@
path: /training/wearables/data-layer/events.html
- title: Creating Watch Faces
path: /training/wearables/watch-faces/index.html
- custom_link_attributes:
- - description="How to create watch faces for wearables."
+ path_attributes:
+ - name: description
+ value: How to create watch faces for wearables.
section:
- title: Designing Watch Faces
path: /training/wearables/watch-faces/designing.html
@@ -515,70 +621,85 @@
path: /training/wearables/watch-faces/performance.html
- title: Detecting Location
path: /training/articles/wear-location-detection.html
- custom_link_attributes:
- - description="How to detect location data on Android Wear devices."
+ path_attributes:
+ - name: description
+ value: How to detect location data on Android Wear devices.
- title: Requesting Permissions
path: /training/articles/wear-permissions.html
- custom_link_attributes:
- - description="How to request permissions on Android Wear devices."
+ path_attributes:
+ - name: description
+ value: How to request permissions on Android Wear devices.
- title: Using the Speaker
path: /training/wearables/wearable-sounds.html
- custom_link_attributes:
- - description="How to use the speaker on Android Wear devices."
+ path_attributes:
+ - name: description
+ value: How to use the speaker on Android Wear devices.
- title: Building Apps for TV
path: /training/tv/index.html
section:
- title: Building TV Apps
path: /training/tv/start/index.html
- custom_link_attributes:
- - ja-lang="TV アプリのビルド"
- - description="How to start building TV apps or extend your existing app to run on TV devices."
+ path_attributes:
+ - name: ja-lang
+ value: TV アプリのビルド
+ - name: description
+ value: How to start building TV apps or extend your existing app to run on TV devices.
section:
- title: Getting Started with TV Apps
path: /training/tv/start/start.html
- custom_link_attributes:
- - ja-lang="TV アプリのビルドを開始する"
+ path_attributes:
+ - name: ja-lang
+ value: TV アプリのビルドを開始する
- title: Handling TV Hardware
path: /training/tv/start/hardware.html
- custom_link_attributes:
- - ja-lang="TV ハードウェアを処理する"
+ path_attributes:
+ - name: ja-lang
+ value: TV ハードウェアを処理する
- title: Building TV Layouts
path: /training/tv/start/layouts.html
- custom_link_attributes:
- - ja-lang="TV 向けレイアウトをビルドする"
+ path_attributes:
+ - name: ja-lang
+ value: TV 向けレイアウトをビルドする
- title: Creating TV Navigation
path: /training/tv/start/navigation.html
- custom_link_attributes:
- - ja-lang="TV 用のナビゲーションを作成する"
+ path_attributes:
+ - name: ja-lang
+ value: TV 用のナビゲーションを作成する
- title: Building TV Playback Apps
path: /training/tv/playback/index.html
- custom_link_attributes:
- - ja-lang="TV 再生アプリのビルド"
- - description="How to build apps that provide media catalogs and play content."
+ path_attributes:
+ - name: ja-lang
+ value: TV 再生アプリのビルド
+ - name: description
+ value: How to build apps that provide media catalogs and play content.
section:
- title: Creating a Catalog Browser
path: /training/tv/playback/browse.html
- custom_link_attributes:
- - ja-lang="カタログ ブラウザを作成する"
+ path_attributes:
+ - name: ja-lang
+ value: カタログ ブラウザを作成する
- title: Providing a Card View
path: /training/tv/playback/card.html
- title: Building a Details View
path: /training/tv/playback/details.html
- custom_link_attributes:
- - ja-lang="詳細ビューをビルドする"
+ path_attributes:
+ - name: ja-lang
+ value: 詳細ビューをビルドする
- title: Displaying a Now Playing Card
path: /training/tv/playback/now-playing.html
- custom_link_attributes:
- - ja-lang="再生中カードを表示する"
+ path_attributes:
+ - name: ja-lang
+ value: 再生中カードを表示する
- title: Adding a Guided Step
path: /training/tv/playback/guided-step.html
- title: Enabling Background Playback
path: /training/tv/playback/options.html
- title: Helping Users Find Content on TV
path: /training/tv/discovery/index.html
- custom_link_attributes:
- - description="How to help users discover content from your app."
+ path_attributes:
+ - name: description
+ value: How to help users discover content from your app.
section:
- title: Recommending TV Content
path: /training/tv/discovery/recommendations.html
@@ -588,12 +709,14 @@
path: /training/tv/discovery/in-app-search.html
- title: Building TV Games
path: /training/tv/games/index.html
- custom_link_attributes:
- - description="How to build games for TV."
+ path_attributes:
+ - name: description
+ value: How to build games for TV.
- title: Building TV Channels
path: /training/tv/tif/index.html
- custom_link_attributes:
- - description="How to build channels for TV."
+ path_attributes:
+ - name: description
+ value: How to build channels for TV.
section:
- title: Developing a TV Input Service
path: /training/tv/tif/tvinput.html
@@ -603,24 +726,28 @@
path: /training/tv/tif/ui.html
- title: TV Apps Checklist
path: /training/tv/publishing/checklist.html
- custom_link_attributes:
- - description="An itemized list of requirements for TV apps."
+ path_attributes:
+ - name: description
+ value: An itemized list of requirements for TV apps.
- title: Building Apps for Auto
path: /training/auto/index.html
section:
- title: Getting Started with Auto
path: /training/auto/start/index.html
- custom_link_attributes:
- - description="How to start building or extending apps that work with Auto devices."
+ path_attributes:
+ - name: description
+ value: How to start building or extending apps that work with Auto devices.
- title: Playing Audio for Auto
path: /training/auto/audio/index.html
- custom_link_attributes:
- - description="How to extend audio apps to play content on Auto devices."
+ path_attributes:
+ - name: description
+ value: How to extend audio apps to play content on Auto devices.
- title: Messaging for Auto
path: /training/auto/messaging/index.html
- custom_link_attributes:
- - description="How to extend text messaging apps to work with Auto devices."
+ path_attributes:
+ - name: description
+ value: How to extend text messaging apps to work with Auto devices.
- title: Building Apps for Work
path: /training/enterprise/index.html
@@ -639,8 +766,9 @@
section:
- title: Designing Effective Navigation
path: /training/design-navigation/index.html
- custom_link_attributes:
- - description="How to plan your app's screen hierarchy and forms of navigation so users can effectively and intuitively traverse your app content using various navigation patterns."
+ path_attributes:
+ - name: description
+ value: How to plan your app's screen hierarchy and forms of navigation so users can effectively and intuitively traverse your app content using various navigation patterns.
section:
- title: Planning Screens and Their Relationships
path: /training/design-navigation/screen-planning.html
@@ -654,8 +782,9 @@
path: /training/design-navigation/wireframing.html
- title: Implementing Effective Navigation
path: /training/implementing-navigation/index.html
- custom_link_attributes:
- - description="How to implement various navigation patterns such as swipe views, a navigation drawer, and up navigation."
+ path_attributes:
+ - name: description
+ value: How to implement various navigation patterns such as swipe views, a navigation drawer, and up navigation.
section:
- title: Creating Swipe Views with Tabs
path: /training/implementing-navigation/lateral.html
@@ -669,8 +798,9 @@
path: /training/implementing-navigation/descendant.html
- title: Notifying the User
path: /training/notify-user/index.html
- custom_link_attributes:
- - description="How to display messages called notifications outside of your application's UI."
+ path_attributes:
+ - name: description
+ value: How to display messages called notifications outside of your application's UI.
section:
- title: Building a Notification
path: /training/notify-user/build-notification.html
@@ -684,8 +814,9 @@
path: /training/notify-user/display-progress.html
- title: Supporting Swipe-to-Refresh
path: /training/swipe/index.html
- custom_link_attributes:
- - description="How to modify your app's layout to support manual content updates triggered by the swipe-to-refresh gesture."
+ path_attributes:
+ - name: description
+ value: How to modify your app's layout to support manual content updates triggered by the swipe-to-refresh gesture.
section:
- title: Adding Swipe-to-Refresh To Your App
path: /training/swipe/add-swipe-interface.html
@@ -693,8 +824,9 @@
path: /training/swipe/respond-refresh-request.html
- title: Adding Search Functionality
path: /training/search/index.html
- custom_link_attributes:
- - description="How to properly add a search interface to your app and create a searchable database."
+ path_attributes:
+ - name: description
+ value: How to properly add a search interface to your app and create a searchable database.
section:
- title: Setting up the Search Interface
path: /training/search/setup.html
@@ -704,8 +836,9 @@
path: /training/search/backward-compat.html
- title: Making Your App Content Searchable by Google
path: /training/app-indexing/index.html
- custom_link_attributes:
- - description="How to enable deep linking and indexing of your application content so that users can open this content directly from their mobile search results."
+ path_attributes:
+ - name: description
+ value: How to enable deep linking and indexing of your application content so that users can open this content directly from their mobile search results.
section:
- title: Enabling Deep Links for App Content
path: /training/app-indexing/deep-linking.html
@@ -713,47 +846,64 @@
path: /training/app-indexing/enabling-app-indexing.html
- title: Optimizing Content for the Assistant
path: /training/articles/assistant.html
- custom_link_attributes:
- - description="Support contextually relevant actions through the Assist API."
+ path_attributes:
+ - name: description
+ value: Support contextually relevant actions through the Assist API.
- title: Handling App Links
path: /training/app-links/index.html
- custom_link_attributes:
- - description="How to enable the system to handle web requests by taking the user directly to your app instead of your website."
+ path_attributes:
+ - name: description
+ value: How to enable the system to handle web requests by taking the user directly to your app instead of your website.
- title: Best Practices for User Interface
path: /training/best-ui.html
section:
- title: Designing for Multiple Screens
path: /training/multiscreen/index.html
- custom_link_attributes:
- - es-lang="Cómo diseñar aplicaciones para varias pantallas"
- - ja-lang="複数画面のデザイン"
- - zh-cn-lang="针对多种屏幕进行设计"
- - description="How to build a user interface that's flexible enough to fit perfectly on any screen and how to create different interaction patterns that are optimized for different screen sizes."
+ path_attributes:
+ - name: es-lang
+ value: Cómo diseñar aplicaciones para varias pantallas
+ - name: ja-lang
+ value: 複数画面のデザイン
+ - name: zh-cn-lang
+ value: 针对多种屏幕进行设计
+ - name: description
+ value: How to build a user interface that's flexible enough to fit perfectly on any screen and how to create different interaction patterns that are optimized for different screen sizes.
section:
- title: Supporting Different Screen Sizes
path: /training/multiscreen/screensizes.html
- custom_link_attributes:
- - es-lang="Cómo admitir varios tamaños de pantalla"
- - ja-lang="さまざまな画面サイズのサポート"
- - ko-lang="다양한 화면 크기 지원"
- - zh-cn-lang="支持各种屏幕尺寸"
+ path_attributes:
+ - name: es-lang
+ value: Cómo admitir varios tamaños de pantalla
+ - name: ja-lang
+ value: さまざまな画面サイズのサポート
+ - name: ko-lang
+ value: 다양한 화면 크기 지원
+ - name: zh-cn-lang
+ value: 支持各种屏幕尺寸
- title: Supporting Different Screen Densities
path: /training/multiscreen/screendensities.html
- custom_link_attributes:
- - es-lang="Cómo admitir varias densidades de pantalla"
- - ja-lang="さまざまな画面密度のサポート"
- - zh-cn-lang="支持各种屏幕密度"
+ path_attributes:
+ - name: es-lang
+ value: Cómo admitir varias densidades de pantalla
+ - name: ja-lang
+ value: さまざまな画面密度のサポート
+ - name: zh-cn-lang
+ value: 支持各种屏幕密度
- title: Implementing Adaptive UI Flows
path: /training/multiscreen/adaptui.html
- custom_link_attributes:
- - es-lang="Cómo implementar interfaces de usuario adaptables"
- - ja-lang="順応性のある UI フローの実装"
- - zh-cn-lang="实施自适应用户界面流程"
+ path_attributes:
+ - name: es-lang
+ value: Cómo implementar interfaces de usuario adaptables
+ - name: ja-lang
+ value: 順応性のある UI フローの実装
+ - name: zh-cn-lang
+ value: 实施自适应用户界面流程
- title: Adding the App Bar
path: /training/appbar/index.html
- custom_link_attributes:
- - description="How to use the support library's toolbar widget to implement an app bar that displays properly on a wide range of devices."
+ path_attributes:
+ - name: description
+ value: How to use the support library's toolbar widget to implement an app bar that displays properly on a wide range of devices.
section:
- title: Setting Up the App Bar
path: /training/appbar/setting-up.html
@@ -765,8 +915,9 @@
path: /training/appbar/action-views.html
- title: Showing Pop-Up Messages
path: /training/snackbar/index.html
- custom_link_attributes:
- - description="How to use the support library's Snackbar widget to display a brief pop-up message."
+ path_attributes:
+ - name: description
+ value: How to use the support library's Snackbar widget to display a brief pop-up message.
section:
- title: Building and Displaying a Pop-Up Message
path: /training/snackbar/showing.html
@@ -774,8 +925,9 @@
path: /training/snackbar/action.html
- title: Creating Custom Views
path: /training/custom-views/index.html
- custom_link_attributes:
- - description="How to build custom UI widgets that are interactive and smooth."
+ path_attributes:
+ - name: description
+ value: How to build custom UI widgets that are interactive and smooth.
section:
- title: Creating a Custom View Class
path: /training/custom-views/create-view.html
@@ -787,8 +939,9 @@
path: /training/custom-views/optimizing-view.html
- title: Creating Backward-Compatible UIs
path: /training/backward-compatible-ui/index.html
- custom_link_attributes:
- - description="How to use UI components and other APIs from the more recent versions of Android while remaining compatible with older versions of the platform."
+ path_attributes:
+ - name: description
+ value: How to use UI components and other APIs from the more recent versions of Android while remaining compatible with older versions of the platform.
section:
- title: Abstracting the New APIs
path: /training/backward-compatible-ui/abstracting.html
@@ -800,8 +953,9 @@
path: /training/backward-compatible-ui/using-component.html
- title: Implementing Accessibility
path: /training/accessibility/index.html
- custom_link_attributes:
- - description="How to make your app accessible to users with vision impairment or other physical disabilities."
+ path_attributes:
+ - name: description
+ value: How to make your app accessible to users with vision impairment or other physical disabilities.
section:
- title: Developing Accessible Applications
path: /training/accessibility/accessible-app.html
@@ -811,8 +965,9 @@
path: /training/accessibility/testing.html
- title: Managing the System UI
path: /training/system-ui/index.html
- custom_link_attributes:
- - description="How to hide and show status and navigation bars across different versions of Android, while managing the display of other screen components."
+ path_attributes:
+ - name: description
+ value: How to hide and show status and navigation bars across different versions of Android, while managing the display of other screen components.
section:
- title: Dimming the System Bars
path: /training/system-ui/dim.html
@@ -826,110 +981,184 @@
path: /training/system-ui/visibility.html
- title: Creating Apps with Material Design
path: /training/material/index.html
- custom_link_attributes:
- - es-lang="Crear aplicaciones con Material Design"
- - in-lang="Desain Bahan untuk Pengembang"
- - ja-lang="マテリアル デザインでのアプリ作成"
- - ko-lang="개발자를 위한 머티리얼 디자인"
- - pt-br-lang="Material Design para desenvolvedores"
- - ru-lang="Создание приложений с помощью Material Design"
- - vi-lang="Material Design cho Nhà phát triển"
- - zh-cn-lang="面向开发者的材料设计"
- - zh-tw-lang="開發人員材料設計"
- - description="How to implement material design on Android."
+ path_attributes:
+ - name: es-lang
+ value: Crear aplicaciones con Material Design
+ - name: in-lang
+ value: Desain Bahan untuk Pengembang
+ - name: ja-lang
+ value: マテリアル デザインでのアプリ作成
+ - name: ko-lang
+ value: 개발자를 위한 머티리얼 디자인
+ - name: pt-br-lang
+ value: Material Design para desenvolvedores
+ - name: ru-lang
+ value: Создание приложений с помощью Material Design
+ - name: vi-lang
+ value: Material Design cho Nhà phát triển
+ - name: zh-cn-lang
+ value: 面向开发者的材料设计
+ - name: zh-tw-lang
+ value: 開發人員材料設計
+ - name: description
+ value: How to implement material design on Android.
section:
- title: Getting Started
path: /training/material/get-started.html
- custom_link_attributes:
- - es-lang="Comencemos"
- - in-lang="Memulai"
- - ja-lang="スタート ガイド"
- - ko-lang="시작하기"
- - pt-br-lang="Como iniciar"
- - ru-lang="Начало работы"
- - vi-lang="Bắt đầu"
- - zh-cn-lang="入门指南"
- - zh-tw-lang="開始使用"
+ path_attributes:
+ - name: es-lang
+ value: Comencemos
+ - name: in-lang
+ value: Memulai
+ - name: ja-lang
+ value: スタート ガイド
+ - name: ko-lang
+ value: 시작하기
+ - name: pt-br-lang
+ value: Como iniciar
+ - name: ru-lang
+ value: Начало работы
+ - name: vi-lang
+ value: Bắt đầu
+ - name: zh-cn-lang
+ value: 入门指南
+ - name: zh-tw-lang
+ value: 開始使用
- title: Using the Material Theme
path: /training/material/theme.html
- custom_link_attributes:
- - es-lang="Usar el tema Material"
- - in-lang="Menggunakan Tema Bahan"
- - ja-lang="マテリアル テーマの使用"
- - ko-lang="머티어리얼 테마 사용"
- - pt-br-lang="Como usar o tema do Material"
- - ru-lang="Использование темы Material Design"
- - vi-lang="Sử dụng Chủ đề Material"
- - zh-cn-lang="使用材料主题"
- - zh-tw-lang="使用材料設計風格"
+ path_attributes:
+ - name: es-lang
+ value: Usar el tema Material
+ - name: in-lang
+ value: Menggunakan Tema Bahan
+ - name: ja-lang
+ value: マテリアル テーマの使用
+ - name: ko-lang
+ value: 머티어리얼 테마 사용
+ - name: pt-br-lang
+ value: Como usar o tema do Material
+ - name: ru-lang
+ value: Использование темы Material Design
+ - name: vi-lang
+ value: Sử dụng Chủ đề Material
+ - name: zh-cn-lang
+ value: 使用材料主题
+ - name: zh-tw-lang
+ value: 使用材料設計風格
- title: Creating Lists and Cards
path: /training/material/lists-cards.html
- custom_link_attributes:
- - es-lang="Crear listas y tarjetas"
- - in-lang="Membuat Daftar dan Kartu"
- - ja-lang="リストとカードの作成"
- - ko-lang="목록 및 카드 생성"
- - pt-br-lang="Como criar listas e cartões"
- - ru-lang="Создание списков и подсказок"
- - vi-lang="Tạo Danh sách và Thẻ"
- - zh-cn-lang="创建列表与卡片"
- - zh-tw-lang="建立清單和卡片"
+ path_attributes:
+ - name: es-lang
+ value: Crear listas y tarjetas
+ - name: in-lang
+ value: Membuat Daftar dan Kartu
+ - name: ja-lang
+ value: リストとカードの作成
+ - name: ko-lang
+ value: 목록 및 카드 생성
+ - name: pt-br-lang
+ value: Como criar listas e cartões
+ - name: ru-lang
+ value: Создание списков и подсказок
+ - name: vi-lang
+ value: Tạo Danh sách và Thẻ
+ - name: zh-cn-lang
+ value: 创建列表与卡片
+ - name: zh-tw-lang
+ value: 建立清單和卡片
- title: Defining Shadows and Clipping Views
path: /training/material/shadows-clipping.html
- custom_link_attributes:
- - es-lang="Definir vistas de recorte y sombras"
- - in-lang="Mendefinisikan Bayangan dan Memangkas Tampilan"
- - ja-lang="シャドウとクリッピング ビューの定義"
- - ko-lang="그림자 정의 및 뷰 클리핑"
- - pt-br-lang="Como definir sombras e recortar visualizações"
- - ru-lang="Определение теней и обрезка представлений"
- - vi-lang="Định nghĩa Đổ bóng và Dạng xem Cắt hình"
- - zh-cn-lang="定义阴影与裁剪视图"
- - zh-tw-lang="定義陰影和裁剪檢視"
+ path_attributes:
+ - name: es-lang
+ value: Definir vistas de recorte y sombras
+ - name: in-lang
+ value: Mendefinisikan Bayangan dan Memangkas Tampilan
+ - name: ja-lang
+ value: シャドウとクリッピング ビューの定義
+ - name: ko-lang
+ value: 그림자 정의 및 뷰 클리핑
+ - name: pt-br-lang
+ value: Como definir sombras e recortar visualizações
+ - name: ru-lang
+ value: Определение теней и обрезка представлений
+ - name: vi-lang
+ value: Định nghĩa Đổ bóng và Dạng xem Cắt hình
+ - name: zh-cn-lang
+ value: 定义阴影与裁剪视图
+ - name: zh-tw-lang
+ value: 定義陰影和裁剪檢視
- title: Working with Drawables
path: /training/material/drawables.html
- custom_link_attributes:
- - es-lang="Trabajar con interfaces dibujables"
- - in-lang="Bekerja dengan Drawable"
- - ja-lang="ドローアブルの使用"
- - ko-lang="Drawable 사용"
- - pt-br-lang="Como trabalhar com desenháveis"
- - ru-lang="Работа с элементами дизайна"
- - vi-lang="Làm việc với Nội dung vẽ được"
- - zh-cn-lang="使用 Drawables"
- - zh-tw-lang="使用可繪項目"
+ path_attributes:
+ - name: es-lang
+ value: Trabajar con interfaces dibujables
+ - name: in-lang
+ value: Bekerja dengan Drawable
+ - name: ja-lang
+ value: ドローアブルの使用
+ - name: ko-lang
+ value: Drawable 사용
+ - name: pt-br-lang
+ value: Como trabalhar com desenháveis
+ - name: ru-lang
+ value: Работа с элементами дизайна
+ - name: vi-lang
+ value: Làm việc với Nội dung vẽ được
+ - name: zh-cn-lang
+ value: 使用 Drawables
+ - name: zh-tw-lang
+ value: 使用可繪項目
- title: Defining Custom Animations
path: /training/material/animations.html
- custom_link_attributes:
- - es-lang="Definir animaciones personalizadas"
- - in-lang="Mendefinisikan Animasi Custom"
- - ja-lang="カスタム アニメーションの定義"
- - ko-lang="사용자지정 애니메이션 정의"
- - pt-br-lang="Como definir animações personalizadas"
- - ru-lang="Определение настраиваемой анимации"
- - vi-lang="Định nghĩa Hoạt hình Tùy chỉnh"
- - zh-cn-lang="定义定制动画"
- - zh-tw-lang="定義自訂動畫"
+ path_attributes:
+ - name: es-lang
+ value: Definir animaciones personalizadas
+ - name: in-lang
+ value: Mendefinisikan Animasi Custom
+ - name: ja-lang
+ value: カスタム アニメーションの定義
+ - name: ko-lang
+ value: 사용자지정 애니메이션 정의
+ - name: pt-br-lang
+ value: Como definir animações personalizadas
+ - name: ru-lang
+ value: Определение настраиваемой анимации
+ - name: vi-lang
+ value: Định nghĩa Hoạt hình Tùy chỉnh
+ - name: zh-cn-lang
+ value: 定义定制动画
+ - name: zh-tw-lang
+ value: 定義自訂動畫
- title: Maintaining Compatibility
path: /training/material/compatibility.html
- custom_link_attributes:
- - es-lang="Mantener la compatibilidad"
- - in-lang="Mempertahankan Kompatibilitas"
- - ja-lang="互換性の維持"
- - ko-lang="호환성 유지"
- - pt-br-lang="Como manter a compatibilidade"
- - ru-lang="Обеспечение совместимости"
- - vi-lang="Duy trì Tính tương thích"
- - zh-cn-lang="维护兼容性"
- - zh-tw-lang="維持相容性"
+ path_attributes:
+ - name: es-lang
+ value: Mantener la compatibilidad
+ - name: in-lang
+ value: Mempertahankan Kompatibilitas
+ - name: ja-lang
+ value: 互換性の維持
+ - name: ko-lang
+ value: 호환성 유지
+ - name: pt-br-lang
+ value: Como manter a compatibilidade
+ - name: ru-lang
+ value: Обеспечение совместимости
+ - name: vi-lang
+ value: Duy trì Tính tương thích
+ - name: zh-cn-lang
+ value: 维护兼容性
+ - name: zh-tw-lang
+ value: 維持相容性
- title: Best Practices for User Input
path: /training/best-user-input.html
section:
- title: Using Touch Gestures
path: /training/gestures/index.html
- custom_link_attributes:
- - description="How to write apps that allow users to interact with the touch screen via touch gestures."
+ path_attributes:
+ - name: description
+ value: How to write apps that allow users to interact with the touch screen via touch gestures.
section:
- title: Detecting Common Gestures
path: /training/gestures/detector.html
@@ -945,8 +1174,9 @@
path: /training/gestures/viewgroup.html
- title: Handling Keyboard Input
path: /training/keyboard-input/index.html
- custom_link_attributes:
- - description="How to specify the appearance and behaviors of soft input methods (such as on-screen keyboards) and how to optimize the experience with hardware keyboards."
+ path_attributes:
+ - name: description
+ value: How to specify the appearance and behaviors of soft input methods (such as on-screen keyboards) and how to optimize the experience with hardware keyboards.
section:
- title: Specifying the Input Method Type
path: /training/keyboard-input/style.html
@@ -958,8 +1188,9 @@
path: /training/keyboard-input/commands.html
- title: Supporting Game Controllers
path: /training/game-controllers/index.html
- custom_link_attributes:
- - description="How to write apps that support game controllers."
+ path_attributes:
+ - name: description
+ value: How to write apps that support game controllers.
section:
- title: Handling Controller Actions
path: /training/game-controllers/controller-input.html
@@ -973,8 +1204,9 @@
section:
- title: Running in a Background Service
path: /training/run-background-service/index.html
- custom_link_attributes:
- - description="How to improve UI performance and responsiveness by sending work to a Service running in the background"
+ path_attributes:
+ - name: description
+ value: How to improve UI performance and responsiveness by sending work to a Service running in the background
section:
- title: Creating a Background Service
path: /training/run-background-service/create-service.html
@@ -984,8 +1216,9 @@
path: /training/run-background-service/report-status.html
- title: Loading Data in the Background
path: /training/load-data-background/index.html
- custom_link_attributes:
- - description="How to use CursorLoader to query data without affecting UI responsiveness."
+ path_attributes:
+ - name: description
+ value: How to use CursorLoader to query data without affecting UI responsiveness.
section:
- title: Running a Query with a CursorLoader
path: /training/load-data-background/setup-loader.html
@@ -993,8 +1226,9 @@
path: /training/load-data-background/handle-results.html
- title: Managing Device Awake State
path: /training/scheduling/index.html
- custom_link_attributes:
- - description="How to use repeating alarms and wake locks to run background jobs."
+ path_attributes:
+ - name: description
+ value: How to use repeating alarms and wake locks to run background jobs.
section:
- title: Keeping the Device Awake
path: /training/scheduling/wakelock.html
@@ -1006,16 +1240,19 @@
section:
- title: Managing Your App's Memory
path: /training/articles/memory.html
- custom_link_attributes:
- - description="How to keep your app's memory footprint small in order to improve performance on a variety of mobile devices."
+ path_attributes:
+ - name: description
+ value: How to keep your app's memory footprint small in order to improve performance on a variety of mobile devices.
- title: Performance Tips
path: /training/articles/perf-tips.html
- custom_link_attributes:
- - description="How to optimize your app's performance in various ways to improve its responsiveness and battery efficiency."
+ path_attributes:
+ - name: description
+ value: How to optimize your app's performance in various ways to improve its responsiveness and battery efficiency.
- title: Improving Layout Performance
path: /training/improving-layouts/index.html
- custom_link_attributes:
- - description="How to identify problems in your app's layout performance and improve the UI responsiveness."
+ path_attributes:
+ - name: description
+ value: How to identify problems in your app's layout performance and improve the UI responsiveness.
section:
- title: Optimizing Layout Hierarchies
path: /training/improving-layouts/optimizing-layout.html
@@ -1027,11 +1264,15 @@
path: /training/improving-layouts/smooth-scrolling.html
- title: Optimizing Battery Life
path: /training/monitoring-device-state/index.html
- custom_link_attributes:
- - es-lang="Cómo optimizar la duración de la batería"
- - ja-lang="電池消費量の最適化"
- - zh-cn-lang="优化电池使用时间"
- - description="How to minimize the amount of power your app requires by adapting to current power conditions and performing power-hungry tasks at proper intervals."
+ path_attributes:
+ - name: es-lang
+ value: Cómo optimizar la duración de la batería
+ - name: ja-lang
+ value: 電池消費量の最適化
+ - name: zh-cn-lang
+ value: 优化电池使用时间
+ - name: description
+ value: How to minimize the amount of power your app requires by adapting to current power conditions and performing power-hungry tasks at proper intervals.
section:
- title: Reducing Network Battery Drain
path: /training/performance/battery/network/index.html
@@ -1052,32 +1293,45 @@
path: /training/monitoring-device-state/doze-standby.html
- title: Monitoring the Battery Level and Charging State
path: /training/monitoring-device-state/battery-monitoring.html
- custom_link_attributes:
- - es-lang="Cómo controlar el nivel de batería y el estado de carga"
- - ja-lang="電池残量と充電状態の監視"
- - zh-cn-lang="监控电池电量和充电状态"
+ path_attributes:
+ - name: es-lang
+ value: Cómo controlar el nivel de batería y el estado de carga
+ - name: ja-lang
+ value: 電池残量と充電状態の監視
+ - name: zh-cn-lang
+ value: 监控电池电量和充电状态
- title: Determining and Monitoring the Docking State and Type
path: /training/monitoring-device-state/docking-monitoring.html
- custom_link_attributes:
- - es-lang="Cómo determinar y controlar el tipo de conector y el estado de la conexión"
- - ja-lang="ホルダーの装着状態とタイプの特定と監視"
- - zh-cn-lang="确定和监控基座对接状态和类型"
+ path_attributes:
+ - name: es-lang
+ value: Cómo determinar y controlar el tipo de conector y el estado de la conexión
+ - name: ja-lang
+ value: ホルダーの装着状態とタイプの特定と監視
+ - name: zh-cn-lang
+ value: 确定和监控基座对接状态和类型
- title: Determining and Monitoring the Connectivity Status
path: /training/monitoring-device-state/connectivity-monitoring.html
- custom_link_attributes:
- - es-lang="Cómo determinar y controlar el estado de la conectividad"
- - ja-lang="接続状態の特定と監視"
- - zh-cn-lang="确定和监控网络连接状态"
+ path_attributes:
+ - name: es-lang
+ value: Cómo determinar y controlar el estado de la conectividad
+ - name: ja-lang
+ value: 接続状態の特定と監視
+ - name: zh-cn-lang
+ value: 确定和监控网络连接状态
- title: Manipulating Broadcast Receivers On Demand
path: /training/monitoring-device-state/manifest-receivers.html
- custom_link_attributes:
- - es-lang="Cómo manipular los receptores de emisión bajo demanda"
- - ja-lang="オンデマンドでのブロードキャスト レシーバ操作"
- - zh-cn-lang="根据需要操作广播接收器"
+ path_attributes:
+ - name: es-lang
+ value: Cómo manipular los receptores de emisión bajo demanda
+ - name: ja-lang
+ value: オンデマンドでのブロードキャスト レシーバ操作
+ - name: zh-cn-lang
+ value: 根据需要操作广播接收器
- title: Sending Operations to Multiple Threads
path: /training/multiple-threads/index.html
- custom_link_attributes:
- - description="How to improve the performance and scalability of long-running operations by dispatching work to multiple threads."
+ path_attributes:
+ - name: description
+ value: How to improve the performance and scalability of long-running operations by dispatching work to multiple threads.
section:
- title: Specifying the Code to Run on a Thread
path: /training/multiple-threads/define-runnable.html
@@ -1089,68 +1343,81 @@
path: /training/multiple-threads/communicate-ui.html
- title: Keeping Your App Responsive
path: /training/articles/perf-anr.html
- custom_link_attributes:
- - description="How to keep your app responsive to user interaction so the UI does not lock-up and display an \"Application Not Responding\" dialog."
+ path_attributes:
+ - name: description
+ value: How to keep your app responsive to user interaction so the UI does not lock-up and display an "Application Not Responding" dialog.
- title: JNI Tips
path: /training/articles/perf-jni.html
- custom_link_attributes:
- - description="How to efficiently use the Java Native Interface with the Android NDK."
+ path_attributes:
+ - name: description
+ value: How to efficiently use the Java Native Interface with the Android NDK.
- title: SMP Primer for Android
path: /training/articles/smp.html
- custom_link_attributes:
- - description="Tips for coding Android apps on symmetric multiprocessor systems."
+ path_attributes:
+ - name: description
+ value: Tips for coding Android apps on symmetric multiprocessor systems.
- title: Best Practices for Security & Privacy
path: /training/best-security.html
section:
- title: Security Tips
path: /training/articles/security-tips.html
- custom_link_attributes:
- - description="How to perform various tasks and keep your app's data and your user's data secure."
+ path_attributes:
+ - name: description
+ value: How to perform various tasks and keep your app's data and your user's data secure.
- title: Security with HTTPS and SSL
path: /training/articles/security-ssl.html
- custom_link_attributes:
- - description="How to ensure that your app is secure when performing network transactions."
+ path_attributes:
+ - name: description
+ value: How to ensure that your app is secure when performing network transactions.
- title: Updating Your Security Provider to Protect Against SSL Exploits
path: /training/articles/security-gms-provider.html
- custom_link_attributes:
- - description="How to use and update Google Play services security provider, to protect against SSL exploits."
+ path_attributes:
+ - name: description
+ value: How to use and update Google Play services security provider, to protect against SSL exploits.
- title: Checking Device Compatibility with SafetyNet
path: /training/safetynet/index.html
- custom_link_attributes:
- - description="How to use the SafetyNet service to analyze a device where your app is running and get information about its compatibility with your app."
+ path_attributes:
+ - name: description
+ value: How to use the SafetyNet service to analyze a device where your app is running and get information about its compatibility with your app.
- title: Enhancing Security with Device Management Policies
path: /training/enterprise/device-management-policy.html
- custom_link_attributes:
- - description="How to create an application that enforces security policies on devices."
+ path_attributes:
+ - name: description
+ value: How to create an application that enforces security policies on devices.
- title: Best Practices for Permissions & Identifiers
path: /training/best-permissions-ids.html
section:
- title: Permissions and User Data
path: /training/articles/user-data-overview.html
- custom_link_attributes:
- - description="Overview of app permissions on Android and how they affect your users."
+ path_attributes:
+ - name: description
+ value: Overview of app permissions on Android and how they affect your users.
- title: Best Practices for App Permissions
path: /training/articles/user-data-permissions.html
- custom_link_attributes:
- - description="How to manage permissions the right way for users."
+ path_attributes:
+ - name: description
+ value: How to manage permissions the right way for users.
- title: Best Practices for Unique Identifiers
path: /training/articles/user-data-ids.html
- custom_link_attributes:
- - description="Unique identifiers available and how to choose the right one for your use case."
+ path_attributes:
+ - name: description
+ value: Unique identifiers available and how to choose the right one for your use case.
- title: Best Practices for Testing
path: /training/testing/index.html
section:
- title: Getting Started with Testing
path: /training/testing/start/index.html
- custom_link_attributes:
- - description="How to get started with testing your Android applications."
+ path_attributes:
+ - name: description
+ value: How to get started with testing your Android applications.
- title: Building Effective Unit Tests
path: /training/testing/unit-testing/index.html
- custom_link_attributes:
- - description="How to build effective unit tests for Android apps."
+ path_attributes:
+ - name: description
+ value: How to build effective unit tests for Android apps.
section:
- title: Building Local Unit Tests
path: /training/testing/unit-testing/local-unit-tests.html
@@ -1158,8 +1425,9 @@
path: /training/testing/unit-testing/instrumented-unit-tests.html
- title: Automating UI Tests
path: /training/testing/ui-testing/index.html
- custom_link_attributes:
- - description="How to automate your user interface tests for Android apps."
+ path_attributes:
+ - name: description
+ value: How to automate your user interface tests for Android apps.
section:
- title: Testing UI for a Single App
path: /training/testing/ui-testing/espresso-testing.html
@@ -1167,8 +1435,9 @@
path: /training/testing/ui-testing/uiautomator-testing.html
- title: Testing App Component Integrations
path: /training/testing/integration-testing/index.html
- custom_link_attributes:
- - description="How to build effective integration tests for Android apps."
+ path_attributes:
+ - name: description
+ value: How to build effective integration tests for Android apps.
section:
- title: Testing Your Service
path: /training/testing/integration-testing/service-testing.html
@@ -1176,16 +1445,18 @@
path: /training/testing/integration-testing/content-provider-testing.html
- title: Testing Display Performance
path: /training/testing/performance.html
- custom_link_attributes:
- - description="How to automate UI performance testing."
+ path_attributes:
+ - name: description
+ value: How to automate UI performance testing.
- title: Using Google Play to Distribute & Monetize
path: /training/distribute.html
section:
- title: Selling In-app Products
path: /training/in-app-billing/index.html
- custom_link_attributes:
- - description="How to sell in-app products from your application using In-app Billing."
+ path_attributes:
+ - name: description
+ value: How to sell in-app products from your application using In-app Billing.
section:
- title: Preparing Your App
path: /training/in-app-billing/preparing-iab-app.html
@@ -1197,8 +1468,9 @@
path: /training/in-app-billing/test-iab-app.html
- title: Maintaining Multiple APKs
path: /training/multiple-apks/index.html
- custom_link_attributes:
- - description="How to publish your app on Google Play with separate APKs that target different devices, while using a single app listing."
+ path_attributes:
+ - name: description
+ value: How to publish your app on Google Play with separate APKs that target different devices, while using a single app listing.
section:
- title: Creating Multiple APKs for Different API Levels
path: /training/multiple-apks/api.html
diff --git a/docs/image_sources/tools/support-library/appbar-kitkat-orig.png b/docs/image_sources/tools/support-library/appbar-kitkat-orig.png
new file mode 100644
index 0000000..ab680ce
--- /dev/null
+++ b/docs/image_sources/tools/support-library/appbar-kitkat-orig.png
Binary files differ
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 95c930c..29bf963 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -123,6 +123,9 @@
public static void request(@NonNull Surface source, @NonNull Bitmap dest,
@NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
validateBitmapDest(dest);
+ if (!source.isValid()) {
+ throw new IllegalArgumentException("Surface isn't valid, source.isValid() == false");
+ }
// TODO: Make this actually async and fast and cool and stuff
int result = ThreadedRenderer.copySurfaceInto(source, dest);
listenerThread.post(new Runnable() {
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index fdbe76a..0a8e3f3 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -471,18 +471,19 @@
return *this;
}
-GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture) {
+GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { &texture,
GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, GL_CLAMP_TO_EDGE,
- nullptr };
+ &textureTransform };
setFill(SK_ColorWHITE, 1.0f, SkXfermode::kSrc_Mode, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+ mDescription.hasTextureTransform = true;
return *this;
}
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index b6c186d..a9dd56f 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -73,7 +73,7 @@
// TODO: Texture should probably know and own its target.
// setFillLayer() forces it to GL_TEXTURE which isn't always correct.
// Similarly setFillLayer normally forces its own wrap & filter mode
- GlopBuilder& setFillExternalTexture(Texture& texture);
+ GlopBuilder& setFillExternalTexture(Texture& texture, Matrix4& textureTransform);
GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
return setTransform(*snapshot.transform, transformFlags);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 36007cd..9cde5d6 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -236,6 +236,8 @@
static const Matrix4& identity();
+ void invalidateType() { mType = kTypeUnknown; }
+
private:
mutable uint8_t mType;
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 49596e1..55f823d 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -101,7 +101,10 @@
// Setup the source
sp<GraphicBuffer> sourceBuffer;
sp<Fence> sourceFence;
- status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
+ Matrix4 texTransform;
+ status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
+ texTransform.data);
+ texTransform.invalidateType();
if (err != NO_ERROR) {
ALOGW("Failed to get last queued buffer, error = %d", err);
return CopyResult::UnknownError;
@@ -163,8 +166,8 @@
Glop glop;
GlopBuilder(renderState, caches, &glop)
.setRoundRectClipState(nullptr)
- .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
- .setFillExternalTexture(sourceTexture)
+ .setMeshTexturedUnitQuad(nullptr)
+ .setFillExternalTexture(sourceTexture, texTransform)
.setTransform(Matrix4::identity(), TransformFlags::None)
.setModelViewMapUnitToRect(destRect)
.build();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 05e12a1..a8c2652 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2688,20 +2688,20 @@
public static final int VP9Profile3HDR = 0x2000;
// from OMX_VIDEO_VP9LEVELTYPE
- public static final int VP9Level1 = 0x0;
- public static final int VP9Level11 = 0x1;
- public static final int VP9Level2 = 0x2;
- public static final int VP9Level21 = 0x4;
- public static final int VP9Level3 = 0x8;
- public static final int VP9Level31 = 0x10;
- public static final int VP9Level4 = 0x20;
- public static final int VP9Level41 = 0x40;
- public static final int VP9Level5 = 0x80;
- public static final int VP9Level51 = 0x100;
- public static final int VP9Level52 = 0x200;
- public static final int VP9Level6 = 0x400;
- public static final int VP9Level61 = 0x800;
- public static final int VP9Level62 = 0x1000;
+ public static final int VP9Level1 = 0x1;
+ public static final int VP9Level11 = 0x2;
+ public static final int VP9Level2 = 0x4;
+ public static final int VP9Level21 = 0x8;
+ public static final int VP9Level3 = 0x10;
+ public static final int VP9Level31 = 0x20;
+ public static final int VP9Level4 = 0x40;
+ public static final int VP9Level41 = 0x80;
+ public static final int VP9Level5 = 0x100;
+ public static final int VP9Level51 = 0x200;
+ public static final int VP9Level52 = 0x400;
+ public static final int VP9Level6 = 0x800;
+ public static final int VP9Level61 = 0x1000;
+ public static final int VP9Level62 = 0x2000;
// from OMX_VIDEO_HEVCPROFILETYPE
public static final int HEVCProfileMain = 0x01;
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1d894e1..22bb5f1 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -188,6 +188,9 @@
RECORDING_ERROR_RESOURCE_BUSY})
public @interface RecordingError {}
+ static final int RECORDING_ERROR_START = 0;
+ static final int RECORDING_ERROR_END = 2;
+
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: The requested operation cannot be
@@ -195,7 +198,7 @@
* for the problem is defined on the higher version than application's
* <code>android:targetSdkVersion</code>.
*/
- public static final int RECORDING_ERROR_UNKNOWN = 0;
+ public static final int RECORDING_ERROR_UNKNOWN = RECORDING_ERROR_START;
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
@@ -209,7 +212,7 @@
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
* a required recording resource was not able to be allocated.
*/
- public static final int RECORDING_ERROR_RESOURCE_BUSY = 2;
+ public static final int RECORDING_ERROR_RESOURCE_BUSY = RECORDING_ERROR_END;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 488b284..168fcdc 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1613,7 +1613,14 @@
* <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
* </ul>
*/
- public void notifyError(@TvInputManager.RecordingError final int error) {
+ public void notifyError(@TvInputManager.RecordingError int error) {
+ if (error < TvInputManager.RECORDING_ERROR_START
+ || error > TvInputManager.RECORDING_ERROR_END) {
+ Log.w(TAG, "notifyError - invalid error code (" + error
+ + ") is changed to RECORDING_ERROR_UNKNOWN.");
+ error = TvInputManager.RECORDING_ERROR_UNKNOWN;
+ }
+ final int validError = error;
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
@@ -1621,7 +1628,7 @@
try {
if (DEBUG) Log.d(TAG, "notifyError");
if (mSessionCallback != null) {
- mSessionCallback.onError(error);
+ mSessionCallback.onError(validError);
}
} catch (RemoteException e) {
Log.w(TAG, "error in notifyError", e);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 74c98e4..efa34ff 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -421,8 +421,6 @@
<string name="wifi_aggressive_handover">Aggressive Wi\u2011Fi to Cellular handover</string>
<!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
<string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string>
- <!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
- <string name="legacy_dhcp_client">Use legacy DHCP client</string>
<!-- Setting Checkbox title whether to always keep cellular data active. [CHAR LIMIT=80] -->
<string name="mobile_data_always_on">Cellular data always active</string>
<!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index c72f5d2..0962d84 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -158,6 +158,7 @@
private int mLastRequestedHeight = -1;
private AsyncTask<Void, Void, Bitmap> mLoader;
private boolean mNeedsDrawAfterLoadingWallpaper;
+ private boolean mSurfaceValid;
public DrawableEngine() {
super();
@@ -249,11 +250,6 @@
}
@Override
- public void onTouchEvent(MotionEvent event) {
- super.onTouchEvent(event);
- }
-
- @Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep,
int xPixels, int yPixels) {
@@ -288,13 +284,23 @@
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
+ if (DEBUG) {
+ Log.i(TAG, "onSurfaceDestroyed");
+ }
+
mLastSurfaceWidth = mLastSurfaceHeight = -1;
+ mSurfaceValid = false;
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
+ if (DEBUG) {
+ Log.i(TAG, "onSurfaceCreated");
+ }
+
mLastSurfaceWidth = mLastSurfaceHeight = -1;
+ mSurfaceValid = true;
}
@Override
@@ -314,6 +320,9 @@
}
void drawFrame() {
+ if (!mSurfaceValid) {
+ return;
+ }
try {
DisplayInfo displayInfo = getDefaultDisplayInfo();
int newRotation = displayInfo.rotation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 23e3561..f7ecd61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1985,19 +1985,29 @@
artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
}
}
+ boolean allowWhenShade = false;
if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap();
if (lockWallpaper != null) {
artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
mBackdropBack.getResources(), lockWallpaper);
+ // We're in the SHADE mode on the SIM screen - yet we still need to show
+ // the lockscreen wallpaper in that mode.
+ allowWhenShade = mStatusBarKeyguardViewManager != null
+ && mStatusBarKeyguardViewManager.isShowing();
}
}
+ boolean hideBecauseOccluded = mStatusBarKeyguardViewManager != null
+ && mStatusBarKeyguardViewManager.isOccluded();
+
final boolean hasArtwork = artworkDrawable != null;
- if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && mState != StatusBarState.SHADE
+ if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
+ && (mState != StatusBarState.SHADE || allowWhenShade)
&& mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
+ != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && !hideBecauseOccluded) {
// time to show some art!
if (mBackdrop.getVisibility() != View.VISIBLE) {
mBackdrop.setVisibility(View.VISIBLE);
@@ -2065,7 +2075,8 @@
Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
}
if (mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
+ == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ || hideBecauseOccluded) {
// We are unlocking directly - no animation!
mBackdrop.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 117e2b3..dcad75a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -247,6 +247,7 @@
}
}
mOccluded = occluded;
+ mPhoneStatusBar.updateMediaMetaData(false, false);
mStatusBarWindowManager.setKeyguardOccluded(occluded);
reset();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index c82ba3b..6800772 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -119,7 +119,7 @@
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_USER_STOPPING);
+ filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
null /* permission */, null /* scheduler */);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
index dd12360..fe5d8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -134,7 +134,8 @@
*/
public void requestFocus(boolean allowRecentsFocusable) {
mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
- if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent) {
+ if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent
+ || !mPipManager.isPipShown()) {
return;
}
mIsPipFocusedInRecent = true;
@@ -153,7 +154,8 @@
* This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
*/
public void clearFocus() {
- if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent) {
+ if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent
+ || !mPipManager.isPipShown()) {
return;
}
if (!mRecentsView.hasFocus()) {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f49235c..c417fe8 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2187,6 +2187,10 @@
// reinflation.
ACTION_REMOTE_INPUT_CLOSE = 400;
+ // OPEN: Settings > Accounts > Work profile settings
+ // CATEGORY: SETTINGS
+ ACCOUNTS_WORK_PROFILE_SETTINGS = 401;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fc2e95d..ae31438 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1245,8 +1245,8 @@
private void updateServicesLocked(UserState userState) {
Map<ComponentName, Service> componentNameToServiceMap =
userState.mComponentNameToServiceMap;
- boolean isUnlocked = mContext.getSystemService(UserManager.class)
- .isUserUnlocked(userState.mUserId);
+ boolean isUnlockingOrUnlocked = mContext.getSystemService(UserManager.class)
+ .isUserUnlockingOrUnlocked(userState.mUserId);
for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
@@ -1256,7 +1256,7 @@
Service service = componentNameToServiceMap.get(componentName);
// Ignore non-encryption-aware services until user is unlocked
- if (!isUnlocked && !installedService.isDirectBootAware()) {
+ if (!isUnlockingOrUnlocked && !installedService.isDirectBootAware()) {
Slog.d(LOG_TAG, "Ignoring non-encryption-aware service " + componentName);
continue;
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 0428ecf..7b9d4456 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -335,7 +335,7 @@
Provider provider = installedProviders.get(i);
final int userId = provider.getUserId();
- if (!mUserManager.isUserUnlocked(userId) ||
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
isProfileWithLockedParent(userId)) {
continue;
}
@@ -369,7 +369,7 @@
}
private void onPackageBroadcastReceived(Intent intent, int userId) {
- if (!mUserManager.isUserUnlocked(userId) ||
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
isProfileWithLockedParent(userId)) {
return;
}
@@ -455,7 +455,7 @@
* userId must be the group parent.
*/
private void reloadWidgetsMaskedStateForGroup(int userId) {
- if (!mUserManager.isUserUnlocked(userId)) {
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
return;
}
synchronized (mLock) {
@@ -472,7 +472,7 @@
try {
UserInfo user = mUserManager.getUserInfo(userId);
- boolean lockedProfile = !mUserManager.isUserUnlocked(userId);
+ boolean lockedProfile = !mUserManager.isUserUnlockingOrUnlocked(userId);
boolean quietProfile = user.isQuietModeEnabled();
final int N = mProviders.size();
for (int i = 0; i < N; i++) {
@@ -656,7 +656,7 @@
}
private void ensureGroupStateLoadedLocked(int userId) {
- if (!mUserManager.isUserUnlocked(userId)) {
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
throw new IllegalStateException(
"User " + userId + " must be unlocked for widgets to be available");
}
@@ -3363,7 +3363,7 @@
if (userInfo != null && userInfo.isManagedProfile()) {
UserInfo parentInfo = mUserManager.getProfileParent(userId);
if (parentInfo != null
- && !mUserManager.isUserUnlocked(parentInfo.getUserHandle())) {
+ && !mUserManager.isUserUnlockingOrUnlocked(parentInfo.getUserHandle())) {
return true;
}
}
@@ -3378,7 +3378,7 @@
if (userInfo != null && userInfo.isManagedProfile()) {
UserInfo parentInfo = mUserManager.getProfileParent(userId);
if (parentInfo != null
- && mUserManager.isUserUnlocked(parentInfo.getUserHandle())) {
+ && mUserManager.isUserUnlockingOrUnlocked(parentInfo.getUserHandle())) {
return true;
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0287332..d85827e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -730,7 +730,7 @@
//set up the listener for user state for creating user VPNs
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTING);
- intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+ intentFilter.addAction(Intent.ACTION_USER_STOPPED);
intentFilter.addAction(Intent.ACTION_USER_ADDED);
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
@@ -3619,7 +3619,7 @@
synchronized(mVpns) {
Vpn userVpn = mVpns.get(userId);
if (userVpn == null) {
- loge("Stopping user has no VPN");
+ loge("Stopped user has no VPN");
return;
}
mVpns.delete(userId);
@@ -3664,7 +3664,7 @@
if (Intent.ACTION_USER_STARTING.equals(action)) {
onUserStart(userId);
- } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
onUserStop(userId);
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
onUserAdded(userId);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 79d16da..a584f70 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1020,7 +1020,7 @@
// If the system is not ready or the device is not yed unlocked by the user, then we use
// copy-on-write settings.
final boolean useCopyOnWriteSettings =
- !mSystemReady || !mUserManager.isUserUnlocked(newUserId);
+ !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId);
mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
updateCurrentProfileIds();
// InputMethodFileManager should be reset when the user is changed
@@ -1077,7 +1077,7 @@
mSystemReady = true;
final int currentUserId = mSettings.getCurrentUserId();
mSettings.switchCurrentUser(currentUserId,
- !mUserManager.isUserUnlocked(currentUserId));
+ !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mStatusBar = statusBar;
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a3ef6b6..cbf7e80 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -256,13 +256,13 @@
for (int i = 0; i < users.size(); i++) {
UserInfo user = users.get(i);
UserHandle userHandle = user.getUserHandle();
- if (!mUserManager.isUserUnlocked(userHandle)) {
+ if (!mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
if (!user.isManagedProfile()) {
showEncryptionNotification(userHandle);
} else {
UserInfo parent = mUserManager.getProfileParent(user.id);
if (parent != null &&
- mUserManager.isUserUnlocked(parent.getUserHandle()) &&
+ mUserManager.isUserUnlockingOrUnlocked(parent.getUserHandle()) &&
!mUserManager.isQuietModeEnabled(userHandle)) {
// Only show notifications for managed profiles once their parent
// user is unlocked.
@@ -350,7 +350,7 @@
UserInfo profile = profiles.get(i);
if (profile.isManagedProfile()) {
UserHandle userHandle = profile.getUserHandle();
- if (!mUserManager.isUserUnlocked(userHandle) &&
+ if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) &&
!mUserManager.isQuietModeEnabled(userHandle)) {
showEncryptionNotificationForProfile(userHandle);
}
@@ -1281,8 +1281,14 @@
// service can't connect to vold, it restarts, and then the new instance
// does successfully connect.
final IMountService service = getMountService();
- String password = service.getPassword();
- service.clearPassword();
+ String password;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ password = service.getPassword();
+ service.clearPassword();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
if (password == null) {
return false;
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index ec05dae..7253870 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2648,6 +2648,8 @@
*/
@Override
public int getPasswordType() {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+ "no permission to access the crypt keeper");
waitForReady();
@@ -2672,6 +2674,8 @@
*/
@Override
public void setField(String field, String contents) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+ "no permission to access the crypt keeper");
waitForReady();
@@ -2690,6 +2694,8 @@
*/
@Override
public String getField(String field) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+ "no permission to access the crypt keeper");
waitForReady();
@@ -2714,6 +2720,8 @@
*/
@Override
public boolean isConvertibleToFBE() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+ "no permission to access the crypt keeper");
waitForReady();
@@ -2728,8 +2736,9 @@
@Override
public String getPassword() throws RemoteException {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
"only keyguard can retrieve password");
+
if (!isReady()) {
return new String();
}
@@ -2752,6 +2761,9 @@
@Override
public void clearPassword() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+ "only keyguard can clear password");
+
if (!isReady()) {
return;
}
@@ -2816,17 +2828,19 @@
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
- // When a user has secure lock screen, require a challenge token to
- // actually unlock. This check is mostly in place for emulation mode.
- if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
- throw new IllegalStateException("Token required to unlock secure user " + userId);
- }
+ if (StorageManager.isFileEncryptedNativeOrEmulated()) {
+ // When a user has secure lock screen, require a challenge token to
+ // actually unlock. This check is mostly in place for emulation mode.
+ if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
+ throw new IllegalStateException("Token required to unlock secure user " + userId);
+ }
- try {
- mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
- encodeBytes(token), encodeBytes(secret));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ try {
+ mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
+ encodeBytes(token), encodeBytes(secret));
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
}
synchronized (mLock) {
@@ -2852,12 +2866,8 @@
@Override
public boolean isUserKeyUnlocked(int userId) {
- if (StorageManager.isFileEncryptedNativeOrEmulated()) {
- synchronized (mLock) {
- return ArrayUtils.contains(mLocalUnlockedUsers, userId);
- }
- } else {
- return true;
+ synchronized (mLock) {
+ return ArrayUtils.contains(mLocalUnlockedUsers, userId);
}
}
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index a628747..4b0d4be 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -166,7 +166,7 @@
mMonitor = new TextServicesMonitor();
mMonitor.register(context, null, true);
final boolean useCopyOnWriteSettings =
- !mSystemReady || !mUserManager.isUserUnlocked(userId);
+ !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(userId);
mSettings = new TextServicesSettings(context.getContentResolver(), userId,
useCopyOnWriteSettings);
@@ -176,7 +176,7 @@
private void resetInternalState(@UserIdInt int userId) {
final boolean useCopyOnWriteSettings =
- !mSystemReady || !mUserManager.isUserUnlocked(userId);
+ !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(userId);
mSettings.switchCurrentUser(userId, useCopyOnWriteSettings);
updateCurrentProfileIds();
unbindServiceLocked();
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 480da72..96214d6 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -304,7 +304,7 @@
}
private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
- private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
+ private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
@@ -424,7 +424,7 @@
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "validateAccountsInternal " + accounts.userId
+ " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
- + " userLocked=" + mUnlockedUsers.get(accounts.userId));
+ + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
}
if (invalidateAuthenticatorCache) {
mAuthenticatorCache.invalidateCache(accounts.userId);
@@ -575,7 +575,7 @@
validateAccounts = true;
}
// open CE database if necessary
- if (!accounts.openHelper.isCeDatabaseAttached() && mUnlockedUsers.get(userId)) {
+ if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
synchronized (accounts.cacheLock) {
File preNDatabaseFile = new File(getPreNDatabaseName(userId));
@@ -648,8 +648,8 @@
synchronized (mUsers) {
accounts = mUsers.get(userId);
mUsers.remove(userId);
- userUnlocked = mUnlockedUsers.get(userId);
- mUnlockedUsers.delete(userId);
+ userUnlocked = mLocalUnlockedUsers.get(userId);
+ mLocalUnlockedUsers.delete(userId);
}
if (accounts != null) {
synchronized (accounts.cacheLock) {
@@ -686,7 +686,7 @@
Log.v(TAG, "onUserUnlocked " + userId);
}
synchronized (mUsers) {
- mUnlockedUsers.put(userId, true);
+ mLocalUnlockedUsers.put(userId, true);
}
if (userId < 1) return;
syncSharedAccounts(userId);
@@ -746,7 +746,7 @@
if (account == null) {
return null;
}
- if (!isUserUnlocked(accounts.userId)) {
+ if (!isLocalUnlockedUser(accounts.userId)) {
Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
return null;
}
@@ -828,7 +828,7 @@
account.type);
throw new SecurityException(msg);
}
- if (!isUserUnlocked(userId)) {
+ if (!isLocalUnlockedUser(userId)) {
Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
return null;
}
@@ -1109,7 +1109,7 @@
if (account == null) {
return false;
}
- if (!isUserUnlocked(accounts.userId)) {
+ if (!isLocalUnlockedUser(accounts.userId)) {
Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
+ " is locked. callingUid=" + callingUid);
return false;
@@ -1176,9 +1176,9 @@
return true;
}
- private boolean isUserUnlocked(int userId) {
+ private boolean isLocalUnlockedUser(int userId) {
synchronized (mUsers) {
- return mUnlockedUsers.get(userId);
+ return mLocalUnlockedUsers.get(userId);
}
}
@@ -1193,7 +1193,7 @@
for (UserInfo user : users) {
if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
addSharedAccountAsUser(account, user.id);
- if (getUserManager().isUserUnlocked(user.id)) {
+ if (isLocalUnlockedUser(user.id)) {
mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
}
@@ -1597,7 +1597,7 @@
private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
int deleted;
- boolean userUnlocked = isUserUnlocked(accounts.userId);
+ boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
if (!userUnlocked) {
Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
+ " is still locked. CE data will be removed later");
@@ -1792,7 +1792,7 @@
account.type);
throw new SecurityException(msg);
}
- if (!isUserUnlocked(userId)) {
+ if (!isLocalUnlockedUser(userId)) {
Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
+ callingUid);
return null;
@@ -4037,8 +4037,7 @@
return false;
}
- final ActivityManager am = mContext.getSystemService(ActivityManager.class);
- if (am.isUserRunningAndLocked(mAccounts.userId)
+ if (!isLocalUnlockedUser(mAccounts.userId)
&& !authenticatorInfo.componentInfo.directBootAware) {
Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
+ " which isn't encryption aware");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d7ddc7..ddb9b8a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1946,9 +1946,7 @@
startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
}
installEncryptionUnawareProviders(userId);
- if (msg.obj instanceof ProgressReporter) {
- ((ProgressReporter) msg.obj).finish();
- }
+ mUserController.finishUserUnlocked((UserState) msg.obj);
break;
}
case SYSTEM_USER_CURRENT_MSG: {
@@ -5949,6 +5947,15 @@
}
+ final boolean clearBroadcastQueueForUserLocked(int userId) {
+ boolean didSomething = false;
+ for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
+ didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
+ null, null, userId, true);
+ }
+ return didSomething;
+ }
+
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, int userId, String reason) {
@@ -6304,7 +6311,11 @@
app.debugging = false;
app.cached = false;
app.killedByAm = false;
- app.unlocked = mContext.getSystemService(UserManager.class).isUserUnlocked(app.userId);
+
+ // We carefully use the same state that PackageManager uses for
+ // filtering, since we use this flag to decide if we need to install
+ // providers when user is unlocked later
+ app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
@@ -11024,14 +11035,6 @@
* belonging to any running apps.
*/
private void installEncryptionUnawareProviders(int userId) {
- if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
- // TODO: eventually pivot this back to look at current user state,
- // similar to the comment in UserManager.isUserUnlocked(), but for
- // now, if we started apps when "unlocked" then unaware providers
- // have already been spun up.
- return;
- }
-
// We're only interested in providers that are encryption unaware, and
// we don't care about uninstalled apps, since there's no way they're
// running at this point.
@@ -17178,6 +17181,7 @@
if (isProtectedBroadcast
|| Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
|| Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
+ || Intent.ACTION_MEDIA_BUTTON.equals(action)
|| Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
|| Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 94e442f..33b87b6 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -685,8 +685,8 @@
Binder.restoreCallingIdentity(token);
}
if (parent != null
- && userManager.isUserUnlocked(parent.getUserHandle())
- && !userManager.isUserUnlocked(userInfo.getUserHandle())) {
+ && userManager.isUserUnlockingOrUnlocked(parent.getUserHandle())
+ && !userManager.isUserUnlockingOrUnlocked(userInfo.getUserHandle())) {
rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index cdb68d8..bfa5b42 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -270,7 +270,7 @@
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
// Only keep marching forward if user is actually unlocked
- if (!isUserKeyUnlocked(userId)) return;
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
uss.mUnlockProgress.start();
@@ -281,9 +281,29 @@
mUserManager.onBeforeUnlockUser(userId);
uss.mUnlockProgress.setProgress(20);
- // Dispatch unlocked to system services
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss.mUnlockProgress)
+ // Dispatch unlocked to system services; when fully dispatched,
+ // that calls through to the next "unlocked" phase
+ mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
+ * {@link UserState#STATE_RUNNING_UNLOCKED}.
+ */
+ void finishUserUnlocked(final UserState uss) {
+ final int userId = uss.mHandle.getIdentifier();
+ synchronized (mService) {
+ // Bail if we ended up with a stale user
+ if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
+
+ // Only keep marching forward if user is actually unlocked
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
+
+ if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
+ uss.mUnlockProgress.finish();
// Dispatch unlocked to external apps
final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
@@ -310,27 +330,25 @@
}
}
- // Send PRE_BOOT broadcasts if fingerprint changed
+ // Send PRE_BOOT broadcasts if user fingerprint changed; we
+ // purposefully block sending BOOT_COMPLETED until after all
+ // PRE_BOOT receivers are finished to avoid ANR'ing apps
final UserInfo info = getUserInfo(userId);
if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
new PreBootBroadcaster(mService, userId, null) {
@Override
public void onFinished() {
- finishUserUnlocked(uss);
+ finishUserUnlockedCompleted(uss);
}
}.sendNext();
} else {
- finishUserUnlocked(uss);
+ finishUserUnlockedCompleted(uss);
}
}
}
}
- /**
- * Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
- * {@link UserState#STATE_RUNNING_UNLOCKED}.
- */
- private void finishUserUnlocked(UserState uss) {
+ private void finishUserUnlockedCompleted(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
synchronized (mService) {
// Bail if we ended up with a stale user
@@ -341,39 +359,38 @@
}
// Only keep marching forward if user is actually unlocked
- if (!isUserKeyUnlocked(userId)) return;
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
- if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
- // Remember that we logged in
- mUserManager.onUserLoggedIn(userId);
+ // Remember that we logged in
+ mUserManager.onUserLoggedIn(userId);
- if (!userInfo.isInitialized()) {
- if (userId != UserHandle.USER_SYSTEM) {
- Slog.d(TAG, "Initializing user #" + userId);
- Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mService.broadcastIntentLocked(null, null, intent, null,
- new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- // Note: performReceive is called with mService lock held
- getUserManager().makeInitialized(userInfo.id);
- }
- }, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, userId);
- }
+ if (!userInfo.isInitialized()) {
+ if (userId != UserHandle.USER_SYSTEM) {
+ Slog.d(TAG, "Initializing user #" + userId);
+ Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, intent, null,
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ // Note: performReceive is called with mService lock held
+ getUserManager().makeInitialized(userInfo.id);
+ }
+ }, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, userId);
}
- Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
- final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
- bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mService.broadcastIntentLocked(null, null, bootIntent, null, null, 0, null, null,
- new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
+
+ Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
+ final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mService.broadcastIntentLocked(null, null, bootIntent, null, null, 0, null, null,
+ new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
}
@@ -483,6 +500,8 @@
});
}
};
+ // Clear broadcast queue for the user to avoid delivering stale broadcasts
+ mService.clearBroadcastQueueForUserLocked(userId);
// Kick things off.
mService.broadcastIntentLocked(null, null, stoppingIntent,
null, stoppingReceiver, 0, null, null,
@@ -680,20 +699,6 @@
return IMountService.Stub.asInterface(ServiceManager.getService("mount"));
}
- private boolean isUserKeyUnlocked(int userId) {
- final IMountService mountService = getMountService();
- if (mountService != null) {
- try {
- return mountService.isUserKeyUnlocked(userId);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- } else {
- Slog.w(TAG, "Mount service not published; guessing locked state based on property");
- return !StorageManager.isFileEncryptedNativeOrEmulated();
- }
- }
-
/**
* Start user, if its not already running.
* <p>The user will be brought to the foreground, if {@code foreground} parameter is set.
@@ -935,7 +940,7 @@
}
}
- if (!isUserKeyUnlocked(userId)) {
+ if (!StorageManager.isUserKeyUnlocked(userId)) {
final UserInfo userInfo = getUserInfo(userId);
final IMountService mountService = getMountService();
try {
@@ -1321,30 +1326,31 @@
if ((flags & ActivityManager.FLAG_OR_STOPPED) != 0) {
return true;
}
-
- final boolean unlocked;
- switch (state.state) {
- case UserState.STATE_STOPPING:
- case UserState.STATE_SHUTDOWN:
- default:
- return false;
-
- case UserState.STATE_BOOTING:
- case UserState.STATE_RUNNING_LOCKED:
- unlocked = false;
- break;
-
- case UserState.STATE_RUNNING_UNLOCKING:
- case UserState.STATE_RUNNING_UNLOCKED:
- unlocked = true;
- break;
- }
-
if ((flags & ActivityManager.FLAG_AND_LOCKED) != 0) {
- return !unlocked;
+ switch (state.state) {
+ case UserState.STATE_BOOTING:
+ case UserState.STATE_RUNNING_LOCKED:
+ return true;
+ default:
+ return false;
+ }
+ }
+ if ((flags & ActivityManager.FLAG_AND_UNLOCKING_OR_UNLOCKED) != 0) {
+ switch (state.state) {
+ case UserState.STATE_RUNNING_UNLOCKING:
+ case UserState.STATE_RUNNING_UNLOCKED:
+ return true;
+ default:
+ return false;
+ }
}
if ((flags & ActivityManager.FLAG_AND_UNLOCKED) != 0) {
- return unlocked;
+ switch (state.state) {
+ case UserState.STATE_RUNNING_UNLOCKED:
+ return true;
+ default:
+ return false;
+ }
}
// One way or another, we're running!
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index fcf2162..493fc4a 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -396,8 +396,8 @@
onUserRemoved(userId);
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
onUserUnlocked(userId);
- } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
- onUserStopping(userId);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ onUserStopped(userId);
}
}
};
@@ -550,7 +550,7 @@
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
- intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+ intentFilter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -1422,7 +1422,7 @@
}
}
- private void onUserStopping(int userId) {
+ private void onUserStopped(int userId) {
updateRunningAccounts(null /* Don't sync any target */);
cancelActiveSync(
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
index b636ce5..6a533c9 100644
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnrollClient.java
@@ -51,7 +51,8 @@
" getGroupId():" + getGroupId());
}
if (remaining == 0) {
- FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId, groupId);
+ FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
+ getTargetUserId());
}
return sendEnrollResult(fingerId, groupId, remaining);
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 9a2db8e..ae01635 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -540,13 +540,15 @@
startClient(client, true /* initiatedByClient */);
}
- private void startEnrollment(IBinder token, byte [] cryptoToken, int callingUserId, int groupId,
+ private void startEnrollment(IBinder token, byte [] cryptoToken, int userId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
String opPackageName) {
- updateActiveGroup(groupId, opPackageName);
+ updateActiveGroup(userId, opPackageName);
+
+ final int groupId = userId; // default group for fingerprint enrollment
EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
- callingUserId, groupId, cryptoToken, restricted, opPackageName) {
+ userId, groupId, cryptoToken, restricted, opPackageName) {
@Override
public IFingerprintDaemon getFingerprintDaemon() {
@@ -680,15 +682,14 @@
}
@Override // Binder call
- public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
+ public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
checkPermission(MANAGE_FINGERPRINT);
final int limit = mContext.getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
- final int callingUserId = UserHandle.getCallingUserId();
- final int enrolled = FingerprintService.this.
- getEnrolledFingerprints(callingUserId).size();
+
+ final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
if (enrolled >= limit) {
Slog.w(TAG, "Too many fingerprints registered");
return;
@@ -696,7 +697,7 @@
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default fingerprints for the user.
- if (!isCurrentUserOrProfile(groupId)) {
+ if (!isCurrentUserOrProfile(userId)) {
return;
}
@@ -704,7 +705,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- startEnrollment(token, cryptoToken, callingUserId, groupId, receiver, flags,
+ startEnrollment(token, cryptoToken, userId, receiver, flags,
restricted, opPackageName);
}
});
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0b1ece5..30aab8c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -465,7 +465,7 @@
continue;
}
for (int j = 0; j < numberUsers; j++) {
- final UserInfo user = users.get(i);
+ final UserInfo user = users.get(j);
final int uid = UserHandle.getUid(user.id, app.uid);
mDefaultRestrictBackgroundWhitelistUids.append(uid, true);
if (LOGD) Slog.d(TAG, "revoked whistelist status for uid " + uid + ": "
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89dc4fb..ba651fc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2674,14 +2674,8 @@
// Prepare storage for system user really early during boot,
// since core system apps like SettingsProvider and SystemUI
// can't wait for user to start
- final int storageFlags;
- if (StorageManager.isFileEncryptedNativeOrEmulated()) {
- storageFlags = StorageManager.FLAG_STORAGE_DE;
- } else {
- storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
- }
reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM,
- storageFlags);
+ StorageManager.FLAG_STORAGE_DE);
// If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
@@ -3112,7 +3106,7 @@
@Override
public void checkPackageStartable(String packageName, int userId) {
- final boolean userKeyUnlocked = isUserKeyUnlocked(userId);
+ final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -3463,30 +3457,6 @@
}
/**
- * Return if the user key is currently unlocked.
- */
- private boolean isUserKeyUnlocked(int userId) {
- if (StorageManager.isFileEncryptedNativeOrEmulated()) {
- final IMountService mount = IMountService.Stub
- .asInterface(ServiceManager.getService("mount"));
- if (mount == null) {
- Slog.w(TAG, "Early during boot, assuming locked");
- return false;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- return mount.isUserKeyUnlocked(userId);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- } else {
- return true;
- }
- }
-
- /**
* Update given flags based on encryption status of current user.
*/
private int updateFlags(int flags, int userId) {
@@ -3497,7 +3467,7 @@
// give them what they want
} else {
// Caller expressed no opinion, so match based on user state
- if (isUserKeyUnlocked(userId)) {
+ if (StorageManager.isUserKeyUnlocked(userId)) {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
} else {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -16106,7 +16076,7 @@
final UserManager um = mContext.getSystemService(UserManager.class);
final int flags;
- if (um.isUserUnlocked(userId)) {
+ if (um.isUserUnlockingOrUnlocked(userId)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (um.isUserRunning(userId)) {
flags = StorageManager.FLAG_STORAGE_DE;
@@ -18790,7 +18760,7 @@
final UserManager um = mContext.getSystemService(UserManager.class);
for (UserInfo user : um.getUsers()) {
final int flags;
- if (um.isUserUnlocked(user.id)) {
+ if (um.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (um.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
@@ -19104,7 +19074,7 @@
// First look for stale data that doesn't belong, and check if things
// have changed since we did our last restorecon
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
- if (!isUserKeyUnlocked(userId)) {
+ if (!StorageManager.isUserKeyUnlocked(userId)) {
throw new RuntimeException(
"Yikes, someone asked us to reconcile CE storage while " + userId
+ " was still locked; this would have caused massive data loss!");
@@ -19212,7 +19182,7 @@
final UserManager um = mContext.getSystemService(UserManager.class);
for (UserInfo user : um.getUsers()) {
final int flags;
- if (um.isUserUnlocked(user.id)) {
+ if (um.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (um.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5764161..c0874ef 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1561,48 +1561,46 @@
// === House keeping ===
- @VisibleForTesting
- void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
- cleanUpPackageLocked(packageName, owningUserId, packageUserId,
- /* forceForCommandLine= */ false);
+ private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId) {
+ synchronized (mLock) {
+ forEachLoadedUserLocked(user ->
+ cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
+ }
}
/**
* Remove all the information associated with a package. This will really remove all the
* information, including the restore information (i.e. it'll remove packages even if they're
* shadow).
+ *
+ * This is called when an app is uninstalled, or an app gets "clear data"ed.
*/
- private void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
- boolean forceForCommandLine) {
- if (!forceForCommandLine && isPackageInstalled(packageName, packageUserId)) {
- wtf("Package " + packageName + " is still installed for user " + packageUserId);
- return;
- }
-
+ @VisibleForTesting
+ void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
- final ShortcutUser mUser = getUserShortcutsLocked(owningUserId);
+ final ShortcutUser user = getUserShortcutsLocked(owningUserId);
boolean doNotify = false;
// First, remove the package from the package list (if the package is a publisher).
if (packageUserId == owningUserId) {
- if (mUser.removePackage(this, packageName) != null) {
+ if (user.removePackage(this, packageName) != null) {
doNotify = true;
}
}
// Also remove from the launcher list (if the package is a launcher).
- mUser.removeLauncher(packageUserId, packageName);
+ user.removeLauncher(packageUserId, packageName);
// Then remove pinned shortcuts from all launchers.
- final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers();
+ final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = user.getAllLaunchers();
for (int i = launchers.size() - 1; i >= 0; i--) {
launchers.valueAt(i).cleanUpPackage(packageName, packageUserId);
}
// Now there may be orphan shortcuts because we removed pinned shortucts at the previous
// step. Remove them too.
- for (int i = mUser.getAllPackages().size() - 1; i >= 0; i--) {
- mUser.getAllPackages().valueAt(i).refreshPinnedFlags(this);
+ for (int i = user.getAllPackages().size() - 1; i >= 0; i--) {
+ user.getAllPackages().valueAt(i).refreshPinnedFlags(this);
}
scheduleSaveUser(owningUserId);
@@ -1842,6 +1840,11 @@
public void onPackageRemoved(String packageName, int uid) {
handlePackageRemoved(packageName, getChangingUserId());
}
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ handlePackageDataCleared(packageName, getChangingUserId());
+ }
};
/**
@@ -1915,16 +1918,15 @@
Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
packageUserId));
}
- handlePackageRemovedInner(packageName, packageUserId, /* forceForCommandLine =*/ false);
+ cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
}
- private void handlePackageRemovedInner(String packageName, @UserIdInt int packageUserId,
- boolean forceForCommandLine) {
- synchronized (mLock) {
- forEachLoadedUserLocked(user ->
- cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
- forceForCommandLine));
+ private void handlePackageDataCleared(String packageName, int packageUserId) {
+ if (DEBUG) {
+ Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
+ packageUserId));
}
+ cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
}
// === PackageManager interaction ===
@@ -2390,8 +2392,7 @@
Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
- ShortcutService.this.handlePackageRemovedInner(packageName, mUserId,
- /* forceForCommandLine= */ true);
+ ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a085370..a2d859b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -692,7 +692,7 @@
@Override
public boolean trySetQuietModeDisabled(int userHandle, IntentSender target) {
final int credentialOwnerUserId = getCredentialOwnerProfile(userHandle);
- if (mContext.getSystemService(StorageManager.class).isUserKeyUnlocked(userHandle)
+ if (StorageManager.isUserKeyUnlocked(userHandle)
|| !mLockPatternUtils.isSecure(credentialOwnerUserId)) {
// if the user is already unlocked, no need to show a profile challenge
setQuietModeEnabled(userHandle, false);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 827ea5b..2636d09 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5159,7 +5159,6 @@
mKeyguardOccluded = false;
mKeyguardDelegate.setOccluded(false);
mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
- mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
return true;
} else if (!wasOccluded && isOccluded && showing) {
mKeyguardOccluded = true;
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index e3e1097..4d91814 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -112,7 +112,7 @@
if (um.getUserInfo(userId) == null) {
throw new IllegalStateException("User " + userId + " doesn't exist");
}
- if (!um.isUserUnlocked(userId)) {
+ if (!um.isUserUnlockingOrUnlocked(userId)) {
throw new IllegalStateException("User " + userId + " isn't unlocked");
}
} finally {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d6ace91..af7e66d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -862,7 +862,6 @@
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
- userFilter.addAction(Intent.ACTION_USER_STOPPING);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -871,12 +870,6 @@
onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_NULL));
}
- // TODO: Race condition causing problems when cleaning up on stopping a user.
- // Comment this out for now.
- // else if (Intent.ACTION_USER_STOPPING.equals(action)) {
- // onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- // UserHandle.USER_NULL));
- // }
}
}, userFilter);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3b1f34a..b4ead44 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -390,8 +390,8 @@
}
boolean hasSavedSurface() {
- for (int i = windows.size() -1; i >= 0; i--) {
- final WindowState ws = windows.get(i);
+ for (int i = allAppWindows.size() -1; i >= 0; i--) {
+ final WindowState ws = allAppWindows.get(i);
if (ws.hasSavedSurface()) {
return true;
}
@@ -408,8 +408,8 @@
// Check if we have enough drawn windows to mark allDrawn= true.
int numInteresting = 0;
int numDrawn = 0;
- for (int i = windows.size() - 1; i >= 0; i--) {
- WindowState w = windows.get(i);
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ WindowState w = allAppWindows.get(i);
if (w.hasSavedSurface()) {
w.restoreSavedSurface();
}
@@ -429,8 +429,8 @@
}
void destroySavedSurfaces() {
- for (int i = windows.size() - 1; i >= 0; i--) {
- WindowState win = windows.get(i);
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ WindowState win = allAppWindows.get(i);
win.destroySavedSurface();
}
mAnimatingWithSavedSurface = false;
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index debb382..220a6de 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -24,10 +24,12 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Rect;
+import android.os.IBinder;
import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.animation.LinearInterpolator;
+import android.view.WindowManagerInternal;
/**
* Enables animating bounds of objects.
@@ -49,6 +51,32 @@
// Only accessed on UI thread.
private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
+ private final WindowManagerInternal.AppTransitionListener mAppTransitionNotifier
+ = new WindowManagerInternal.AppTransitionListener() {
+ public void onAppTransitionCancelledLocked() {
+ animationFinished();
+ }
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ animationFinished();
+ }
+ private void animationFinished() {
+ if (mFinishAnimationAfterTransition) {
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ BoundsAnimator b = mRunningAnimations.valueAt(i);
+ b.onAnimationEnd(null);
+ }
+ }
+ }
+ };
+
+ private final AppTransition mAppTransition;
+ private boolean mFinishAnimationAfterTransition = false;
+
+ BoundsAnimationController(AppTransition transition) {
+ mAppTransition = transition;
+ mAppTransition.registerListenerLocked(mAppTransitionNotifier);
+ }
+
private final class BoundsAnimator extends ValueAnimator
implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
private final AnimateBoundsUser mTarget;
@@ -129,6 +157,7 @@
public void onAnimationStart(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
+ " mReplacement=" + mReplacement);
+ mFinishAnimationAfterTransition = false;
// Ensure that we have prepared the target for animation before
// we trigger any size changes, so it can swap surfaces
// in to appropriate modes, or do as it wishes otherwise.
@@ -150,10 +179,21 @@
if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
+ " mMoveToFullScreen=" + mMoveToFullScreen + " mWillReplace=" + mWillReplace);
- finishAnimation();
+ // There could be another animation running. For example in the
+ // move to fullscreen case, recents will also be closing while the
+ // previous task will be taking its place in the fullscreen stack.
+ // we have to ensure this is completed before we finish the animation
+ // and take our place in the fullscreen stack.
+ if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) {
+ mFinishAnimationAfterTransition = true;
+ return;
+ }
+
if (mMoveToFullScreen && !mWillReplace) {
mTarget.moveToFullscreen();
}
+
+ finishAnimation();
}
@Override
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8174c13..c640b2a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -377,7 +377,7 @@
// If the app that having visibility change is not the top visible one in the task,
// it does not affect whether the docked stack is minimized, ignore it.
- if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) {
+ if (task.getTopAppToken() == null || task.getTopAppToken() != wtoken) {
return;
}
@@ -598,7 +598,7 @@
}
private boolean animateForMinimizedDockedStack(long now) {
- final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
+ final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6925fa5..8d41dab 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -719,6 +719,10 @@
return null;
}
+ AppWindowToken getTopAppToken() {
+ return mAppTokens.size() > 0 ? mAppTokens.get(mAppTokens.size() - 1) : null;
+ }
+
@Override
public boolean isFullscreen() {
if (useCurrentBounds()) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7c1d2d7..4224c57 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1098,6 +1098,14 @@
pw.println(prefix + "mDeferDetach=" + mDeferDetach);
pw.println(prefix + "mFullscreen=" + mFullscreen);
pw.println(prefix + "mBounds=" + mBounds.toShortString());
+ if (mMinimizeAmount != 0f) {
+ pw.println(prefix + "mMinimizeAmout=" + mMinimizeAmount);
+ }
+ if (mAdjustedForIme) {
+ pw.println(prefix + "mAdjustedForIme=true");
+ pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
+ pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
+ }
if (!mAdjustedBounds.isEmpty()) {
pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1bd8c32..57f551c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -715,8 +715,7 @@
final WindowAnimator mAnimator;
- private final BoundsAnimationController mBoundsAnimationController =
- new BoundsAnimationController();
+ private final BoundsAnimationController mBoundsAnimationController;
SparseArray<Task> mTaskIdToTask = new SparseArray<>();
@@ -974,6 +973,8 @@
mAppTransition = new AppTransition(context, this);
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
+ mBoundsAnimationController = new BoundsAnimationController(mAppTransition);
+
mActivityManager = ActivityManagerNative.getDefault();
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
@@ -4334,15 +4335,21 @@
}
}
- if (visibilityChanged && !delayed) {
- if (visible) {
+ if (visibilityChanged) {
+ if (visible && !delayed) {
// The token was made immediately visible, there will be no entrance animation.
// We need to inform the client the enter animation was finished.
wtoken.mEnteringAnimation = true;
mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(wtoken.token);
}
- getDefaultDisplayContentLocked().getDockedDividerController()
- .notifyAppVisibilityChanged(wtoken, visible);
+
+ if (!mClosingApps.contains(wtoken) && !mOpeningApps.contains(wtoken)) {
+ // The token is not closing nor opening, so even if there is an animation set, that
+ // doesn't mean that it goes through the normal app transition cycle so we have
+ // to inform the docked controller about visibility change.
+ getDefaultDisplayContentLocked().getDockedDividerController()
+ .notifyAppVisibilityChanged(wtoken, visible);
+ }
}
return delayed;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 56e2001..93fbe5c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6224,6 +6224,8 @@
}
private void enforceUserUnlocked(int userId) {
+ // Since we're doing this operation on behalf of an app, we only
+ // want to use the actual "unlocked" state.
Preconditions.checkState(mUserManager.isUserUnlocked(userId),
"User must be running and unlocked");
}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 4d02928..f2c995b 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -727,7 +727,7 @@
@Override
public void onPackageModified(String packageName) {
- if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+ if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false);
synchronized (mLock) {
@@ -742,7 +742,7 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
- if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+ if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false);
synchronized (mLock) {
@@ -757,7 +757,7 @@
@Override
public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
int uid, boolean doit) {
- if (!mUserManager.isUserUnlocked(getChangingUserId())) return false;
+ if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return false;
synchronized (mLock) {
// A background user/profile's print jobs are running but there is
// no UI shown. Hence, if the packages of such a user change we need
@@ -795,7 +795,7 @@
@Override
public void onPackageAdded(String packageName, int uid) {
- if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+ if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
synchronized (mLock) {
if (hasPrintService(packageName)) {
UserState userState = getOrCreateUserStateLocked(getChangingUserId(),
@@ -812,7 +812,7 @@
}
private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority) {
- if (!mUserManager.isUserUnlocked(userId)) {
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
throw new IllegalStateException(
"User " + userId + " must be unlocked for printing to be available");
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index bc43576..13518b5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -1061,6 +1061,12 @@
i.putExtra(Intent.EXTRA_REPLACING, true);
return i;
}
+ private Intent genPackageDataClear(String packageName, int userId) {
+ Intent i = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ i.setData(Uri.parse("package:" + packageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ return i;
+ }
private ShortcutInfo parceled(ShortcutInfo si) {
Parcel p = Parcel.obtain();
@@ -4159,6 +4165,79 @@
assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
}
+ /** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */
+ public void testHandlePackageClearData() {
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeShortcutWithIcon("s1", bmp32x32), makeShortcutWithIcon("s2", bmp32x32)
+ )));
+
+ setCaller(CALLING_PACKAGE_2, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_3, USER_0);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_2, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ setCaller(CALLING_PACKAGE_3, USER_10);
+ assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDataClear(CALLING_PACKAGE_1, USER_0));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDataClear(CALLING_PACKAGE_2, USER_10));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+ assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+ assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+ }
+
public void testHandlePackageUpdate() throws Throwable {
// Set up shortcuts and launchers.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b9e9ac8..3785cdc 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -383,6 +383,15 @@
</activity>
<activity
+ android:name="VideoViewCaptureActivity"
+ android:label="SurfaceView/GetBitmap with Video source">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="GLTextureViewActivity"
android:label="TextureView/OpenGL">
<intent-filter>
diff --git a/tests/HwAccelerationTest/res/raw/colorgrid_video.mp4 b/tests/HwAccelerationTest/res/raw/colorgrid_video.mp4
new file mode 100644
index 0000000..1be8bee
--- /dev/null
+++ b/tests/HwAccelerationTest/res/raw/colorgrid_video.mp4
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
index f658b7c..6fe2cb4 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
@@ -23,6 +23,7 @@
import android.os.Environment;
import android.view.Gravity;
import android.view.PixelCopy;
+import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
@@ -87,9 +88,42 @@
android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
}
+ setCameraDisplayOrientation(this, 0, mCamera);
mCamera.startPreview();
}
+ public static void setCameraDisplayOrientation(Activity activity,
+ int cameraId, android.hardware.Camera camera) {
+ android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
+ android.hardware.Camera.getCameraInfo(cameraId, info);
+ int rotation = activity.getWindowManager().getDefaultDisplay()
+ .getRotation();
+ int degrees = 0;
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ degrees = 0;
+ break;
+ case Surface.ROTATION_90:
+ degrees = 90;
+ break;
+ case Surface.ROTATION_180:
+ degrees = 180;
+ break;
+ case Surface.ROTATION_270:
+ degrees = 270;
+ break;
+ }
+
+ int result;
+ if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ result = (info.orientation + degrees) % 360;
+ result = (360 - result) % 360; // compensate the mirror
+ } else { // back-facing
+ result = (info.orientation - degrees + 360) % 360;
+ }
+ camera.setDisplayOrientation(result);
+ }
+
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java
new file mode 100644
index 0000000..b87be80
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java
@@ -0,0 +1,89 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.PixelCopy;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import java.io.FileOutputStream;
+
+public class VideoViewCaptureActivity extends Activity {
+ private VideoView mVideoView;
+ private int mVideoWidth, mVideoHeight;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mVideoView = new VideoView(this);
+ mVideoView.setOnPreparedListener(mp -> {
+ mp.setLooping(true);
+ mVideoWidth = mp.getVideoWidth();
+ mVideoHeight = mp.getVideoHeight();
+ mVideoView.start();
+ });
+
+ Uri uri = Uri.parse("android.resource://com.android.test.hwui/" + R.raw.colorgrid_video);
+ mVideoView.setVideoURI(uri);
+
+ Button button = new Button(this);
+ button.setText("Copy bitmap to /sdcard/surfaceview.png");
+ button.setOnClickListener((View v) -> {
+ final Bitmap b = Bitmap.createBitmap(
+ mVideoWidth, mVideoHeight,
+ Bitmap.Config.ARGB_8888);
+ PixelCopy.request(mVideoView, b,
+ (int result) -> {
+ if (result != PixelCopy.SUCCESS) {
+ Toast.makeText(VideoViewCaptureActivity.this,
+ "Failed to copy", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ try {
+ try (FileOutputStream out = new FileOutputStream(
+ Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+ b.compress(Bitmap.CompressFormat.PNG, 100, out);
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }, mVideoView.getHandler());
+ });
+
+ FrameLayout content = new FrameLayout(this);
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ layout.addView(mVideoView, LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+
+ content.addView(layout, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT));
+ setContentView(content);
+ }
+}
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index ca06ac4..653c1b4 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -55,7 +55,7 @@
mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
mAutoAddOverlay(false), mGenDependencies(false), mNoVersionVectors(false),
- mCrunchedOutputDir(NULL), mProguardFile(NULL),
+ mCrunchedOutputDir(NULL), mProguardFile(NULL), mMainDexProguardFile(NULL),
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
@@ -146,6 +146,8 @@
void setCrunchedOutputDir(const char* dir) { mCrunchedOutputDir = dir; }
const char* getProguardFile() const { return mProguardFile; }
void setProguardFile(const char* file) { mProguardFile = file; }
+ const char* getMainDexProguardFile() const { return mMainDexProguardFile; }
+ void setMainDexProguardFile(const char* file) { mMainDexProguardFile = file; }
const android::Vector<const char*>& getResourceSourceDirs() const { return mResourceSourceDirs; }
void addResourceSourceDir(const char* dir) { mResourceSourceDirs.insertAt(dir,0); }
const char* getAndroidManifestFile() const { return mAndroidManifestFile; }
@@ -299,6 +301,7 @@
bool mNoVersionVectors;
const char* mCrunchedOutputDir;
const char* mProguardFile;
+ const char* mMainDexProguardFile;
const char* mAndroidManifestFile;
const char* mPublicOutputFile;
const char* mRClassDir;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index db40416..9976d00 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2631,6 +2631,12 @@
goto bail;
}
+ // Write out the Main Dex ProGuard file
+ err = writeMainDexProguardFile(bundle, assets);
+ if (err < 0) {
+ goto bail;
+ }
+
// Write the apk
if (outputAPKFile) {
// Gather all resources and add them to the APK Builder. The builder will then
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index c424cc5..984d98e 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -67,6 +67,7 @@
" [--max-res-version VAL] \\\n"
" [-I base-package [-I base-package ...]] \\\n"
" [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n"
+ " [-D main-dex-class-list-file] \\\n"
" [-S resource-sources [-S resource-sources ...]] \\\n"
" [-F apk-file] [-J R-file-dir] \\\n"
" [--product product1,product2,...] \\\n"
@@ -120,6 +121,7 @@
" localization=\"suggested\"\n"
" -A additional directory in which to find raw asset files\n"
" -G A file to output proguard options into.\n"
+ " -D A file to output proguard options for the main dex into.\n"
" -F specify the apk file to output\n"
" -I add an existing package to base include set\n"
" -J specify where to output R.java resource constant definitions\n"
@@ -390,6 +392,17 @@
convertPath(argv[0]);
bundle.setProguardFile(argv[0]);
break;
+ case 'D':
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-D' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ convertPath(argv[0]);
+ bundle.setMainDexProguardFile(argv[0]);
+ break;
case 'I':
argc--;
argv++;
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index e84c4c5..a493842 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -54,6 +54,7 @@
bool includePrivate, bool emitCallback);
extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets);
+extern android::status_t writeMainDexProguardFile(Bundle* bundle, const sp<AaptAssets>& assets);
extern bool isValidResourceType(const String8& type);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 18a1943..8e7045b 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2832,7 +2832,7 @@
}
status_t
-writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
+writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets, bool mainDex)
{
status_t err;
ResXMLTree tree;
@@ -2844,6 +2844,7 @@
sp<AaptGroup> assGroup;
sp<AaptFile> assFile;
String8 pkg;
+ String8 defaultProcess;
// First, look for a package file to parse. This is required to
// be able to generate the resource information.
@@ -2900,6 +2901,15 @@
addProguardKeepRule(keep, agent, pkg.string(),
assFile->getPrintableSource(), tree.getLineNumber());
}
+
+ if (mainDex) {
+ defaultProcess = AaptXml::getAttribute(tree,
+ "http://schemas.android.com/apk/res/android", "process", &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: %s\n", error.string());
+ return -1;
+ }
+ }
} else if (tag == "instrumentation") {
keepTag = true;
}
@@ -2916,7 +2926,23 @@
fprintf(stderr, "ERROR: %s\n", error.string());
return -1;
}
- if (name.length() > 0) {
+
+ keepTag = name.length() > 0;
+
+ if (keepTag && mainDex) {
+ String8 componentProcess = AaptXml::getAttribute(tree,
+ "http://schemas.android.com/apk/res/android", "process", &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: %s\n", error.string());
+ return -1;
+ }
+
+ const String8& process =
+ componentProcess.length() > 0 ? componentProcess : defaultProcess;
+ keepTag = process.length() > 0 && process.find(":") != 0;
+ }
+
+ if (keepTag) {
addProguardKeepRule(keep, name, pkg.string(),
assFile->getPrintableSource(), tree.getLineNumber());
}
@@ -3099,30 +3125,12 @@
}
status_t
-writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+writeProguardSpec(const char* filename, const ProguardKeepSet& keep, status_t err)
{
- status_t err = -1;
-
- if (!bundle->getProguardFile()) {
- return NO_ERROR;
- }
-
- ProguardKeepSet keep;
-
- err = writeProguardForAndroidManifest(&keep, assets);
- if (err < 0) {
- return err;
- }
-
- err = writeProguardForLayouts(&keep, assets);
- if (err < 0) {
- return err;
- }
-
- FILE* fp = fopen(bundle->getProguardFile(), "w+");
+ FILE* fp = fopen(filename, "w+");
if (fp == NULL) {
fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
- bundle->getProguardFile(), strerror(errno));
+ filename, strerror(errno));
return UNKNOWN_ERROR;
}
@@ -3141,6 +3149,49 @@
return err;
}
+status_t
+writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+ status_t err = -1;
+
+ if (!bundle->getProguardFile()) {
+ return NO_ERROR;
+ }
+
+ ProguardKeepSet keep;
+
+ err = writeProguardForAndroidManifest(&keep, assets, false);
+ if (err < 0) {
+ return err;
+ }
+
+ err = writeProguardForLayouts(&keep, assets);
+ if (err < 0) {
+ return err;
+ }
+
+ return writeProguardSpec(bundle->getProguardFile(), keep, err);
+}
+
+status_t
+writeMainDexProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+ status_t err = -1;
+
+ if (!bundle->getMainDexProguardFile()) {
+ return NO_ERROR;
+ }
+
+ ProguardKeepSet keep;
+
+ err = writeProguardForAndroidManifest(&keep, assets, true);
+ if (err < 0) {
+ return err;
+ }
+
+ return writeProguardSpec(bundle->getMainDexProguardFile(), keep, err);
+}
+
// Loops through the string paths and writes them to the file pointer
// Each file path is written on its own line with a terminating backslash.
status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8c8bffa..4997120 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -1459,6 +1459,21 @@
return 1;
}
+ // Expand all argument-files passed into the command line. These start with '@'.
+ std::vector<std::string> argList;
+ for (const std::string& arg : flags.getArgs()) {
+ if (util::stringStartsWith<char>(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::appendArgsFromFile(path, &argList, &error)) {
+ context.getDiagnostics()->error(DiagMessage(path) << error);
+ return 1;
+ }
+ } else {
+ argList.push_back(arg);
+ }
+ }
+
if (verbose) {
context.setVerbose(verbose);
}
@@ -1568,7 +1583,7 @@
}
LinkCommand cmd(&context, options);
- return cmd.run(flags.getArgs());
+ return cmd.run(argList);
}
} // namespace aapt
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index bb093ab..f5e49f1 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -18,6 +18,7 @@
#include "util/Util.h"
#include <algorithm>
+#include <android-base/file.h>
#include <cerrno>
#include <cstdio>
#include <dirent.h>
@@ -190,6 +191,23 @@
return std::move(fileMap);
}
+bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+ std::string* outError) {
+ std::string contents;
+ if (!android::base::ReadFileToString(path.toString(), &contents)) {
+ if (outError) *outError = "failed to read argument-list file";
+ return false;
+ }
+
+ for (StringPiece line : util::tokenize<char>(contents, ' ')) {
+ line = util::trimWhitespace(line);
+ if (!line.empty()) {
+ outArgList->push_back(line.toString());
+ }
+ }
+ return true;
+}
+
bool FileFilter::setPattern(const StringPiece& pattern) {
mPatternTokens = util::splitAndLowercase(pattern, ':');
return true;
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index c2e6115..4d8a1fe 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -95,6 +95,12 @@
*/
Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError);
+/**
+ * Reads the file at path and appends each line to the outArgList vector.
+ */
+bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+ std::string* outError);
+
/*
* Filter that determines which resource files/directories are
* processed by AAPT. Takes a pattern string supplied by the user.