summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--core/java/android/app/Notification.java64
-rw-r--r--core/java/android/app/PendingIntent.java35
-rw-r--r--core/java/android/content/pm/PackageParser.java5
-rw-r--r--core/java/android/os/Parcel.java37
-rw-r--r--core/java/android/util/ArraySet.java26
-rw-r--r--core/java/android/view/SurfaceView.java8
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java6
-rw-r--r--core/res/res/drawable-nodpi/platlogo.xml6
-rw-r--r--core/res/res/values-bn-rBD/strings.xml9
-rw-r--r--core/res/res/values-bs-rBA/strings.xml9
-rw-r--r--core/res/res/values-eu-rES/strings.xml9
-rw-r--r--core/res/res/values-gl-rES/strings.xml9
-rw-r--r--core/res/res/values-gu-rIN/strings.xml9
-rw-r--r--core/res/res/values-is-rIS/strings.xml9
-rw-r--r--core/res/res/values-kk-rKZ/strings.xml9
-rw-r--r--core/res/res/values-kn-rIN/strings.xml9
-rw-r--r--core/res/res/values-ky-rKG/strings.xml2
-rw-r--r--core/res/res/values-mk-rMK/strings.xml9
-rw-r--r--core/res/res/values-ml-rIN/strings.xml9
-rw-r--r--core/res/res/values-mr-rIN/strings.xml9
-rw-r--r--core/res/res/values-my-rMM/strings.xml9
-rw-r--r--core/res/res/values-pa-rIN/strings.xml9
-rw-r--r--core/res/res/values-pt-rPT/strings.xml2
-rw-r--r--core/res/res/values-sq-rAL/strings.xml9
-rw-r--r--core/res/res/values-ta-rIN/strings.xml9
-rw-r--r--core/res/res/values-te-rIN/strings.xml9
-rw-r--r--core/res/res/values-ur-rPK/strings.xml9
-rw-r--r--core/res/res/values-uz-rUZ/strings.xml9
-rw-r--r--core/res/res/values/themes.xml1
-rw-r--r--docs/html/_redirects.yaml2
-rw-r--r--docs/html/distribute/googleplay/guide.jd102
-rw-r--r--docs/html/images/cards/card-secrets-playbook_2x.jpgbin0 -> 12270 bytes
-rw-r--r--docs/html/images/gp-secrets-playbook-lg.pngbin0 -> 148384 bytes
-rw-r--r--docs/html/images/gp-secrets-playbook.pngbin0 -> 12009 bytes
-rw-r--r--docs/html/jd_extras_en.js4
-rw-r--r--docs/html/ndk/downloads/index.jd2
-rw-r--r--docs/html/ndk/guides/setup.jd3
-rw-r--r--docs/html/preview/features/picture-in-picture.jd8
-rw-r--r--libs/hwui/FrameBuilder.cpp1
-rw-r--r--libs/hwui/LayerUpdateQueue.cpp1
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp34
-rw-r--r--libs/hwui/tests/unit/LayerUpdateQueueTests.cpp6
-rw-r--r--media/java/android/media/MediaCodec.java114
-rw-r--r--media/java/android/media/MediaCodecInfo.java66
-rw-r--r--media/java/android/media/MediaCodecList.java6
-rw-r--r--media/java/android/media/MediaExtractor.java106
-rw-r--r--media/java/android/media/MediaFormat.java14
-rw-r--r--media/java/android/media/MediaMuxer.java115
-rw-r--r--packages/EasterEgg/Android.mk21
-rw-r--r--packages/EasterEgg/AndroidManifest.xml75
-rw-r--r--packages/EasterEgg/res/drawable/back.xml22
-rw-r--r--packages/EasterEgg/res/drawable/belly.xml22
-rw-r--r--packages/EasterEgg/res/drawable/body.xml22
-rw-r--r--packages/EasterEgg/res/drawable/bowtie.xml22
-rw-r--r--packages/EasterEgg/res/drawable/cap.xml22
-rw-r--r--packages/EasterEgg/res/drawable/collar.xml22
-rw-r--r--packages/EasterEgg/res/drawable/face_spot.xml22
-rw-r--r--packages/EasterEgg/res/drawable/food_bits.xml33
-rw-r--r--packages/EasterEgg/res/drawable/food_chicken.xml39
-rw-r--r--packages/EasterEgg/res/drawable/food_dish.xml24
-rw-r--r--packages/EasterEgg/res/drawable/food_donut.xml24
-rw-r--r--packages/EasterEgg/res/drawable/food_sysuituna.xml24
-rw-r--r--packages/EasterEgg/res/drawable/foot1.xml22
-rw-r--r--packages/EasterEgg/res/drawable/foot2.xml22
-rw-r--r--packages/EasterEgg/res/drawable/foot3.xml22
-rw-r--r--packages/EasterEgg/res/drawable/foot4.xml22
-rw-r--r--packages/EasterEgg/res/drawable/head.xml22
-rw-r--r--packages/EasterEgg/res/drawable/ic_close.xml24
-rw-r--r--packages/EasterEgg/res/drawable/ic_share.xml24
-rw-r--r--packages/EasterEgg/res/drawable/icon.xml37
-rw-r--r--packages/EasterEgg/res/drawable/left_ear.xml22
-rw-r--r--packages/EasterEgg/res/drawable/left_ear_inside.xml22
-rw-r--r--packages/EasterEgg/res/drawable/left_eye.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg1.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg2.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg2_shadow.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg3.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg4.xml22
-rw-r--r--packages/EasterEgg/res/drawable/mouth.xml27
-rw-r--r--packages/EasterEgg/res/drawable/nose.xml22
-rw-r--r--packages/EasterEgg/res/drawable/right_ear.xml22
-rw-r--r--packages/EasterEgg/res/drawable/right_ear_inside.xml23
-rw-r--r--packages/EasterEgg/res/drawable/right_eye.xml22
-rw-r--r--packages/EasterEgg/res/drawable/stat_icon.xml30
-rw-r--r--packages/EasterEgg/res/drawable/tail.xml26
-rw-r--r--packages/EasterEgg/res/drawable/tail_cap.xml22
-rw-r--r--packages/EasterEgg/res/drawable/tail_shadow.xml22
-rw-r--r--packages/EasterEgg/res/layout/cat_view.xml79
-rw-r--r--packages/EasterEgg/res/layout/edit_text.xml30
-rw-r--r--packages/EasterEgg/res/layout/food_layout.xml31
-rw-r--r--packages/EasterEgg/res/layout/neko_activity.xml25
-rw-r--r--packages/EasterEgg/res/values/strings.xml52
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/Cat.java375
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/Food.java60
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java53
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java105
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoLand.java280
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java45
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoService.java136
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoTile.java96
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/PrefState.java94
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java4
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java15
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java45
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java3
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java17
-rw-r--r--services/core/java/com/android/server/content/SyncOperation.java2
-rw-r--r--services/core/java/com/android/server/job/controllers/ContentObserverController.java40
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java78
112 files changed, 3335 insertions, 258 deletions
diff --git a/Android.mk b/Android.mk
index c2d398794fa1..dec667e9b71c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -495,6 +495,10 @@ LOCAL_JACK_FLAGS := --multi-dex native
LOCAL_RMTYPEDEFS := true
+ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true)
+LOCAL_EMMA_INSTRUMENT := true
+endif
+
include $(BUILD_JAVA_LIBRARY)
framework_module := $(LOCAL_INSTALLED_MODULE)
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6fc1820c3c36..7682af8def81 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -39,6 +39,7 @@ import android.media.AudioManager;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.BadParcelableException;
+import android.os.BaseBundle;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
@@ -53,6 +54,7 @@ import android.text.style.AbsoluteSizeSpan;
import android.text.style.CharacterStyle;
import android.text.style.RelativeSizeSpan;
import android.text.style.TextAppearanceSpan;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.view.Gravity;
@@ -63,6 +65,7 @@ import android.widget.ProgressBar;
import android.widget.RemoteViews;
import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.NotificationColorUtil;
import java.lang.annotation.Retention;
@@ -70,6 +73,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -758,6 +762,15 @@ public class Notification implements Parcelable
public Bundle extras = new Bundle();
/**
+ * All pending intents in the notification as the system needs to be able to access them but
+ * touching the extras bundle in the system process is not safe because the bundle may contain
+ * custom parcelable objects.
+ *
+ * @hide
+ */
+ public ArraySet<PendingIntent> allPendingIntents;
+
+ /**
* {@link #extras} key: this is the title of the notification,
* as supplied to {@link Builder#setContentTitle(CharSequence)}.
*/
@@ -1549,7 +1562,16 @@ public class Notification implements Parcelable
/**
* Unflatten the notification from a parcel.
*/
- public Notification(Parcel parcel)
+ @SuppressWarnings("unchecked")
+ public Notification(Parcel parcel) {
+ // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
+ // intents in extras are always written as the last entry.
+ readFromParcelImpl(parcel);
+ // Must be read last!
+ allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
+ }
+
+ private void readFromParcelImpl(Parcel parcel)
{
int version = parcel.readInt();
@@ -1704,6 +1726,10 @@ public class Notification implements Parcelable
}
}
+ if (!ArrayUtils.isEmpty(allPendingIntents)) {
+ that.allPendingIntents = new ArraySet<>(allPendingIntents);
+ }
+
if (this.actions != null) {
that.actions = new Action[this.actions.length];
for(int i=0; i<this.actions.length; i++) {
@@ -1819,8 +1845,40 @@ public class Notification implements Parcelable
/**
* Flatten this notification into a parcel.
*/
- public void writeToParcel(Parcel parcel, int flags)
- {
+ public void writeToParcel(Parcel parcel, int flags) {
+ // We need to mark all pending intents getting into the notification
+ // system as being put there to later allow the notification ranker
+ // to launch them and by doing so add the app to the battery saver white
+ // list for a short period of time. The problem is that the system
+ // cannot look into the extras as there may be parcelables there that
+ // the platform does not know how to handle. To go around that we have
+ // an explicit list of the pending intents in the extras bundle.
+ final boolean collectPendingIntents = (allPendingIntents == null);
+ if (collectPendingIntents) {
+ PendingIntent.setOnMarshaledListener(
+ (PendingIntent intent, Parcel out, int outFlags) -> {
+ if (parcel == out) {
+ if (allPendingIntents == null) {
+ allPendingIntents = new ArraySet<>();
+ }
+ allPendingIntents.add(intent);
+ }
+ });
+ }
+ try {
+ // IMPORTANT: Add marshaling code in writeToParcelImpl as we
+ // want to intercept all pending events written to the pacel.
+ writeToParcelImpl(parcel, flags);
+ // Must be written last!
+ parcel.writeArraySet(allPendingIntents);
+ } finally {
+ if (collectPendingIntents) {
+ PendingIntent.setOnMarshaledListener(null);
+ }
+ }
+ }
+
+ private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
parcel.writeLong(when);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index cb15392b6a8a..cfa242be02aa 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -242,6 +242,36 @@ public final class PendingIntent implements Parcelable {
}
/**
+ * Listener for observing when pending intents are written to a parcel.
+ *
+ * @hide
+ */
+ public interface OnMarshaledListener {
+ /**
+ * Called when a pending intent is written to a parcel.
+ *
+ * @param intent The pending intent.
+ * @param parcel The parcel to which it was written.
+ * @param flags The parcel flags when it was written.
+ */
+ void onMarshaled(PendingIntent intent, Parcel parcel, int flags);
+ }
+
+ private static final ThreadLocal<OnMarshaledListener> sOnMarshaledListener
+ = new ThreadLocal<>();
+
+ /**
+ * Registers an listener for pending intents being written to a parcel.
+ *
+ * @param listener The listener, null to clear.
+ *
+ * @hide
+ */
+ public static void setOnMarshaledListener(OnMarshaledListener listener) {
+ sOnMarshaledListener.set(listener);
+ }
+
+ /**
* Retrieve a PendingIntent that will start a new activity, like calling
* {@link Context#startActivity(Intent) Context.startActivity(Intent)}.
* Note that the activity will be started outside of the context of an
@@ -1016,6 +1046,11 @@ public final class PendingIntent implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeStrongBinder(mTarget.asBinder());
+ OnMarshaledListener listener = sOnMarshaledListener.get();
+ if (listener != null) {
+ listener.onMarshaled(this, out, flags);
+ }
+
}
public static final Parcelable.Creator<PendingIntent> CREATOR
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index adf9fe62c37e..4da77f4b35c9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1871,7 +1871,7 @@ public class PackageParser {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesSdk);
- int minVers = 0;
+ int minVers = 1;
String minCode = null;
int targetVers = 0;
String targetCode = null;
@@ -1898,9 +1898,6 @@ public class PackageParser {
} else {
// If it's not a string, it's an integer.
targetVers = val.data;
- if (minVers == 0) {
- minVers = targetVers;
- }
}
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 26312475297c..74dcc0787b3b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -17,8 +17,10 @@
package android.os;
import android.annotation.IntegerRes;
+import android.annotation.Nullable;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Size;
import android.util.SizeF;
@@ -734,6 +736,21 @@ public final class Parcel {
}
/**
+ * Write an array set to the parcel.
+ *
+ * @param val The array set to write.
+ *
+ * @hide
+ */
+ public void writeArraySet(@Nullable ArraySet<? extends Object> val) {
+ final int size = (val != null) ? val.size() : -1;
+ writeInt(size);
+ for (int i = 0; i < size; i++) {
+ writeValue(val.valueAt(i));
+ }
+ }
+
+ /**
* Flatten a Bundle into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
@@ -2735,6 +2752,26 @@ public final class Parcel {
readArrayMapInternal(outVal, N, loader);
}
+ /**
+ * Reads an array set.
+ *
+ * @param loader The class loader to use.
+ *
+ * @hide
+ */
+ public @Nullable ArraySet<? extends Object> readArraySet(ClassLoader loader) {
+ final int size = readInt();
+ if (size < 0) {
+ return null;
+ }
+ ArraySet<Object> result = new ArraySet<>(size);
+ for (int i = 0; i < size; i++) {
+ Object value = readValue(loader);
+ result.append(value);
+ }
+ return result;
+ }
+
private void readListInternal(List outVal, int N,
ClassLoader loader) {
while (N > 0) {
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 9e9314fba4c4..d39e91fd98b2 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -390,6 +390,32 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
}
/**
+ * Special fast path for appending items to the end of the array without validation.
+ * The array must already be large enough to contain the item.
+ * @hide
+ */
+ public void append(E value) {
+ final int index = mSize;
+ final int hash = value == null ? 0
+ : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode());
+ if (index >= mHashes.length) {
+ throw new IllegalStateException("Array is full");
+ }
+ if (index > 0 && mHashes[index - 1] > hash) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "New hash " + hash
+ + " is before end of array hash " + mHashes[index - 1]
+ + " at index " + index, e);
+ add(value);
+ return;
+ }
+ mSize = index + 1;
+ mHashes[index] = hash;
+ mArray[index] = value;
+ }
+
+ /**
* Perform a {@link #add(Object)} of all values in <var>array</var>
* @param array The array whose contents are to be retrieved.
*/
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ac43bb7adb8c..203b82563ef8 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -745,6 +745,12 @@ public class SurfaceView extends View {
Log.d(TAG, String.format("%d windowPositionLostRT RT, frameNr = %d",
System.identityHashCode(this), frameNumber));
}
+ IWindowSession session = mSession;
+ MyWindow window = mWindow;
+ if (session == null || window == null) {
+ // We got detached prior to receiving this, abort
+ return;
+ }
if (mRtHandlingPositionUpdates) {
mRtHandlingPositionUpdates = false;
// This callback will happen while the UI thread is blocked, so we can
@@ -757,7 +763,7 @@ public class SurfaceView extends View {
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
mWinFrame.left, mWinFrame.top,
mWinFrame.right, mWinFrame.bottom));
- mSession.repositionChild(mWindow, mWinFrame.left, mWinFrame.top,
+ session.repositionChild(window, mWinFrame.left, mWinFrame.top,
mWinFrame.right, mWinFrame.bottom, frameNumber, mWinFrame);
} catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index ef2fd0d7401d..36ab394f4a79 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -53,7 +53,8 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
public class PlatLogoActivity extends Activity {
- public static final boolean REVEAL_THE_NAME = true;
+ public static final boolean REVEAL_THE_NAME = false;
+ public static final boolean FINISH = false;
FrameLayout mLayout;
int mTapCount;
@@ -112,7 +113,6 @@ public class PlatLogoActivity extends Activity {
ObjectAnimator.ofInt(overlay, "alpha", 0, 255)
.setDuration(500)
.start();
- return true;
}
final ContentResolver cr = getContentResolver();
@@ -139,7 +139,7 @@ public class PlatLogoActivity extends Activity {
} catch (ActivityNotFoundException ex) {
Log.e("PlatLogoActivity", "No more eggs.");
}
- finish();
+ if (FINISH) finish();
}
});
return true;
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index defa83a9b5c6..516f25284282 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -19,10 +19,10 @@ Copyright (C) 2016 The Android Open Source Project
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="#FF7E5BBF"
+ android:fillColor="#FFc7d4b6"
android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/>
<path
- android:fillColor="#FF7E5BBF"
+ android:fillColor="#FFfbd3cb"
android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/>
<path
android:fillColor="#40000000"
@@ -31,7 +31,7 @@ Copyright (C) 2016 The Android Open Source Project
android:fillColor="#40000000"
android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/>
<path
- android:fillColor="#FF55C4F5"
+ android:fillColor="#FFe0e0d6"
android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/>
</vector>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 8adee9b663bc..2d88a1d7a33a 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"কোনো অ্যাপ্লিকেশানকে সেশনগুলি পড়ার অনুমতি দেয়। এটি সক্রিয় প্যাকেজ ইনস্টলেশনের বিশদ বিবরণ দেখতে দেয়।"</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"প্যাকেজগুলি ইনস্টল করার অনুরোধ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"একটি অ্যাপ্লিকেশানকে প্যাকেজগুলির ইনস্টল করার অনুরোধ জানাতে অনুমতি দেয়৷"</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"জুম নিয়ন্ত্রণের জন্য দুবার আলতো চাপুন"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"উইজেট যোগ করা যায়নি৷"</string>
<string name="ime_action_go" msgid="8320845651737369027">"যান"</string>
<string name="ime_action_search" msgid="658110271822807811">"অনুসন্ধান করুন"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"বিজ্ঞপ্তি র‌্যাঙ্কার পরিষেবা"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN সক্রিয়"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> এর দ্বারা VPN সক্রিয় করা হয়েছে"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"নেটওয়ার্ক পরিচালনা করতে আলতো চাপুন।"</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> তে সংযুক্ত হয়েছে৷ নেটওয়ার্ক পরিচালনা করতে আলতো চাপুন৷"</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"সর্বদা-চালু VPN সংযুক্ত হচ্ছে..."</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"সর্বদা-চালু VPN সংযুক্ত হয়েছে"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"সর্বদা-চালু VPN ত্রুটি"</string>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 89a12ee41759..272375bad37f 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -1222,8 +1222,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Dozvoljava aplikaciji da čita sesije instalacija. Ovim se aplikaciji omogućava da vidi detalje o aktivnim instalacijama paketa."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"zahtijevanje paketa za instaliranje"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Omogućava aplikaciji da zahtijeva instalaciju paket ā."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Dodirnite dvaput za kontrolu uvećanja"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Dodavanje vidžeta nije uspjelo."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Počni"</string>
<string name="ime_action_search" msgid="658110271822807811">"Traži"</string>
@@ -1254,10 +1253,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Usluga rangiranja obavještenja"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> je aktivirala VPN"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Dodirnite da upravljate mrežom."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"Povezano sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dodirnite da upravljate mrežom."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezivanje na uvijek aktivni VPN…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Povezan na uvijek aktivni VPN"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Greška u povezivanju na uvijek aktivni VPN"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index b5e062e01bc4..98adc8624b27 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Instalazio-saioak irakurtzea baimentzen die aplikazioei. Horrela, pakete-instalazio aktiboei buruzko xehetasunak ikus ditzakete."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Eskatu instalazio-paketeak"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Paketeak instalatzeko eskatzea baimentzen die aplikazioei."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Sakatu birritan zooma kontrolatzeko"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Ezin izan da widgeta gehitu."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Joan"</string>
<string name="ime_action_search" msgid="658110271822807811">"Bilatu"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Jakinarazpenen sailkapen-zerbitzua"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN eginbidea aktibatuta"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN konexioa aktibatu du"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Sakatu sarea kudeatzeko."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> saiora konektatuta. Sakatu sarea kudeatzeko."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Beti aktibatuta dagoen VPNa konektatzen…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Beti aktibatuta dagoen VPNa konektatu da"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Beti aktibatuta dagoen VPN errorea"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 08d02b8da15d..23669d4dc09c 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que unha aplicación consulte as sesións de instalación. Desta forma, pode ver os detalles acerca das instalacións de paquetes activas."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar instalación de paquetes"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite a unha aplicación solicitar a instalación dos paquetes."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Toca dúas veces para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Non se puido engadir o widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
<string name="ime_action_search" msgid="658110271822807811">"Buscar"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Servizo de clasificación de notificacións"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> activou a VPN"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Toca aquí para xestionar a rede."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca aquí para xestionar a rede."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sempre activada conectándose..."</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre activada conectada"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Erro na VPN sempre activada"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index b9a9f118c930..656061dd340d 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"એપ્લિકેશનને ઇન્સ્ટોલ સત્રોને વાંચવાની મંજૂરી આપે છે. આ તેને સક્રિય પૅકેજ ઇન્સ્ટોલેશન્સ વિશે વિગતો જોવાની મંજૂરી આપે છે."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"પૅકેજેસ ઇન્સ્ટૉલ કરવાની વિનંતી કરો"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"એપ્લિકેશનને પૅકેજેસના ઇન્સ્ટોલેશનની વિનંતી કરવાની મંજૂરી આપો."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ઝૂમ નિયંત્રણ માટે બેવાર ટૅપ કરો"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"વિજેટ ઉમેરી શકાયું નથી."</string>
<string name="ime_action_go" msgid="8320845651737369027">"જાઓ"</string>
<string name="ime_action_search" msgid="658110271822807811">"શોધો"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"સૂચના રેંકર સેવા"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN સક્રિય કર્યું"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> દ્વારા VPN સક્રિય થયું"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"નેટવર્કને સંચાલિત કરવા માટે ટૅપ કરો."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> થી કનેક્ટ થયાં. નેટવર્કને સંચાલિત કરવા માટે ટૅપ કરો."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"હંમેશા-ચાલુ VPN કનેક્ટ થઈ રહ્યું છે…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"હંમેશા-ચાલુ VPN કનેક્ટ થયું"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"હંમેશાં ચાલુ VPN ભૂલ"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 58f5d9afe8c9..b575e791c666 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Leyfir forriti að lesa uppsetningarlotur. Þetta gerir því kleift að sjá upplýsingar um virkar pakkauppsetningar."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"fara fram á uppsetningu pakka"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Leyfir forriti að fara fram á uppsetningu pakka."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Ýttu tvisvar til að opna aðdráttarstýringar"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Ekki tókst að bæta græju við."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Áfram"</string>
<string name="ime_action_search" msgid="658110271822807811">"Leita"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Tilkynningaröðun"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN virkjað"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN er virkjað með <xliff:g id="APP">%s</xliff:g>"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Ýttu til að hafa umsjón með netkerfi"</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"Tengt við <xliff:g id="SESSION">%s</xliff:g>. Ýttu til að hafa umsjón með netinu."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Sívirkt VPN tengist…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Sívirkt VPN tengt"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Villa í sívirku VPN"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 71561fb902f0..d667d5df28ac 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Қолданбаға орнату сеанстарын оқуға рұқсат етеді. Бұл оған белсенді бума орнатулары туралы мәліметтерді көруге рұқсат етеді."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"орнату бумаларын сұрау"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Қолданбаның бумаларды орнатуға рұқсат сұрауына мүмкіндік береді."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Масштабтау параметрін басқару үшін екі рет түртіңіз"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетті қосу."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Өту"</string>
<string name="ime_action_search" msgid="658110271822807811">"Іздеу"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Хабарландыруларды жіктеу қызметі"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN белсенді"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"ВЖЭ <xliff:g id="APP">%s</xliff:g> арқылы қосылған"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Желіні басқару үшін түртіңіз"</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> жүйесіне жалғанған. Желіні басқару үшін түріңіз."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Әрқашан қосылған ВЖЖ жалғануда…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Әрқашан қосылған ВЖЖ жалғанған"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Әрқашан қосылған ВЖЖ қателігі"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 26bbbc6b87e3..c172e912743f 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ಸ್ಥಾಪಿತ ಸೆಷನ್‌ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ. ಸಕ್ರಿಯ ಪ್ಯಾಕೇಜ್‌ ಸ್ಥಾಪನೆಗಳ ಕುರಿತು ವಿವರಣೆಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಇದು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ಸ್ಥಾಪನೆ ಪ್ಯಾಕೇಜ್‌ಗಳನ್ನು ವಿನಂತಿಸಿ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ಪ್ಯಾಕೇಜ್‌ಗಳ ಸ್ಥಾಪನೆಯನ್ನು ವಿನಂತಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ಝೂಮ್‌ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
<string name="ime_action_go" msgid="8320845651737369027">"ಹೋಗು"</string>
<string name="ime_action_search" msgid="658110271822807811">"ಹುಡುಕು"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"ಅಧಿಸೂಚನೆ ಶ್ರೇಣಿಯ ಸೇವೆ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ಮೂಲಕ VPN ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"ನೆಟ್‍ವರ್ಕ್ ನಿರ್ವಹಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ. ನೆಟ್‍ವರ್ಕ್ ನಿರ್ವಹಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ಯಾವಾಗಲೂ-ಆನ್ VPN ಸಂಪರ್ಕಗೊಳ್ಳುತ್ತಿದೆ…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"ಯಾವಾಗಲೂ-ಆನ್ VPN ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"ಯಾವಾಗಲೂ-ಆನ್ VPN ದೋಷ"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index a18ac62aaebd..50340d4d3b75 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1015,7 +1015,7 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Шкала"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Ар дайым көрсөтүлсүн"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Муну тутум жөндөөлөрүнөн кайра иштетүү &gt; Колдонмолор &gt; Жүктөлүп алынган."</string>
- <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу учурдагы дисплей өлчөмүнүн жөндөөлөрүн колдоого албагандыктан, күтүүсүз аракеттерди жасашы мүмкүн."</string>
+ <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу көрүнүштүн тандалган өлчөмүн экранда көрсөтө албайт жана туура эмес иштеши мүмкүн."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Ар дайым көрсөтүлсүн"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string>
<string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> процесси өзүнүн мажбурланган StrictMode саясатын бузуп койду."</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 2519dd95f096..a24ad5711e11 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дозволува апликација да чита сесии на инсталирање. Тоа овозможува апликацијата да гледа детали за активни инсталации на пакет."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"барање пакети за инсталирање"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Дозволува апликацијата да бара инсталација на пакети."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Допрете двапати за контрола на зумот"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не можеше да се додаде виџет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Оди"</string>
<string name="ime_action_search" msgid="658110271822807811">"Пребарај"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Услуга за рангирање известувања"</string>
<string name="vpn_title" msgid="19615213552042827">"Активирана VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана со <xliff:g id="APP">%s</xliff:g>"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Допрете за да управувате со мрежата."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"Поврзани сте на <xliff:g id="SESSION">%s</xliff:g>. Допрете за да управувате со мрежата."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Поврзување со секогаш вклучена VPN..."</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Поврзани со секогаш вклучена VPN"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка на секогаш вклучена VPN"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 9d8c2749d38a..f91db2d1a495 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ഇൻസ്റ്റാൾ ചെയ്‌ത സെഷനുകൾ റീഡുചെയ്യുന്നതിന് ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. സജീവ പാക്കേജ് ഇൻസ്റ്റാളേഷനുകളെക്കുറിച്ചുള്ള വിശദാംശങ്ങൾ കാണുന്നതിന് ഇത് അനുവദിക്കുന്നു."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"പാക്കേജുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ അഭ്യർത്ഥിക്കുക"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"പാക്കേജുകളുടെ ഇൻസ്റ്റാളേഷൻ അഭ്യർത്ഥിക്കാൻ ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"സൂം നിയന്ത്രണം ലഭിക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"വിജറ്റ് ചേർക്കാനായില്ല."</string>
<string name="ime_action_go" msgid="8320845651737369027">"പോവുക"</string>
<string name="ime_action_search" msgid="658110271822807811">"തിരയൽ"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"അറിയിപ്പ് റാങ്കർ സേവനം"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN സജീവമാക്കി"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ഉപയോഗിച്ച് VPN പ്രവർത്തനക്ഷമമാക്കി"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"നെറ്റ്‌വർക്ക് മാനേജുചെയ്യാൻ ടാപ്പുചെയ്യുക"</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> എന്ന സെഷനിലേക്ക് കണക്റ്റുചെയ്തു. നെറ്റ്‌വർക്ക് മാനേജുചെയ്യാൻ ടാപ്പുചെയ്യുക."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"എല്ലായ്‌പ്പോഴും ഓണായിരിക്കുന്ന VPN കണക്റ്റുചെയ്യുന്നു…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"എല്ലായ്‌പ്പോഴും ഓണായിരിക്കുന്ന VPN കണക്റ്റുചെയ്‌തു"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"എല്ലായ്‌പ്പോഴും ഓണായിരിക്കുന്ന VPN പിശക്"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 728c3a12425b..c7d219133d8a 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"अनुप्रयोगास स्‍थापना सत्र वाचण्‍याची अनुमती देते. हे सक्रिय पॅकेज स्‍थापनांविषयी तपशील पाहाण्‍याची यास अनुमती देते."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"पॅकेज स्थापित करण्यासाठी विनंती करा"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"पॅकेजच्या स्थापना करण्यासाठी अनुप्रयोगास अनुमती देते."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"झूम नियंत्रणासाठी दोनदा टॅप करा"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट जोडू शकलो नाही."</string>
<string name="ime_action_go" msgid="8320845651737369027">"जा"</string>
<string name="ime_action_search" msgid="658110271822807811">"शोधा"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"सूचना रॅंकर सेवा"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN सक्रिय"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> द्वारे VPN सक्रिय केले आहे"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"नेटवर्क व्यवस्थापित करण्यासाठी टॅप करा."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> शी कनेक्ट केले. नेटवर्क व्यवस्थापित करण्यासाठी टॅप करा."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN कनेक्ट करणे नेहमी-चालू…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN कनेक्ट केलेले नेहमी-चालू"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"VPN त्रुटी नेहमी-चालू"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 0ac389837940..5dbf5174bafa 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"အပလီကေးရှင်းအား တပ်ဆင်ရေး ချိတ်ဆက်မှုများကို ဖတ်ခွင့်ပြုသည်။ ၎င်းသည် ဖွင့်သုံးနေသည့် အထုပ်အား တပ်ဆင်မှုဆိုင်ရာ အသေးိစတ်များကို ကြည့်ရှုခွင့် ပြုသည်။"</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"တပ်ဆင်ရေး အထုပ်များကို တောင်းဆိုပါ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ပက်ကေ့များ သွင်းယူခြင်းအတွက် တောင်းဆိုရန် အပ္ပလီကေးရှင်းအား ခွင့်ပြုပါ"</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ဇူးမ်အသုံးပြုရန် နှစ်ချက်တို့ပါ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ဝဒ်ဂျက်ထည့်လို့ မရပါ"</string>
<string name="ime_action_go" msgid="8320845651737369027">"သွားပါ"</string>
<string name="ime_action_search" msgid="658110271822807811">"ရှာဖွေခြင်း"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"သတိပေးချက် အဆင့်သတ်မှတ်ခြင်းဝန်ဆောင်မှု"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ဖွင့်ထားပါသည်"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g>မှVPNအလုပ်လုပ်နေသည်"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"ကွန်ရက်ကို စီမံခန့်ခွဲရန် တို့ပါ။"</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> သို့ ချိတ်ဆက်ထားသည်။ ကွန်ရက်ကို စီမံခန့်ခွဲရန် တို့ပါ။"</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"အမြဲတမ်းဖွင့်ထား VPN ဆက်သွယ်နေစဉ်…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"အမြဲတမ်းဖွင့်ထား VPN ဆက်သွယ်မှုရှိ"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"အမြဲတမ်းဖွင့်ထား VPN အမှား"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 5bb9f440db8e..a4e5b89d30bb 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ਇੱਕ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਇੰਸਟੌਲ ਸੈਸ਼ਨ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਸਨੂੰ ਸਕਿਰਿਆ ਪੈਕੇਜ ਇੰਸਟੌਲੇਸ਼ਨਾਂ ਬਾਰੇ ਵੇਰਵੇ ਦੇਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ਪੈਕੇਜ ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਬੇਨਤੀ ਕਰੋ"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ਪੈਕੇਜ ਦੀ ਸਥਾਪਨਾ ਦੀ ਬੇਨਤੀ ਕਰਨ ਲਈ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਅਨੁਮਤੀ ਦਿੰਦਾ ਹੈ"</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ਜ਼ੂਮ ਕੰਟਰੋਲ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ਵਿਜੇਟ ਨਹੀਂ ਜੋੜ ਸਕਿਆ।"</string>
<string name="ime_action_go" msgid="8320845651737369027">"ਜਾਓ"</string>
<string name="ime_action_search" msgid="658110271822807811">"ਖੋਜੋ"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"ਸੂਚਨਾ ਰੈਂਕਰ ਸੇਵਾ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ਸਕਿਰਿਆ ਕੀਤਾ"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> ਰਾਹੀਂ ਸਕਿਰਿਆ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"ਨੈੱਟਵਰਕ ਦੇ ਪ੍ਰਬੰਧਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ। ਨੈੱਟਵਰਕ ਦੇ ਪ੍ਰਬੰਧਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ਹਮੇਸ਼ਾਂ-ਚਾਲੂ VPN ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"ਹਮੇਸ਼ਾਂ-ਚਾਲੂ VPN ਕਨੈਕਟ ਕੀਤਾ"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"ਹਮੇਸ਼ਾਂ-ਚਾਲੂ VPN ਅਸ਼ੁੱਧੀ"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 558b97ac7af8..398193e311b6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1316,7 +1316,7 @@
<string name="storage_usb" msgid="3017954059538517278">"Armazenamento USB"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Aviso de utilização de dados"</string>
- <string name="data_usage_warning_body" msgid="6660692274311972007">"Toque para ver a utiliz. e def."</string>
+ <string name="data_usage_warning_body" msgid="6660692274311972007">"Toque para ver a utilização e definições"</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Limite de dados 2G/3G atingido"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Limite de dados 4G atingido"</string>
<string name="data_usage_mobile_limit_title" msgid="557158376602636112">"Limite de dados móveis atingido"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 1a237b95ff9f..9dcd8e6cc0c8 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Lejon një aplikacion të lexojë sesionet e instalimit. Kjo e lejon atë të shohë detaje rreth instalimeve të paketave aktive."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"kërko paketat e instalimit"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Lejon që një aplikacion të kërkojë instalimin e paketave."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Trokit dy herë për të kontrolluar zmadhimin"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nuk mundi të shtonte miniaplikacion."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Shko"</string>
<string name="ime_action_search" msgid="658110271822807811">"Kërko"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Shërbimi i klasifikimit të njoftimeve"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN-ja u aktivizua"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-ja është aktivizuar nga <xliff:g id="APP">%s</xliff:g>"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Trokit për të menaxhuar rrjetin."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"Lidhur me <xliff:g id="SESSION">%s</xliff:g>. Trokit për të menaxhuar rrjetin."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Po lidh VPN-në për aktivizim të përhershëm…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN e lidhur në mënyrë të përhershme"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Gabimi VPN-je për aktivizimin e përhershëm"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 47e2753ba56f..d2df81264fc8 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"நிறுவல் அமர்வுகளைப் படிக்க, பயன்பாட்டை அனுமதிக்கிறது. இது செயல்படும் தொகுப்பு நிறுவல்களைப் பற்றிய விவரங்களைப் பார்க்க அனுமதிக்கிறது."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"நிறுவல் தொகுப்புகளைக் கோருதல்"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"தொகுப்புகளின் நிறுவலைக் கோர, பயன்பாட்டை அனுமதிக்கும்."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"அளவை மாற்றுவதற்கான கட்டுப்பாட்டிற்கு, இருமுறை தட்டவும்"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"விட்ஜெட்டைச் சேர்க்க முடியவில்லை."</string>
<string name="ime_action_go" msgid="8320845651737369027">"செல்"</string>
<string name="ime_action_search" msgid="658110271822807811">"தேடு"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"அறிவிப்பை மதிப்பீடு செய்யும் சேவை"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN செயல்படுத்தப்பட்டது"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ஆல் VPN செயல்படுத்தப்பட்டது"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"நெட்வொர்க்கை நிர்வகிக்க, தட்டவும்."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> உடன் இணைக்கப்பட்டது. நெட்வொர்க்கை நிர்வகிக்க, தட்டவும்."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"எப்போதும் இயங்கும் VPN உடன் இணைக்கிறது…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"எப்போதும் இயங்கும் VPN இணைக்கப்பட்டது"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"எப்போதும் இயங்கும் VPN பிழை"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index f91c25aa6e39..1cfab6e68770 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ఇన్‌స్టాల్ సెషన్‌లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది సక్రియ ప్యాకేజీ ఇన్‌స్టాలేషన్‌ల గురించి వివరాలను చూడటానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ఇన్‌స్టాల్ ప్యాకేజీలను అభ్యర్థించడం"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ప్యాకేజీల ఇన్‌స్టాలేషన్ అభ్యర్థించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"విడ్జెట్‌ను జోడించడం సాధ్యపడలేదు."</string>
<string name="ime_action_go" msgid="8320845651737369027">"వెళ్లు"</string>
<string name="ime_action_search" msgid="658110271822807811">"శోధించు"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"నోటిఫికేషన్ ర్యాంకర్ సేవ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN సక్రియం చేయబడింది"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ద్వారా VPN సక్రియం చేయబడింది"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"నెట్‌వర్క్‌ను నిర్వహించడానికి నొక్కండి."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>కు కనెక్ట్ చేయబడింది. నెట్‌వర్క్‌ను నిర్వహించడానికి నొక్కండి."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ఎల్లప్పుడూ-ఆన్‌లో ఉండే VPN కనెక్ట్ చేయబడుతోంది…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"ఎల్లప్పుడూ-ఆన్‌లో ఉండే VPN కనెక్ట్ చేయబడింది"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"ఎల్లప్పుడూ-ఆన్‌లో ఉండే VPN లోపం"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index f916cf56e6e3..91a8c06e3a08 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ایک ایپلیکیشن کو انسٹال سیشنز پڑھنے کی اجازت دیتا ہے۔ یہ اسے فعال پیکیج انسٹالیشنز کے بارے میں تفصیلات دیکھنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"پیکجز انسٹال کرنے کی درخواست کریں"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ایک ایپلیکیشن کو پیکجز انسٹال کرنے کی اجازت دیتی ہے۔"</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"زوم کنٹرول کیلئے دوبار تھپتھپائیں"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ویجٹس کو شامل نہیں کرسکا۔"</string>
<string name="ime_action_go" msgid="8320845651737369027">"جائیں"</string>
<string name="ime_action_search" msgid="658110271822807811">"تلاش کریں"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"اطلاع کی درجہ بندی سروس"</string>
<string name="vpn_title" msgid="19615213552042827">"‏VPN فعال ہوگیا"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"‏<xliff:g id="APP">%s</xliff:g> کے ذریعہ VPN فعال ہے"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"نیٹ ورک نظم کرنے کیلئے تھپتھپائیں۔"</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> سے منسلک ہے۔ نیٹ ورک کا نظم کرنے کیلئے تھپتھپائیں۔"</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"‏ہمیشہ آن VPN مربوط ہو رہا ہے…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"‏ہمیشہ آن VPN مربوط ہوگیا"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"‏ہمیشہ آن VPN کی خرابی"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 2b28e59cf980..ed190a4e37b9 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1195,8 +1195,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ilovaga o‘rnatilgan seanslarni o‘qish uchun ruxsat beradi. Bu unga faol paket o‘rnatmalari haqidagi ma’lumotlarni ko‘rish imkonini beradi."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"paketlarni o‘rnatish so‘rovini yuborish"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ilovaga paketlarni o‘rnatish so‘rovini yuborish imkonini beradi."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Ko‘lamini o‘zgartirish uchun ikki marta bosing"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidjet qo‘shilmadi."</string>
<string name="ime_action_go" msgid="8320845651737369027">"O‘tish"</string>
<string name="ime_action_search" msgid="658110271822807811">"Qidirish"</string>
@@ -1227,10 +1226,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Bildirishnomalarni baholash xizmati"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN faollashtirildi"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> tomonidan faollashtirilgan"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Tarmoq sozlamalarini o‘zgartirish uchun bu yerni bosing."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> ulandi. Tarmoq sozlamalarini o‘zgartirish uchun bu yerni bosing."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ulanmoqda…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ulandi"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Xato"</string>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 998eea5ac9ab..a77b9512dbef 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -317,6 +317,7 @@ please see themes_device_defaults.xml.
<item name="activityChooserViewStyle">@style/Widget.ActivityChooserView</item>
<item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.MediaRouteButton</item>
<item name="fragmentBreadCrumbsStyle">@style/Widget.FragmentBreadCrumbs</item>
+ <item name="contextPopupMenuStyle">?attr/popupMenuStyle</item>
<!-- Preference styles -->
<item name="preferenceScreenStyle">@style/Preference.PreferenceScreen</item>
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 6e1cc23193d8..774339a964db 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -1160,7 +1160,7 @@ redirects:
- from: /r/studio-ui/avd-manager.html
to: /studio/run/managing-avds.html
- from: /r/studio-ui/rundebugconfig.html
- to: /studio/run/index.html
+ to: /studio/run/rundebugconfig.html
- from: /r/studio-ui/devicechooser.html
to: /studio/run/emulator.html
- from: /r/studio-ui/virtualdeviceconfig.html
diff --git a/docs/html/distribute/googleplay/guide.jd b/docs/html/distribute/googleplay/guide.jd
index 6cb8cc0a68c2..293ccae4a234 100644
--- a/docs/html/distribute/googleplay/guide.jd
+++ b/docs/html/distribute/googleplay/guide.jd
@@ -1,43 +1,81 @@
page.title=Find Success on Google Play
-page.metaDescription=The updated guide that helps you find success with your app or game business on Google Play.
-page.tags="play,protips"
-page.timestamp=1447437450
-meta.tags="secrets, success, play, google"
-page.image=distribute/images/play_dev_guide.png
+page.metaDescription=Stay up to date with features, best practices, and strategies to help you grow your business and find success on Google Play.
+page.tags="playbook,play,protips"
+page.timestamp=1466793455
+meta.tags="playbook,play,google"
+page.image=images/cards/card-secrets-playbook_2x.jpg
@jd:body
+<div class="figure-right">
+ <img src="{@docRoot}images/gp-secrets-playbook.png"
+ style="width:180px" />
+ <div style="text-align:center">
+ <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&utm_source=dac&utm_medium=page&utm_campaign=evergreen&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1">
+ <img alt="Get it on Google Play"
+ src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
+ style="height:60px" />
+ </a>
+ </div>
+</div>
<p>
- We’ve created a downloadable guide to help you find success with your app or
- game business on Google Play. In it, you’ll find features, tips, and best
- practices to help you build an effective strategy to improve the quality,
- reach, retention, and monetization of your apps and games.
+ With the Playbook app for developers you can stay on top of the
+ features and best practices you can use to grow your app or game
+ business on Google Play.
</p>
+<ul>
+ <li>Choose topics relating to your business objectives to personalize
+ <strong>My Playbook</strong> with curated articles and videos from
+ Google, and from experts across the web.</li>
+ <li><strong>Explore</strong> the in-depth guide to Google’s developer
+ products, grouped by what you’re trying to do: develop, launch, engage,
+ grow, earn. </li>
+ <li>Take actions on items &mdash; complete, share, or dismiss them &mdash; and read
+ your <strong>Saved</strong> articles later, including offline if they’re
+ written in the app.</li>
+</ul>
+
+<p>
+The app is available in the following languages: <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&utm_source=dac&utm_medium=page&utm_campaign=evergreen&hl=en">English</a>,
+<a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=id&utm_source=dac&utm_medium=page&utm_campaign=id">Bahasa
+Indonesia</a>, <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=de&utm_source=dac&utm_medium=page&utm_campaign=de">Deutsch</a>,
+<a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=es-419&utm_source=dac&utm_medium=page&utm_campaign=es-419">español
+(Latinoamérica)</a>, <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=fr&utm_source=dac&utm_medium=page&utm_campaign=fr">le
+français</a>, <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=pt-BR&utm_source=dac&utm_medium=page&utm_campaign=pr-BR">português
+do Brasil</a>, <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=vi&utm_source=dac&utm_medium=page&utm_campaign=vi">tiếng
+Việt</a>, <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=ru&utm_source=dac&utm_medium=page&utm_campaign=ru">русский
+язы́к</a>, <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=ko&utm_source=dac&utm_medium=page&utm_campaign=ko">한국어</a>,
+<a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=zh-CN&utm_source=dac&utm_medium=page&utm_campaign=zh-CN">中文
+(简体)</a>, <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=zh-TW&utm_source=dac&utm_medium=page&utm_campaign=zh-TW">中文
+(繁體)</a>, and <a
+href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&hl=ja&utm_source=dac&utm_medium=page&utm_campaign=ja">日本語</a>.
+</p>
+
+<p>The Playbook app replaces the
+<a href="https://play.google.com/store/books/details?id=O2a5CgAAQBAJ">Secrets to
+App Success on Google Play</a> guides, which you can still read on Google Play Books.</p>
-<a href="https://play.google.com/store/books/details?id=O2a5CgAAQBAJ&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1">
- <img src="{@docRoot}images/distribute/secrets_v2_banner.jpg">
-</a>
<div style="text-align:center">
- <a href="https://play.google.com/store/books/details?id=O2a5CgAAQBAJ&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1">
- <img alt="Get it on Google Play"
- src="https://play.google.com/intl/en_us/badges/images/books/en-play-badge-border.png"
- style="height:60px" />
+ <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&utm_source=dac&utm_medium=page&utm_campaign=evergreen&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1">
+ <img src="{@docRoot}images/gp-secrets-playbook-lg.png" style="padding-top:1em;" />
</a>
+ <div style="text-align:center">
+ <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.secrets&utm_source=dac&utm_medium=page&utm_campaign=evergreen&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1">
+ <img alt="Get it on Google Play"
+ src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
+ style="height:60px" />
+ </a>
+ </div>
</div>
-
-<p><a
- href="https://docs.google.com/forms/d/1KFE9D7NlOrxM_jzmyMeZGaczgg1xo57jBoGq0R5nnsU/viewform">Sign
- up to be notified</a> when the guide is released in the following languages:
- Bahasa Indonesia, Deutsch, español (Latinoamérica), le français, português do
- Brasil, <span style="white-space: nowrap;">tiếng Việt</span>, <span style="white-space:
- nowrap;">русский язы́к</span>, <span style="white-space: nowrap;">ไทย</span>,
- <span style="white-space: nowrap;">한국어</span>, <span style="white-space: nowrap;">中文
- (简体)</span>, <span style="white-space: nowrap;">中文 (繁體)</span>, and <span style="white-space:
- nowrap;">日本語</span>.
-</p>
-
-<p>You can also <a
- href="{@docRoot}shareables/distribute/secrets_play/v2/web/secrets_to_app_success_v2_en.pdf">download
- the pdf</a>.
-</p>
diff --git a/docs/html/images/cards/card-secrets-playbook_2x.jpg b/docs/html/images/cards/card-secrets-playbook_2x.jpg
new file mode 100644
index 000000000000..87e6c8c37124
--- /dev/null
+++ b/docs/html/images/cards/card-secrets-playbook_2x.jpg
Binary files differ
diff --git a/docs/html/images/gp-secrets-playbook-lg.png b/docs/html/images/gp-secrets-playbook-lg.png
new file mode 100644
index 000000000000..08b2a338b0e6
--- /dev/null
+++ b/docs/html/images/gp-secrets-playbook-lg.png
Binary files differ
diff --git a/docs/html/images/gp-secrets-playbook.png b/docs/html/images/gp-secrets-playbook.png
new file mode 100644
index 000000000000..831376b9aef2
--- /dev/null
+++ b/docs/html/images/gp-secrets-playbook.png
Binary files differ
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index 6295e0efd00c..5e271b978580 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -3758,8 +3758,8 @@ METADATA['en'].carousel = {
},
"distribute/googleplay/guide.html": {
"image": "images/distribute/hero-secrets-to-app-success.jpg",
- "title": "Secrets to App Success on Google Play",
- "summary": "Get the updated guide full of useful features, tips, and best practices that will help you grow a successful app or game business on Google Play.",
+ "title": "Playbook for Developers",
+ "summary": "Stay up to date with features, best practices, and strategies to help you grow your business and find success on Google Play.",
},
"about/versions/lollipop.html": {
"image": "images/home/hero-lollipop_2x.png",
diff --git a/docs/html/ndk/downloads/index.jd b/docs/html/ndk/downloads/index.jd
index 954b049798c8..28860b28894c 100644
--- a/docs/html/ndk/downloads/index.jd
+++ b/docs/html/ndk/downloads/index.jd
@@ -361,7 +361,7 @@ $('#Downloads').after($('#download-table'));
documentation</a>.
</li>
- <li>Removed all sysroots for platform levels prior to Android 2.3 (API level 10).
+ <li>Removed all sysroots for platform levels prior to Android 2.3 (API level 9).
We dropped support for them in NDK r11, but neglected to actually remove them.
</li>
diff --git a/docs/html/ndk/guides/setup.jd b/docs/html/ndk/guides/setup.jd
index 1459bfefffa4..d3ace07a1c4b 100644
--- a/docs/html/ndk/guides/setup.jd
+++ b/docs/html/ndk/guides/setup.jd
@@ -34,8 +34,7 @@ information on that topic, see the
<p>To install and configure the NDK, follow these steps:</p>
<ol type="1">
<li>Get and install the <a href="{@docRoot}studio/index.html">Android SDK</a>.</li>
-<li><a href="{@docRoot}ndk/downloads/index.html">Download</a> and
-<a href="{@docRoot}ndk/downloads/index.html#extract">extract</a> the NDK,
+<li><a href="{@docRoot}ndk/downloads/index.html">Download</a> the NDK,
making sure to download the correct version for your development platform. You may place the
unzipped directory anywhere on your local drive.</li>
<li>Update your {@code PATH} environment variable with the location of the directory that
diff --git a/docs/html/preview/features/picture-in-picture.jd b/docs/html/preview/features/picture-in-picture.jd
index c089feb17670..03a17682a0a4 100644
--- a/docs/html/preview/features/picture-in-picture.jd
+++ b/docs/html/preview/features/picture-in-picture.jd
@@ -220,7 +220,11 @@ in any area that can be obscured by the PIP window.</p>
<p>When an activity is in PIP mode, by default it doesn't get input focus. To
receive input events while in PIP mode, use
-<code>MediaSession.setMediaButtonReceiver()</code>.</p>
+{@link android.media.session.MediaSession#setCallback
+MediaSession.setCallback()}. For more information on using
+{@link android.media.session.MediaSession#setCallback setCallback()} see
+<a href="{@docRoot}training/tv/playback/now-playing.html">Displaying
+a Now Playing Card</a>.</p>
<p>When your app is in PIP mode, video playback in the PIP window can cause
audio interference with another app, such as a music player app or voice search
@@ -228,4 +232,4 @@ app. To avoid this, request audio focus when you start playing the video,
and handle audio focus change notifications, as described in
<a href="{@docRoot}training/managing-audio/audio-focus.html">Managing Audio
Focus</a>. If you receive notification of audio focus loss when in PIP mode,
-pause or stop video playback.</p> \ No newline at end of file
+pause or stop video playback.</p>
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index b8a5ce6686b4..37d9d0e749e6 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -946,6 +946,7 @@ void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
Rect dstRect(op.unmappedBounds);
boundsTransform.mapRect(dstRect);
+ dstRect.roundOut();
dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());
if (dstRect.isEmpty()) {
diff --git a/libs/hwui/LayerUpdateQueue.cpp b/libs/hwui/LayerUpdateQueue.cpp
index db5f676d09dc..95f5cfb33474 100644
--- a/libs/hwui/LayerUpdateQueue.cpp
+++ b/libs/hwui/LayerUpdateQueue.cpp
@@ -26,6 +26,7 @@ void LayerUpdateQueue::clear() {
}
void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damage) {
+ damage.roundOut();
damage.doIntersect(0, 0, renderNode->getWidth(), renderNode->getHeight());
if (!damage.isEmpty()) {
for (Entry& entry : mEntries) {
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index af1fbd8254f9..af54e079daab 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -1007,6 +1007,40 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
EXPECT_EQ(4, renderer.getIndex());
}
+RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
+ class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
+ public:
+ void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
+ << "Bounds rect should round out";
+ }
+ void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {}
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {}
+ void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
+ << "Bounds rect should round out";
+ }
+ };
+
+ auto node = TestUtils::createNode(0, 0, 200, 200,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
+ 128, (SaveFlags::Flags)(0));
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.restore();
+ });
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
+ SaveLayerUnclippedRoundTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
+}
+
RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
public:
diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
index 8b0e91c77396..4db1cb935902 100644
--- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
@@ -39,17 +39,21 @@ static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) {
TEST(LayerUpdateQueue, enqueueSimple) {
sp<RenderNode> a = createSyncedNode(100, 100);
sp<RenderNode> b = createSyncedNode(200, 200);
+ sp<RenderNode> c = createSyncedNode(200, 200);
LayerUpdateQueue queue;
queue.enqueueLayerWithDamage(a.get(), Rect(25, 25, 75, 75));
queue.enqueueLayerWithDamage(b.get(), Rect(100, 100, 300, 300));
+ queue.enqueueLayerWithDamage(c.get(), Rect(.5, .5, .5, .5));
- EXPECT_EQ(2u, queue.entries().size());
+ EXPECT_EQ(3u, queue.entries().size());
EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
+ EXPECT_EQ(c.get(), queue.entries()[2].renderNode);
+ EXPECT_EQ(Rect(0, 0, 1, 1), queue.entries()[2].damage); // rounded out
}
TEST(LayerUpdateQueue, enqueueUnion) {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 4d2cf250d38c..542dcedb007c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -125,6 +125,77 @@ import java.util.Map;
All video codecs support flexible YUV 4:2:0 buffers since {@link
android.os.Build.VERSION_CODES#LOLLIPOP_MR1}.
+ <h4>Accessing Raw Video ByteBuffers on Older Devices</h4>
+ <p>
+ Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP} and {@link Image} support, you need to
+ use the {@link MediaFormat#KEY_STRIDE} and {@link MediaFormat#KEY_SLICE_HEIGHT} output format
+ values to understand the layout of the raw output buffers.
+ <p class=note>
+ Note that on some devices the slice-height is advertised as 0. This could mean either that the
+ slice-height is the same as the frame height, or that the slice-height is the frame height
+ aligned to some value (usually a power of 2). Unfortunately, there is no way to tell the actual
+ slice height in this case. Furthermore, the vertical stride of the {@code U} plane in planar
+ formats is also not specified or defined, though usually it is half of the slice height.
+ <p>
+ The {@link MediaFormat#KEY_WIDTH} and {@link MediaFormat#KEY_HEIGHT} keys specify the size of the
+ video frames; however, for most encondings the video (picture) only occupies a portion of the
+ video frame. This is represented by the 'crop rectangle'.
+ <p>
+ You need to use the following keys to get the crop rectangle of raw output images from the
+ {@linkplain #getOutputFormat output format}. If these keys are not present, the video occupies the
+ entire video frame.The crop rectangle is understood in the context of the output frame
+ <em>before</em> applying any {@linkplain MediaFormat#KEY_ROTATION rotation}.
+ <table style="width: 0%">
+ <thead>
+ <tr>
+ <th>Format Key</th>
+ <th>Type</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{@code "crop-left"}</td>
+ <td>Integer</td>
+ <td>The left-coordinate (x) of the crop rectangle</td>
+ </tr><tr>
+ <td>{@code "crop-top"}</td>
+ <td>Integer</td>
+ <td>The top-coordinate (y) of the crop rectangle</td>
+ </tr><tr>
+ <td>{@code "crop-right"}</td>
+ <td>Integer</td>
+ <td>The right-coordinate (x) <strong>MINUS 1</strong> of the crop rectangle</td>
+ </tr><tr>
+ <td>{@code "crop-bottom"}</td>
+ <td>Integer</td>
+ <td>The bottom-coordinate (y) <strong>MINUS 1</strong> of the crop rectangle</td>
+ </tr><tr>
+ <td colspan=3>
+ The right and bottom coordinates can be understood as the coordinates of the right-most
+ valid column/bottom-most valid row of the cropped output image.
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <p>
+ The size of the video frame (before rotation) can be calculated as such:
+ <pre class=prettyprint>
+ MediaFormat format = decoder.getOutputFormat(&hellip;);
+ int width = format.getInteger(MediaFormat.KEY_WIDTH);
+ if (format.containsKey("crop-left") && format.containsKey("crop-right")) {
+ width = format.getInteger("crop-right") + 1 - format.getInteger("crop-left");
+ }
+ int height = format.getInteger(MediaFormat.KEY_HEIGHT);
+ if (format.containsKey("crop-top") && format.containsKey("crop-bottom")) {
+ height = format.getInteger("crop-bottom") + 1 - format.getInteger("crop-top");
+ }
+ </pre>
+ <p class=note>
+ Also note that the meaning of {@link BufferInfo#offset BufferInfo.offset} was not consistent across
+ devices. On some devices the offset pointed to the top-left pixel of the crop rectangle, while on
+ most devices it pointed to the top-left pixel of the entire frame.
+
<h3>States</h3>
<p>
During its life a codec conceptually exists in one of three states: Stopped, Executing or
@@ -542,6 +613,32 @@ import java.util.Map;
Also since {@link android.os.Build.VERSION_CODES#M}, you can change the output Surface
dynamically using {@link #setOutputSurface setOutputSurface}.
+ <h4>Transformations When Rendering onto Surface</h4>
+
+ If the codec is configured into Surface mode, any crop rectangle, {@linkplain
+ MediaFormat#KEY_ROTATION rotation} and {@linkplain #setVideoScalingMode video scaling
+ mode} will be automatically applied with one exception:
+ <p class=note>
+ Prior to the {@link android.os.Build.VERSION_CODES#M} release, software decoders may not
+ have applied the rotation when being rendered onto a Surface. Unfortunately, there is no way to
+ identify software decoders, or if they apply the rotation other than by trying it out.
+ <p>
+ There are also some caveats.
+ <p class=note>
+ Note that the pixel aspect ratio is not considered when displaying the output onto the
+ Surface. This means that if you are using {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT} mode, you
+ must position the output Surface so that it has the proper final display aspect ratio. Conversely,
+ you can only use {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} mode for content with
+ square pixels (pixel aspect ratio or 1:1).
+ <p class=note>
+ Note also that as of {@link android.os.Build.VERSION_CODES#N} release, {@link
+ #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} mode may not work correctly for videos rotated
+ by 90 or 270 degrees.
+ <p class=note>
+ When setting the video scaling mode, note that it must be reset after each time the output
+ buffers change. Since the {@link #INFO_OUTPUT_BUFFERS_CHANGED} event is deprecated, you can
+ do this after each time the output format changes.
+
<h4>Using an Input Surface</h4>
<p>
When using an input Surface, there are no accessible input buffers, as buffers are automatically
@@ -2991,7 +3088,13 @@ final public class MediaCodec {
/**
* The content is scaled, maintaining its aspect ratio, the whole
- * surface area is used, content may be cropped
+ * surface area is used, content may be cropped.
+ * <p class=note>
+ * This mode is only suitable for content with 1:1 pixel aspect ratio as you cannot
+ * configure the pixel aspect ratio for a {@link Surface}.
+ * <p class=note>
+ * As of {@link android.os.Build.VERSION_CODES#N} release, this mode may not work if
+ * the video is {@linkplain MediaFormat#KEY_ROTATION rotated} by 90 or 270 degrees.
*/
public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
@@ -3006,10 +3109,15 @@ final public class MediaCodec {
/**
* If a surface has been specified in a previous call to {@link #configure}
* specifies the scaling mode to use. The default is "scale to fit".
- * <p class=note>The scaling mode may be reset to the <strong>default</strong> each time an
+ * <p class=note>
+ * The scaling mode may be reset to the <strong>default</strong> each time an
* {@link #INFO_OUTPUT_BUFFERS_CHANGED} event is received from the codec; therefore, the client
* must call this method after every buffer change event (and before the first output buffer is
- * released for rendering) to ensure consistent scaling mode.</p>
+ * released for rendering) to ensure consistent scaling mode.
+ * <p class=note>
+ * Since the {@link #INFO_OUTPUT_BUFFERS_CHANGED} event is deprecated, this can also be done
+ * after each {@link #INFO_OUTPUT_FORMAT_CHANGED} event.
+ *
* @throws IllegalArgumentException if mode is not recognized.
* @throws IllegalStateException if in the Released state.
*/
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 07d1f7542b26..0bfeaed36282 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -541,6 +541,72 @@ public final class MediaCodecInfo {
* frame rate}. Use
* <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
* to clear any existing frame rate setting in the format.
+ * <p>
+ *
+ * The following table summarizes the format keys considered by this method.
+ *
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th rowspan=3>OS Version(s)</th>
+ * <td colspan=3>{@code MediaFormat} keys considered for</th>
+ * </tr><tr>
+ * <th>Audio Codecs</th>
+ * <th>Video Codecs</th>
+ * <th>Encoders</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</th>
+ * <td rowspan=3>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br>
+ * {@link MediaFormat#KEY_SAMPLE_RATE},<br>
+ * {@link MediaFormat#KEY_CHANNEL_COUNT},</td>
+ * <td>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br>
+ * {@link CodecCapabilities#FEATURE_AdaptivePlayback}<sup>D</sup>,<br>
+ * {@link CodecCapabilities#FEATURE_SecurePlayback}<sup>D</sup>,<br>
+ * {@link CodecCapabilities#FEATURE_TunneledPlayback}<sup>D</sup>,<br>
+ * {@link MediaFormat#KEY_WIDTH},<br>
+ * {@link MediaFormat#KEY_HEIGHT},<br>
+ * <strong>no</strong> {@code KEY_FRAME_RATE}</td>
+ * <td rowspan=4>{@link MediaFormat#KEY_BITRATE_MODE},<br>
+ * {@link MediaFormat#KEY_PROFILE}
+ * (and/or {@link MediaFormat#KEY_AAC_PROFILE}<sup>~</sup>),<br>
+ * <!-- {link MediaFormat#KEY_QUALITY},<br> -->
+ * {@link MediaFormat#KEY_COMPLEXITY}
+ * (and/or {@link MediaFormat#KEY_FLAC_COMPRESSION_LEVEL}<sup>~</sup>)</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</th>
+ * <td rowspan=2>as above, plus<br>
+ * {@link MediaFormat#KEY_FRAME_RATE}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#M}</th>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#N}</th>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#KEY_PROFILE},<br>
+ * <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
+ * {@link MediaFormat#KEY_BIT_RATE}</td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#KEY_PROFILE},<br>
+ * {@link MediaFormat#KEY_LEVEL}<sup>+</sup>,<br>
+ * <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
+ * {@link MediaFormat#KEY_BIT_RATE},<br>
+ * {@link CodecCapabilities#FEATURE_IntraRefresh}<sup>E</sup></td>
+ * </tr>
+ * <tr>
+ * <td colspan=4>
+ * <p class=note><strong>Notes:</strong><br>
+ * *: must be specified; otherwise, method returns {@code false}.<br>
+ * +: method does not verify that the format parameters are supported
+ * by the specified level.<br>
+ * D: decoders only<br>
+ * E: encoders only<br>
+ * ~: if both keys are provided values must match
+ * </td>
+ * </tr>
+ * </tbody>
+ * </table>
*
* @param format media format with optional feature directives.
* @throws IllegalArgumentException if format is not a valid media format.
diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java
index 42ce5110f134..3cb4cbe99a4b 100644
--- a/media/java/android/media/MediaCodecList.java
+++ b/media/java/android/media/MediaCodecList.java
@@ -201,6 +201,9 @@ final public class MediaCodecList {
* <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
* to clear any existing frame rate setting in the format.
*
+ * @see MediaCodecList.CodecCapabilities.isFormatSupported for format keys
+ * considered per android versions when evaluating suitable codecs.
+ *
* @param format A decoder media format with optional feature directives.
* @throws IllegalArgumentException if format is not a valid media format.
* @throws NullPointerException if format is null.
@@ -222,6 +225,9 @@ final public class MediaCodecList {
* <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>
* to clear any existing frame rate setting in the format.
*
+ * @see MediaCodecList.CodecCapabilities.isFormatSupported for format keys
+ * considered per android versions when evaluating suitable codecs.
+ *
* @param format An encoder media format with optional feature directives.
* @throws IllegalArgumentException if format is not a valid media format.
* @throws NullPointerException if format is null.
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 24a400e48189..6f5199b6959c 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -333,7 +333,113 @@ final public class MediaExtractor {
/**
* Get the track format at the specified index.
+ *
* More detail on the representation can be found at {@link android.media.MediaCodec}
+ * <p>
+ * The following table summarizes support for format keys across android releases:
+ *
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th rowspan=2>OS Version(s)</th>
+ * <td colspan=3>{@code MediaFormat} keys used for</th>
+ * </tr><tr>
+ * <th>All Tracks</th>
+ * <th>Audio Tracks</th>
+ * <th>Video Tracks</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN}</td>
+ * <td rowspan=8>{@link MediaFormat#KEY_MIME},<br>
+ * {@link MediaFormat#KEY_DURATION},<br>
+ * {@link MediaFormat#KEY_MAX_INPUT_SIZE}</td>
+ * <td rowspan=5>{@link MediaFormat#KEY_SAMPLE_RATE},<br>
+ * {@link MediaFormat#KEY_CHANNEL_COUNT},<br>
+ * {@link MediaFormat#KEY_CHANNEL_MASK},<br>
+ * gapless playback information<sup>.mp3, .mp4</sup>,<br>
+ * {@link MediaFormat#KEY_IS_ADTS}<sup>AAC if streaming</sup>,<br>
+ * codec-specific data<sup>AAC, Vorbis</sup></td>
+ * <td rowspan=2>{@link MediaFormat#KEY_WIDTH},<br>
+ * {@link MediaFormat#KEY_HEIGHT},<br>
+ * codec-specific data<sup>AVC, MPEG4</sup></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
+ * <td rowspan=3>as above, plus<br>
+ * Pixel aspect ratio information<sup>AVC, *</sup></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
+ * <td rowspan=2>as above, plus<br>
+ * {@link MediaFormat#KEY_BIT_RATE}<sup>AAC</sup>,<br>
+ * codec-specific data<sup>Opus</sup></td>
+ * <td rowspan=2>as above, plus<br>
+ * {@link MediaFormat#KEY_ROTATION}<sup>.mp4</sup>,<br>
+ * {@link MediaFormat#KEY_BIT_RATE}<sup>MPEG4</sup>,<br>
+ * codec-specific data<sup>HEVC</sup></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#M}</td>
+ * <td>as above, plus<br>
+ * gapless playback information<sup>Opus</sup></td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#KEY_FRAME_RATE} (integer)</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#N}</td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#KEY_TRACK_ID},<br>
+ * <!-- {link MediaFormat#KEY_MAX_BIT_RATE}<sup>#, .mp4</sup>,<br> -->
+ * {@link MediaFormat#KEY_BIT_RATE}<sup>#, .mp4</sup></td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#KEY_PCM_ENCODING},<br>
+ * {@link MediaFormat#KEY_PROFILE}<sup>AAC</sup></td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#KEY_HDR_STATIC_INFO}<sup>#, .webm</sup>,<br>
+ * {@link MediaFormat#KEY_COLOR_STANDARD}<sup>#</sup>,<br>
+ * {@link MediaFormat#KEY_COLOR_TRANSFER}<sup>#</sup>,<br>
+ * {@link MediaFormat#KEY_COLOR_RANGE}<sup>#</sup>,<br>
+ * {@link MediaFormat#KEY_PROFILE}<sup>MPEG2, H.263, MPEG4, AVC, HEVC, VP9</sup>,<br>
+ * {@link MediaFormat#KEY_LEVEL}<sup>H.263, MPEG4, AVC, HEVC, VP9</sup>,<br>
+ * codec-specific data<sup>VP9</sup></td>
+ * </tr>
+ * <tr>
+ * <td colspan=4>
+ * <p class=note><strong>Notes:</strong><br>
+ * #: container-specified value only.<br>
+ * .mp4, .webm&hellip;: for listed containers<br>
+ * MPEG4, AAC&hellip;: for listed codecs
+ * </td>
+ * </tr><tr>
+ * <td colspan=4>
+ * <p class=note>Note that that level information contained in the container many times
+ * does not match the level of the actual bitstream. You may want to clear the level using
+ * {@code MediaFormat.setString(KEY_LEVEL, null)} before using the track format to find a
+ * decoder that can play back a particular track.
+ * </td>
+ * </tr><tr>
+ * <td colspan=4>
+ * <p class=note><strong>*Pixel (sample) aspect ratio</strong> is returned in the following
+ * keys. The display width can be calculated for example as:
+ * <p align=center>
+ * display-width = display-height * crop-width / crop-height * sar-width / sar-height
+ * </td>
+ * </tr><tr>
+ * <th>Format Key</th><th>Value Type</th><th colspan=2>Description</th>
+ * </tr><tr>
+ * <td>{@code "sar-width"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio width</td>
+ * </tr><tr>
+ * <td>{@code "sar-height"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio height</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
*/
@NonNull
public MediaFormat getTrackFormat(int index) {
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 33e39575fdac..d7a18d97874d 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -295,17 +295,19 @@ public final class MediaFormat {
* Stride (or row increment) is the difference between the index of a pixel
* and that of the pixel directly underneath. For YUV 420 formats, the
* stride corresponds to the Y plane; the stride of the U and V planes can
- * be calculated based on the color format.
+ * be calculated based on the color format, though it is generally undefined
+ * and depends on the device and release.
* The associated value is an integer, representing number of bytes.
*/
public static final String KEY_STRIDE = "stride";
/**
* A key describing the plane height of a multi-planar (YUV) video bytebuffer layout.
- * Slice height (or plane height) is the number of rows that must be skipped to get
- * from the top of the Y plane to the top of the U plane in the bytebuffer. In essence
+ * Slice height (or plane height/vertical stride) is the number of rows that must be skipped
+ * to get from the top of the Y plane to the top of the U plane in the bytebuffer. In essence
* the offset of the U plane is sliceHeight * stride. The height of the U/V planes
- * can be calculated based on the color format.
+ * can be calculated based on the color format, though it is generally undefined
+ * and depends on the device and release.
* The associated value is an integer, representing number of rows.
*/
public static final String KEY_SLICE_HEIGHT = "slice-height";
@@ -552,7 +554,9 @@ public final class MediaFormat {
/**
* A key describing the desired clockwise rotation on an output surface.
* This key is only used when the codec is configured using an output surface.
- * The associated value is an integer, representing degrees.
+ * The associated value is an integer, representing degrees. Supported values
+ * are 0, 90, 180 or 270. This is an optional field; if not specified, rotation
+ * defaults to 0.
*
* @see MediaCodecInfo.CodecCapabilities#profileLevels
*/
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 7117fbdbf122..e481aa141d6b 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -266,6 +266,121 @@ final public class MediaMuxer {
/**
* Adds a track with the specified format.
+ * <p>
+ * The following table summarizes support for specific format keys across android releases.
+ * Keys marked with '+:' are required.
+ *
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th rowspan=2>OS Version(s)</th>
+ * <td colspan=3>{@code MediaFormat} keys used for</th>
+ * </tr><tr>
+ * <th>All Tracks</th>
+ * <th>Audio Tracks</th>
+ * <th>Video Tracks</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
+ * <td rowspan=7>+: {@link MediaFormat#KEY_MIME}</td>
+ * <td rowspan=3>+: {@link MediaFormat#KEY_SAMPLE_RATE},<br>
+ * +: {@link MediaFormat#KEY_CHANNEL_COUNT},<br>
+ * +: <strong>codec-specific data<sup>AAC</sup></strong></td>
+ * <td rowspan=5>+: {@link MediaFormat#KEY_WIDTH},<br>
+ * +: {@link MediaFormat#KEY_HEIGHT},<br>
+ * no {@code KEY_ROTATION},
+ * use {@link #setOrientationHint setOrientationHint()}<sup>.mp4</sup>,<br>
+ * +: <strong>codec-specific data<sup>AVC, MPEG4</sup></strong></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
+ * <td rowspan=4>as above, plus<br>
+ * +: <strong>codec-specific data<sup>Vorbis & .webm</sup></strong></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#M}</td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#KEY_BIT_RATE}<sup>AAC</sup></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#N}</td>
+ * <td>as above, plus<br>
+ * <!-- {link MediaFormat#KEY_MAX_BIT_RATE}<sup>AAC, MPEG4</sup>,<br> -->
+ * {@link MediaFormat#KEY_BIT_RATE}<sup>MPEG4</sup>,<br>
+ * {@link MediaFormat#KEY_HDR_STATIC_INFO}<sup>#, .webm</sup>,<br>
+ * {@link MediaFormat#KEY_COLOR_STANDARD}<sup>#</sup>,<br>
+ * {@link MediaFormat#KEY_COLOR_TRANSFER}<sup>#</sup>,<br>
+ * {@link MediaFormat#KEY_COLOR_RANGE}<sup>#</sup>,<br>
+ * +: <strong>codec-specific data<sup>HEVC</sup></strong>,<br>
+ * codec-specific data<sup>VP9</sup></td>
+ * </tr>
+ * <tr>
+ * <td colspan=4>
+ * <p class=note><strong>Notes:</strong><br>
+ * #: storing into container metadata.<br>
+ * .mp4, .webm&hellip;: for listed containers<br>
+ * MPEG4, AAC&hellip;: for listed codecs
+ * </td>
+ * </tr><tr>
+ * <td colspan=4>
+ * <p class=note>Note that the codec-specific data for the track must be specified using
+ * this method. Furthermore, codec-specific data must not be passed/specified via the
+ * {@link #writeSampleData writeSampleData()} call.
+ * </td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <p>
+ * The following table summarizes codec support for containers across android releases:
+ *
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th rowspan=2>OS Version(s)</th>
+ * <td colspan=3>Codec support</th>
+ * </tr><tr>
+ * <th>{@linkplain OutputFormat#MUXER_OUTPUT_MPEG_4 MP4}</th>
+ * <th>{@linkplain OutputFormat#MUXER_OUTPUT_WEBM WEBM}</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
+ * <td rowspan=6>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC},<br>
+ * {@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR},<br>
+ * {@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR},<br>
+ * {@link MediaFormat#MIMETYPE_VIDEO_H263 H.263},<br>
+ * {@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4},<br>
+ * {@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td>
+ * <td rowspan=3>Not supported</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
+ * <td rowspan=3>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis},<br>
+ * {@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#M}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#N}</td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td>
+ * <td>as above, plus<br>
+ * {@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
* @param format The media format for the track. This must not be an empty
* MediaFormat.
* @return The track index for this newly added track, and it should be used
diff --git a/packages/EasterEgg/Android.mk b/packages/EasterEgg/Android.mk
new file mode 100644
index 000000000000..df081f4091d9
--- /dev/null
+++ b/packages/EasterEgg/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+ android-support-v13 \
+ android-support-v7-recyclerview \
+ android-support-v7-preference \
+ android-support-v7-appcompat \
+ android-support-v14-preference \
+ jsr305
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := EasterEgg
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
new file mode 100644
index 000000000000..50e8b5ca7d2b
--- /dev/null
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.egg"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="24" />
+
+ <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application android:label="@string/app_name" android:icon="@drawable/icon">
+ <!-- Long press the QS tile to get here -->
+ <activity android:name=".neko.NekoLand"
+ android:theme="@android:style/Theme.Material.NoActionBar"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
+ </intent-filter>
+ </activity>
+
+ <!-- This is where the magic happens -->
+ <service
+ android:name=".neko.NekoService"
+ android:enabled="true"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true" >
+ </service>
+
+ <!-- Used to show over lock screen -->
+ <activity android:name=".neko.NekoLockedActivity"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar"
+ android:showOnLockScreen="true" />
+
+ <!-- Used to enable easter egg -->
+ <activity android:name=".neko.NekoActivationActivity"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.NoDisplay"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="com.android.internal.category.PLATLOGO" />
+ </intent-filter>
+ </activity>
+
+ <!-- The quick settings tile, disabled by default -->
+ <service
+ android:name=".neko.NekoTile"
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
+ android:icon="@drawable/stat_icon"
+ android:enabled="false"
+ android:label="@string/default_tile_name">
+ <intent-filter>
+ <action android:name="android.service.quicksettings.action.QS_TILE" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/packages/EasterEgg/res/drawable/back.xml b/packages/EasterEgg/res/drawable/back.xml
new file mode 100644
index 000000000000..b55d65cdf76d
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/back.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="back" android:fillColor="#FF000000" android:pathData="M37.1,22c-1.1,0 -1.9,0.8 -1.9,1.9v5.6c0,1.1 0.8,1.9 1.9,1.9H39v-1.9v-5.6V22H37.1z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/belly.xml b/packages/EasterEgg/res/drawable/belly.xml
new file mode 100644
index 000000000000..8b0e9afac463
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/belly.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="belly" android:fillColor="#FF000000" android:pathData="M20.5,25c-3.6,0 -6.5,2.9 -6.5,6.5V38h13v-6.5C27,27.9 24.1,25 20.5,25z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/body.xml b/packages/EasterEgg/res/drawable/body.xml
new file mode 100644
index 000000000000..86087209eff5
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/body.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="body" android:fillColor="#FF000000" android:pathData="M9,20h30v18h-30z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/bowtie.xml b/packages/EasterEgg/res/drawable/bowtie.xml
new file mode 100644
index 000000000000..33fa9216712f
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/bowtie.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="bowtie" android:fillColor="#FF000000" android:pathData="M29,16.8l-10,5l0,-5l10,5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/cap.xml b/packages/EasterEgg/res/drawable/cap.xml
new file mode 100644
index 000000000000..d8b4cc58a261
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/cap.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="cap" android:fillColor="#FF000000" android:pathData="M27.2,3.8c-1,-0.2 -2.1,-0.3 -3.2,-0.3s-2.1,0.1 -3.2,0.3c0.2,1.3 1.5,2.2 3.2,2.2C25.6,6.1 26.9,5.1 27.2,3.8z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/collar.xml b/packages/EasterEgg/res/drawable/collar.xml
new file mode 100644
index 000000000000..6c0d90a9bf22
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/collar.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="collar" android:fillColor="#FF000000" android:pathData="M9,18.4h30v1.6h-30z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/face_spot.xml b/packages/EasterEgg/res/drawable/face_spot.xml
new file mode 100644
index 000000000000..a89fb4fdaadd
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/face_spot.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="face_spot" android:fillColor="#FF000000" android:pathData="M19.5,15.2a4.5,3.2 0,1 0,9 0a4.5,3.2 0,1 0,-9 0z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_bits.xml b/packages/EasterEgg/res/drawable/food_bits.xml
new file mode 100644
index 000000000000..1b2bb6f36947
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_bits.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.1,34l-3.5,1.3c-1,0.4,-2.2,-0.1,-2.6,-1.1l-1.2,-3c-0.4,-1,0.1,-2.2,1.1,-2.6l3.5,-1.3c1,-0.4,2.2,0.1,2.6,1.1l1.2,3 C20.6,32.4,20.1,33.6,19.1,34z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M25.2,28.1L22.9,28c-0.8,0,-1.5,-0.7,-1.4,-1.6l0.1,-2c0,-0.8,0.7,-1.5,1.6,-1.4l2.4,0.1c0.8,0,1.5,0.7,1.4,1.6l-0.1,2 C26.8,27.5,26.1,28.1,25.2,28.1z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.7,23.1L16.5,23c-0.5,0,-0.9,-0.4,-0.8,-0.9l0.1,-2.2c0,-0.5,0.4,-0.9,0.9,-0.8l2.2,0.1c0.5,0,0.9,0.4,0.8,0.9 l-0.1,2.2C19.6,22.8,19.2,23.1,18.7,23.1z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M32.2,35.3l-3.6,-1.8c-1,-0.5,-1.4,-1.7,-0.9,-2.7l1.6,-3.1c0.5,-1,1.7,-1.4,2.7,-0.9l3.6,1.8c1,0.5,1.4,1.7,0.9,2.7 l-1.6,3.1C34.4,35.4,33.2,35.7,32.2,35.3z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_chicken.xml b/packages/EasterEgg/res/drawable/food_chicken.xml
new file mode 100644
index 000000000000..95b2fb55b796
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_chicken.xml
@@ -0,0 +1,39 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,12v14h10V11H9z M11.7,16.3c-0.7,0,-1.3,-0.6,-1.3,-1.3s0.6,-1.3,1.3,-1.3S13,14.3,13,15S12.4,16.3,11.7,16.3z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.7,20.1l1.6,-3.0l-1.6,-3.0l4.4,3.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.0,6.0l-2.3,2.3l-2.7,-2.6l-2.7,2.6l-2.3,-2.3l0.0,4.0l10.0,0.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,25c0,8.3,6.7,15,15,15s15,-6.7,15,-15H9z M29.9,31.5h-11v-1h12L29.9,31.5z M31.9,29.5h-13v-1h14L31.9,29.5z M33.9,27.5 h-15v-1h16L33.9,27.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M27.0,38.6h2.0v6.0h-2.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.4,44.6l-2.1999998,0.0l4.4000006,-6.0l2.1999989,0.0z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_dish.xml b/packages/EasterEgg/res/drawable/food_dish.xml
new file mode 100644
index 000000000000..3fff6a90fad2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_dish.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24,13.8C11.3,13.8,1,18.3,1,24c0,5.7,10.3,10.2,23,10.2S47,29.7,47,24C47,18.3,36.7,13.8,24,13.8z M33.7,26.6 c1.1,-0.6,1.8,-1.3,1.8,-2c0,-2.1,-5.2,-3.8,-11.7,-3.8s-11.7,1.7,-11.7,3.8c0,0.6,0.4,1.2,1.2,1.7c-1.7,-0.8,-2.8,-1.7,-2.8,-2.8 c0,-2.5,6,-4.5,13.4,-4.5s13.4,2,13.4,4.5C37.4,24.7,36,25.8,33.7,26.6z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_donut.xml b/packages/EasterEgg/res/drawable/food_donut.xml
new file mode 100644
index 000000000000..eaf831ea560c
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_donut.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24,4.5c-10.5,0,-19,8.5,-19,19s8.5,19,19,19s19,-8.5,19,-19S34.5,4.5,24,4.5z M35.2,15.5l1.6,-1.1 c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1 C34.9,16.1,35,15.7,35.2,15.5z M32.7,10.7c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0 c-0.3,0,-0.5,-0.3,-0.5,-0.6L32.7,10.7z M31.7,15.1l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C31.3,15.4,31.5,15.2,31.7,15.1z M28.8,10.6l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1C28.4,11.1,28.5,10.8,28.8,10.6z M25.8,6 c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6L25.8,6z M20.7,6.5l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1 C20.3,6.9,20.4,6.6,20.7,6.5z M19.9,10.9l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C19.5,11.1,19.7,10.9,19.9,10.9z M16,10.9L16,10.9c0.2,-0.3,0.4,-0.4,0.6,-0.3l1.3,0.7 c0.2,0.1,0.3,0.4,0.2,0.6L18,12c-0.1,0.2,-0.4,0.3,-0.6,0.2l-1.3,-0.7C15.9,11.4,15.8,11.1,16,10.9z M15.8,18.5c0.2,0,0.4,0.1,0.5,0.4 l0,0.1c0,0.2,-0.1,0.4,-0.4,0.5l-1.5,0.2c-0.2,0,-0.4,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.4,0.4,-0.5L15.8,18.5z M14,21.8l-1.6,1.1 c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 C14.3,21.3,14.3,21.6,14,21.8z M12.4,12L12.4,12c0.3,-0.2,0.5,-0.2,0.7,-0.1l1,1.1c0.2,0.2,0.2,0.4,0,0.6L14,13.7 c-0.2,0.2,-0.4,0.2,-0.6,0l-1,-1.1C12.2,12.4,12.2,12.1,12.4,12z M8.3,24.5c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6 l0.2,-2c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6L8.3,24.5z M8.5,16.2v-0.1c0,-0.3,0.2,-0.6,0.6,-0.6h2 c0.3,0,0.6,0.2,0.6,0.6v0.1c0,0.3,-0.2,0.6,-0.6,0.6H9C8.7,16.7,8.5,16.5,8.5,16.2z M10.3,20.7c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1 c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8L10.3,20.7z M11.3,28.3l0,-0.1 c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7L12,28.6C11.7,28.7,11.4,28.6,11.3,28.3z M14.4,33c0,0.2,-0.2,0.4,-0.4,0.4h-1.5c-0.2,0,-0.4,-0.2,-0.4,-0.4v-0.1c0,-0.2,0.2,-0.4,0.4,-0.4H14c0.2,0,0.4,0.2,0.4,0.4V33z M17.9,35.2 l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 C18.2,34.7,18.2,35.1,17.9,35.2z M20.7,33.8l-0.1,0.1c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1 c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C20.7,33.2,20.8,33.5,20.7,33.8z M17.5,23.5c0,-3.6,2.9,-6.5,6.5,-6.5s6.5,2.9,6.5,6.5 c0,3.6,-2.9,6.5,-6.5,6.5S17.5,27.1,17.5,23.5z M27.4,35.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7 c0.3,-0.1,0.6,0,0.7,0.3l0,0.1C27.9,35.3,27.7,35.6,27.4,35.7z M29.7,32.7l-1.4,0.5c-0.2,0.1,-0.5,0,-0.5,-0.3l0,-0.1 c-0.1,-0.2,0,-0.5,0.3,-0.5l1.4,-0.5c0.2,-0.1,0.5,0,0.5,0.3l0,0.1C30,32.3,29.9,32.6,29.7,32.7z M32.8,35.5l-0.1,0.1 c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C32.8,34.9,32.9,35.2,32.8,35.5z M33.7,30.9c0,0.2,-0.2,0.4,-0.5,0.4l-0.1,0c-0.2,0,-0.4,-0.2,-0.4,-0.5l0.1,-1.5c0,-0.2,0.2,-0.4,0.5,-0.4l0.1,0c0.2,0,0.4,0.2,0.4,0.5 L33.7,30.9z M34.5,26.5l-1.3,0.9c-0.2,0.1,-0.5,0.1,-0.6,-0.1l-0.1,-0.1c-0.1,-0.2,-0.1,-0.5,0.1,-0.6l1.3,-0.9c0.2,-0.1,0.5,-0.1,0.6,0.1 l0.1,0.1C34.8,26.1,34.7,26.3,34.5,26.5z M35.6,20.6l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1 c0.3,0.1,0.4,0.5,0.2,0.8l-0.1,0.1C36.2,20.6,35.8,20.7,35.6,20.6z M38.6,27.1l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1L36.1,28 c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1C38.9,26.6,38.8,27,38.6,27.1z M39,19.4l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.5,0.4,-0.5l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1C39.4,19.1,39.2,19.3,39,19.4z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_sysuituna.xml b/packages/EasterEgg/res/drawable/food_sysuituna.xml
new file mode 100644
index 000000000000..28cf4a2c7683
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_sysuituna.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M46,18.4l-5.8,4.6c-3.9,-3.2,-8.9,-5.6,-14.6,-6.3l1.2,-6l-7.3,5.9C12.5,17.2,6.4,20,2,24.3l7.2,1.4L2,27 c4.3,4.2,10.4,7.1,17.3,7.6l3.1,2.5L22,34.8c7.1,0,13.5,-2.5,18.2,-6.5l5.8,4.6l-1.4,-7.2L46,18.4z M14.3,24.8l-0.6,0.6l-1.1,-1.1 l-1.1,1.1l-0.6,-0.6l1.1,-1.1l-1.1,-1.1l0.6,-0.6l1.1,1.1l1.1,-1.1l0.6,0.6l-1.1,1.1L14.3,24.8z M18.8,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8 c0,-1.6,-0.4,-3,-1.1,-3.8c1.1,0.5,1.9,2,1.9,3.8S19.9,28.5,18.8,29.1z M20.7,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8c0,-1.6,-0.4,-3,-1.1,-3.8 c1.1,0.5,1.9,2,1.9,3.8S21.8,28.5,20.7,29.1z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot1.xml b/packages/EasterEgg/res/drawable/foot1.xml
new file mode 100644
index 000000000000..0d9085998a18
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot1.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot1" android:fillColor="#FF000000" android:pathData="M11.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot2.xml b/packages/EasterEgg/res/drawable/foot2.xml
new file mode 100644
index 000000000000..364ba0cd861c
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot2.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot2" android:fillColor="#FF000000" android:pathData="M18.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot3.xml b/packages/EasterEgg/res/drawable/foot3.xml
new file mode 100644
index 000000000000..e3a512a2568d
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot3.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot3" android:fillColor="#FF000000" android:pathData="M29.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot4.xml b/packages/EasterEgg/res/drawable/foot4.xml
new file mode 100644
index 000000000000..66b78fa26649
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot4.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot4" android:fillColor="#FF000000" android:pathData="M36.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/head.xml b/packages/EasterEgg/res/drawable/head.xml
new file mode 100644
index 000000000000..df600a8613cd
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/head.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="head" android:fillColor="#FF000000" android:pathData="M9,18.5c0,-8.3 6.8,-15 15,-15s15,6.7 15,15H9z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_close.xml b/packages/EasterEgg/res/drawable/ic_close.xml
new file mode 100644
index 000000000000..60ea36b11fcc
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_close.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M19.0,6.41L17.59,5.0 12.0,10.59 6.41,5.0 5.0,6.41 10.59,12.0 5.0,17.59 6.41,19.0 12.0,13.41 17.59,19.0 19.0,17.59 13.41,12.0z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_share.xml b/packages/EasterEgg/res/drawable/ic_share.xml
new file mode 100644
index 000000000000..8cebc7ed46de
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_share.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M18.0,16.08c-0.76,0.0 -1.4,0.3 -1.9,0.77L8.91,12.7c0.05,-0.2 0.09,-0.4 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.5,0.5 1.2,0.81 2.0,0.81 1.66,0.0 3.0,-1.34 3.0,-3.0s-1.34,-3.0 -3.0,-3.0 -3.0,1.34 -3.0,3.0c0.0,0.2 0.0,0.4 0.0,0.7L8.04,9.81C7.5,9.31 6.79,9.0 6.0,9.0c-1.66,0.0 -3.0,1.34 -3.0,3.0s1.34,3.0 3.0,3.0c0.79,0.0 1.5,-0.31 2.04,-0.81l7.12,4.16c0.0,0.21 0.0,0.43 0.0,0.65 0.0,1.61 1.31,2.92 2.92,2.92 1.61,0.0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/icon.xml
new file mode 100644
index 000000000000..defa83a9b5c6
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/icon.xml
@@ -0,0 +1,37 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="512dp"
+ android:height="512dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF7E5BBF"
+ android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/>
+ <path
+ android:fillColor="#FF7E5BBF"
+ android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/>
+ <path
+ android:fillColor="#40000000"
+ android:pathData="M44.0,35.5l-12.0,-12.0l0.0,-4.0z"/>
+ <path
+ android:fillColor="#40000000"
+ android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/>
+ <path
+ android:fillColor="#FF55C4F5"
+ android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/>
+</vector>
+
diff --git a/packages/EasterEgg/res/drawable/left_ear.xml b/packages/EasterEgg/res/drawable/left_ear.xml
new file mode 100644
index 000000000000..2b98736df039
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/left_ear.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="left_ear" android:fillColor="#FF000000" android:pathData="M15.4,1l5.1000004,5.3l-6.3,2.8000002z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/left_ear_inside.xml b/packages/EasterEgg/res/drawable/left_ear_inside.xml
new file mode 100644
index 000000000000..1d947edc31e2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/left_ear_inside.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="left_ear_inside" android:fillColor="#FF000000" android:pathData="M15.4,1l3.5,6.2l-4.7,1.9z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/left_eye.xml b/packages/EasterEgg/res/drawable/left_eye.xml
new file mode 100644
index 000000000000..4dde1b661393
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/left_eye.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="left_eye" android:fillColor="#FF000000" android:pathData="M20.5,11c0,1.7 -3,1.7 -3,0C17.5,9.3 20.5,9.3 20.5,11z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg1.xml b/packages/EasterEgg/res/drawable/leg1.xml
new file mode 100644
index 000000000000..625733318e72
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg1.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg1" android:fillColor="#FF000000" android:pathData="M9,38h5v5h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2.xml b/packages/EasterEgg/res/drawable/leg2.xml
new file mode 100644
index 000000000000..73352f69e80a
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg2.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg2" android:fillColor="#FF000000" android:pathData="M16,38h5v5h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2_shadow.xml b/packages/EasterEgg/res/drawable/leg2_shadow.xml
new file mode 100644
index 000000000000..77f4893194fe
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg2_shadow.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg2_shadow" android:fillColor="#FF000000" android:pathData="M16,38h5v1h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg3.xml b/packages/EasterEgg/res/drawable/leg3.xml
new file mode 100644
index 000000000000..53dea5c2becf
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg3.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg3" android:fillColor="#FF000000" android:pathData="M27,38h5v5h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg4.xml b/packages/EasterEgg/res/drawable/leg4.xml
new file mode 100644
index 000000000000..f2ce73e195a2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg4.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg4" android:fillColor="#FF000000" android:pathData="M34,38h5v5h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/mouth.xml b/packages/EasterEgg/res/drawable/mouth.xml
new file mode 100644
index 000000000000..ddcf2e82f976
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/mouth.xml
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="mouth"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="1.2"
+ android:strokeLineCap="round"
+ android:pathData="M29,14.3c-0.4,0.8 -1.3,1.4 -2.3,1.4c-1.4,0 -2.7,-1.3 -2.7,-2.7
+ M24,13c0,1.5 -1.2,2.7 -2.7,2.7c-1,0 -1.9,-0.5 -2.3,-1.4"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/nose.xml b/packages/EasterEgg/res/drawable/nose.xml
new file mode 100644
index 000000000000..d403cd1baadf
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/nose.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="nose" android:fillColor="#FF000000" android:pathData="M25.2,13c0,1.3 -2.3,1.3 -2.3,0S25.2,11.7 25.2,13z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/right_ear.xml b/packages/EasterEgg/res/drawable/right_ear.xml
new file mode 100644
index 000000000000..b9fb4d1c7470
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/right_ear.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="right_ear" android:fillColor="#FF000000" android:pathData="M32.6,1l-5.0999985,5.3l6.299999,2.8000002z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/right_ear_inside.xml b/packages/EasterEgg/res/drawable/right_ear_inside.xml
new file mode 100644
index 000000000000..86b6e3428d1f
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/right_ear_inside.xml
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+
+ <path android:name="right_ear_inside" android:fillColor="#FF000000" android:pathData="M33.8,9.1l-4.7,-1.9l3.5,-6.2z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/right_eye.xml b/packages/EasterEgg/res/drawable/right_eye.xml
new file mode 100644
index 000000000000..a1871a62c25b
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/right_eye.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="right_eye" android:fillColor="#FF000000" android:pathData="M30.5,11c0,1.7 -3,1.7 -3,0C27.5,9.3 30.5,9.3 30.5,11z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/stat_icon.xml b/packages/EasterEgg/res/drawable/stat_icon.xml
new file mode 100644
index 000000000000..608cb2017c3f
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/stat_icon.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.5,2 2,6.5 2,12c0,5.5 4.5,10 10,10s10,-4.5 10,-10C22,6.5 17.5,2 12,2zM5.5,11c0,-1.6 3,-1.6 3,0C8.5,12.7 5.5,12.7 5.5,11zM17.5,14.6c-0.6,1 -1.7,1.7 -2.9,1.7c-1.1,0 -2,-0.6 -2.6,-1.4c-0.6,0.9 -1.6,1.4 -2.7,1.4c-1.3,0 -2.3,-0.7 -2.9,-1.8c-0.2,-0.3 0,-0.7 0.3,-0.8c0.3,-0.2 0.7,0 0.8,0.3c0.3,0.7 1,1.1 1.8,1.1c0.9,0 1.6,-0.5 1.9,-1.3c-0.2,-0.2 -0.4,-0.4 -0.4,-0.7c0,-1.3 2.3,-1.3 2.3,0c0,0.3 -0.2,0.6 -0.4,0.7c0.3,0.8 1.1,1.3 1.9,1.3c0.8,0 1.5,-0.6 1.8,-1.1c0.2,-0.3 0.6,-0.4 0.9,-0.2C17.6,13.9 17.7,14.3 17.5,14.6zM15.5,11c0,-1.6 3,-1.6 3,0C18.5,12.7 15.5,12.7 15.5,11z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.2,1.0l4.1000004,4.2l-5.0,2.1000004z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.8,1.0l-4.0999994,4.2l5.000001,2.1000004z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/tail.xml b/packages/EasterEgg/res/drawable/tail.xml
new file mode 100644
index 000000000000..0cca23c3e16c
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/tail.xml
@@ -0,0 +1,26 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="tail"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="5"
+ android:strokeLineCap="round"
+ android:pathData="M35,35.5h5.9c2.1,0 3.8,-1.7 3.8,-3.8v-6.2"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_cap.xml b/packages/EasterEgg/res/drawable/tail_cap.xml
new file mode 100644
index 000000000000..b82f6f9b478a
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/tail_cap.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="tail_cap" android:fillColor="#FF000000" android:pathData="M42.2,25.5c0,-1.4 1.1,-2.5 2.5,-2.5s2.5,1.1 2.5,2.5H42.2z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_shadow.xml b/packages/EasterEgg/res/drawable/tail_shadow.xml
new file mode 100644
index 000000000000..bb1ff12b3afe
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/tail_shadow.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="tail_shadow" android:fillColor="#FF000000" android:pathData="M40,38l0,-5l-1,0l0,5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/layout/cat_view.xml b/packages/EasterEgg/res/layout/cat_view.xml
new file mode 100644
index 000000000000..82ced2f240b5
--- /dev/null
+++ b/packages/EasterEgg/res/layout/cat_view.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:gravity="center_horizontal"
+ android:clipToPadding="false">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:scaleType="fitCenter" />
+
+ <LinearLayout
+ android:id="@+id/contextGroup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:layout_gravity="bottom">
+
+ <ImageView
+ android:id="@android:id/shareText"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="8dp"
+ android:src="@drawable/ic_share"
+ android:scaleType="fitCenter"
+ android:background="#40000000"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <ImageView
+ android:id="@android:id/closeButton"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="4dp"
+ android:src="@drawable/ic_close"
+ android:scaleType="fitCenter"
+ android:background="#40000000"/>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:gravity="center"/>
+</LinearLayout>
+
diff --git a/packages/EasterEgg/res/layout/edit_text.xml b/packages/EasterEgg/res/layout/edit_text.xml
new file mode 100644
index 000000000000..9f7ac802bad4
--- /dev/null
+++ b/packages/EasterEgg/res/layout/edit_text.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp">
+
+ <EditText
+ android:id="@android:id/edit"
+ android:maxLines="1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/food_layout.xml b/packages/EasterEgg/res/layout/food_layout.xml
new file mode 100644
index 000000000000..d0ca0c8899aa
--- /dev/null
+++ b/packages/EasterEgg/res/layout/food_layout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:paddingLeft="4dp" android:paddingRight="4dp"
+ android:paddingBottom="6dp" android:paddingTop="6dp">
+ <ImageView
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:id="@+id/icon"
+ android:tint="?android:attr/colorControlNormal"/>
+ <TextView android:layout_width="64dp" android:layout_height="wrap_content"
+ android:gravity="top|center_horizontal"
+ android:id="@+id/text" />
+</LinearLayout>
diff --git a/packages/EasterEgg/res/layout/neko_activity.xml b/packages/EasterEgg/res/layout/neko_activity.xml
new file mode 100644
index 000000000000..21a4600bae40
--- /dev/null
+++ b/packages/EasterEgg/res/layout/neko_activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/holder"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"/>
+</FrameLayout> \ No newline at end of file
diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml
new file mode 100644
index 000000000000..a2440c7bc786
--- /dev/null
+++ b/packages/EasterEgg/res/values/strings.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <string name="app_name" translatable="false">Android Easter Egg</string>
+ <string name="notification_name" translatable="false">Android Neko</string>
+ <string name="default_tile_name" translatable="false">\????</string>
+ <string name="notification_title" translatable="false">A cat is here.</string>
+ <string name="default_cat_name" translatable="false">Cat #%s</string>
+ <string name="directory_name" translatable="false">Cats</string>
+ <string-array name="food_names" translatable="false">
+ <item>Empty dish</item>
+ <item>Bits</item>
+ <item>Fish</item>
+ <item>Chicken</item>
+ <item>Treat</item>
+ </string-array>
+ <array name="food_icons">
+ <item>@drawable/food_dish</item>
+ <item>@drawable/food_bits</item>
+ <item>@drawable/food_sysuituna</item>
+ <item>@drawable/food_chicken</item>
+ <item>@drawable/food_donut</item>
+ </array>
+ <integer-array name="food_intervals">
+ <item>0</item>
+ <item>15</item>
+ <item>30</item>
+ <item>60</item>
+ <item>120</item>
+ </integer-array>
+ <integer-array name="food_new_cat_prob">
+ <item>0</item>
+ <item>5</item>
+ <item>35</item>
+ <item>65</item>
+ <item>90</item>
+ </integer-array>
+</resources>
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
new file mode 100644
index 000000000000..864b20c73fbf
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
@@ -0,0 +1,375 @@
+/*
+ * 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.egg.neko;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.*;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import com.android.egg.R;
+
+public class Cat extends Drawable {
+ public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40};
+
+ private Random mNotSoRandom;
+ private Bitmap mBitmap;
+ private long mSeed;
+ private String mName;
+ private int mBodyColor;
+
+ private synchronized Random notSoRandom(long seed) {
+ if (mNotSoRandom == null) {
+ mNotSoRandom = new Random();
+ mNotSoRandom.setSeed(seed);
+ }
+ return mNotSoRandom;
+ }
+
+ public static final float frandrange(Random r, float a, float b) {
+ return (b-a)*r.nextFloat() + a;
+ }
+
+ public static final Object choose(Random r, Object...l) {
+ return l[r.nextInt(l.length)];
+ }
+
+ public static final int chooseP(Random r, int[] a) {
+ int pct = r.nextInt(1000);
+ final int stop = a.length-2;
+ int i=0;
+ while (i<stop) {
+ pct -= a[i];
+ if (pct < 0) break;
+ i+=2;
+ }
+ return a[i+1];
+ }
+
+ public static final int[] P_BODY_COLORS = {
+ 180, 0xFF212121, // black
+ 180, 0xFFFFFFFF, // white
+ 140, 0xFF616161, // gray
+ 140, 0xFF795548, // brown
+ 100, 0xFF90A4AE, // steel
+ 100, 0xFFFFF9C4, // buff
+ 100, 0xFFFF8F00, // orange
+ 5, 0xFF29B6F6, // blue..?
+ 5, 0xFFFFCDD2, // pink!?
+ 5, 0xFFCE93D8, // purple?!?!?
+ 4, 0xFF43A047, // yeah, why not green
+ 1, 0, // ?!?!?!
+ };
+
+ public static final int[] P_COLLAR_COLORS = {
+ 250, 0xFFFFFFFF,
+ 250, 0xFF000000,
+ 250, 0xFFF44336,
+ 50, 0xFF1976D2,
+ 50, 0xFFFDD835,
+ 50, 0xFFFB8C00,
+ 50, 0xFFF48FB1,
+ 50, 0xFF4CAF50,
+ };
+
+ public static final int[] P_BELLY_COLORS = {
+ 750, 0,
+ 250, 0xFFFFFFFF,
+ };
+
+ public static final int[] P_DARK_SPOT_COLORS = {
+ 700, 0,
+ 250, 0xFF212121,
+ 50, 0xFF6D4C41,
+ };
+
+ public static final int[] P_LIGHT_SPOT_COLORS = {
+ 700, 0,
+ 300, 0xFFFFFFFF,
+ };
+
+ private CatParts D;
+
+ public static void tint(int color, Drawable ... ds) {
+ for (Drawable d : ds) {
+ if (d != null) {
+ d.mutate().setTint(color);
+ }
+ }
+ }
+
+ public static boolean isDark(int color) {
+ final int r = (color & 0xFF0000) >> 16;
+ final int g = (color & 0x00FF00) >> 8;
+ final int b = color & 0x0000FF;
+ return (r + g + b) < 0x80;
+ }
+
+ public Cat(Context context, long seed) {
+ D = new CatParts(context);
+ mSeed = seed;
+
+ setName(context.getString(R.string.default_cat_name,
+ String.valueOf(mSeed).substring(0, 3)));
+
+ final Random nsr = notSoRandom(seed);
+
+ // body color
+ mBodyColor = chooseP(nsr, P_BODY_COLORS);
+ if (mBodyColor == 0) mBodyColor = Color.HSVToColor(new float[] {
+ nsr.nextFloat()*360f, frandrange(nsr,0.5f,1f), frandrange(nsr,0.5f, 1f)});
+
+ tint(mBodyColor, D.body, D.head, D.leg1, D.leg2, D.leg3, D.leg4, D.tail,
+ D.leftEar, D.rightEar, D.foot1, D.foot2, D.foot3, D.foot4, D.tailCap);
+ tint(0x20000000, D.leg2Shadow, D.tailShadow);
+ if (isDark(mBodyColor)) {
+ tint(0xFFFFFFFF, D.leftEye, D.rightEye, D.mouth, D.nose);
+ }
+ tint(isDark(mBodyColor) ? 0xFFEF9A9A : 0x20D50000, D.leftEarInside, D.rightEarInside);
+
+ tint(chooseP(nsr, P_BELLY_COLORS), D.belly);
+ tint(chooseP(nsr, P_BELLY_COLORS), D.back);
+ final int faceColor = chooseP(nsr, P_BELLY_COLORS);
+ tint(faceColor, D.faceSpot);
+ if (!isDark(faceColor)) {
+ tint(0xFF000000, D.mouth, D.nose);
+ }
+
+ if (nsr.nextFloat() < 0.25f) {
+ tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4);
+ } else {
+ if (nsr.nextFloat() < 0.25f) {
+ tint(0xFFFFFFFF, D.foot1, D.foot2);
+ } else if (nsr.nextFloat() < 0.25f) {
+ tint(0xFFFFFFFF, D.foot3, D.foot4);
+ } else if (nsr.nextFloat() < 0.1f) {
+ tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4));
+ }
+ }
+
+ tint(nsr.nextFloat() < 0.333f ? 0xFFFFFFFF : mBodyColor, D.tailCap);
+
+ final int capColor = chooseP(nsr, isDark(mBodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS);
+ tint(capColor, D.cap);
+ //tint(chooseP(nsr, isDark(bodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS), D.nose);
+
+ final int collarColor = chooseP(nsr, P_COLLAR_COLORS);
+ tint(collarColor, D.collar);
+ tint((nsr.nextFloat() < 0.1f) ? collarColor : 0, D.bowtie);
+ }
+
+ public static Cat create(Context context) {
+ return new Cat(context, Math.abs(ThreadLocalRandom.current().nextInt()));
+ }
+
+ public Notification.Builder buildNotification(Context context) {
+ final Bundle extras = new Bundle();
+ extras.putString("android.substName", context.getString(R.string.notification_name));
+ final Intent intent = new Intent(Intent.ACTION_MAIN)
+ .setClass(context, NekoLand.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return new Notification.Builder(context)
+ .setSmallIcon(Icon.createWithResource(context, R.drawable.stat_icon))
+ .setLargeIcon(createLargeIcon(context))
+ .setColor(getBodyColor())
+ .setPriority(Notification.PRIORITY_LOW)
+ .setContentTitle(context.getString(R.string.notification_title))
+ .setShowWhen(true)
+ .setCategory(Notification.CATEGORY_STATUS)
+ .setContentText(getName())
+ .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0))
+ .setAutoCancel(true)
+ .setVibrate(PURR)
+ .addExtras(extras);
+ }
+
+ public long getSeed() {
+ return mSeed;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final int w = Math.min(canvas.getWidth(), canvas.getHeight());
+ final int h = w;
+
+ if (mBitmap == null || mBitmap.getWidth() != w || mBitmap.getHeight() != h) {
+ mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ final Canvas bitCanvas = new Canvas(mBitmap);
+ slowDraw(bitCanvas, 0, 0, w, h);
+ }
+ canvas.drawBitmap(mBitmap, 0, 0, null);
+ }
+
+ private void slowDraw(Canvas canvas, int x, int y, int w, int h) {
+ for (int i = 0; i < D.drawingOrder.length; i++) {
+ final Drawable d = D.drawingOrder[i];
+ if (d != null) {
+ d.setBounds(x, y, x+w, y+h);
+ d.draw(canvas);
+ }
+ }
+
+ }
+
+ public Bitmap createBitmap(int w, int h) {
+ if (mBitmap != null && mBitmap.getWidth() == w && mBitmap.getHeight() == h) {
+ return mBitmap.copy(mBitmap.getConfig(), true);
+ }
+ Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ slowDraw(new Canvas(result), 0, 0, w, h);
+ return result;
+ }
+
+ public Icon createLargeIcon(Context context) {
+ final Resources res = context.getResources();
+ final int w = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ final int h = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
+
+ Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(result);
+ final Paint pt = new Paint();
+ float[] hsv = new float[3];
+ Color.colorToHSV(mBodyColor, hsv);
+ hsv[2] = (hsv[2]>0.5f)
+ ? (hsv[2] - 0.25f)
+ : (hsv[2] + 0.25f);
+ pt.setColor(Color.HSVToColor(hsv));
+ float r = w/2;
+ canvas.drawCircle(r, r, r, pt);
+ int m = w/10;
+
+ slowDraw(canvas, m, m, w-m-m, h-m-m);
+
+ return Icon.createWithBitmap(result);
+ }
+
+ @Override
+ public void setAlpha(int i) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setName(String name) {
+ this.mName = name;
+ }
+
+ public int getBodyColor() {
+ return mBodyColor;
+ }
+
+ public static class CatParts {
+ public Drawable leftEar;
+ public Drawable rightEar;
+ public Drawable rightEarInside;
+ public Drawable leftEarInside;
+ public Drawable head;
+ public Drawable faceSpot;
+ public Drawable cap;
+ public Drawable mouth;
+ public Drawable body;
+ public Drawable foot1;
+ public Drawable leg1;
+ public Drawable foot2;
+ public Drawable leg2;
+ public Drawable foot3;
+ public Drawable leg3;
+ public Drawable foot4;
+ public Drawable leg4;
+ public Drawable tail;
+ public Drawable leg2Shadow;
+ public Drawable tailShadow;
+ public Drawable tailCap;
+ public Drawable belly;
+ public Drawable back;
+ public Drawable rightEye;
+ public Drawable leftEye;
+ public Drawable nose;
+ public Drawable bowtie;
+ public Drawable collar;
+ public Drawable[] drawingOrder;
+
+ public CatParts(Context context) {
+ body = context.getDrawable(R.drawable.body);
+ head = context.getDrawable(R.drawable.head);
+ leg1 = context.getDrawable(R.drawable.leg1);
+ leg2 = context.getDrawable(R.drawable.leg2);
+ leg3 = context.getDrawable(R.drawable.leg3);
+ leg4 = context.getDrawable(R.drawable.leg4);
+ tail = context.getDrawable(R.drawable.tail);
+ leftEar = context.getDrawable(R.drawable.left_ear);
+ rightEar = context.getDrawable(R.drawable.right_ear);
+ rightEarInside = context.getDrawable(R.drawable.right_ear_inside);
+ leftEarInside = context.getDrawable(R.drawable.left_ear_inside);
+ faceSpot = context.getDrawable(R.drawable.face_spot);
+ cap = context.getDrawable(R.drawable.cap);
+ mouth = context.getDrawable(R.drawable.mouth);
+ foot4 = context.getDrawable(R.drawable.foot4);
+ foot3 = context.getDrawable(R.drawable.foot3);
+ foot1 = context.getDrawable(R.drawable.foot1);
+ foot2 = context.getDrawable(R.drawable.foot2);
+ leg2Shadow = context.getDrawable(R.drawable.leg2_shadow);
+ tailShadow = context.getDrawable(R.drawable.tail_shadow);
+ tailCap = context.getDrawable(R.drawable.tail_cap);
+ belly = context.getDrawable(R.drawable.belly);
+ back = context.getDrawable(R.drawable.back);
+ rightEye = context.getDrawable(R.drawable.right_eye);
+ leftEye = context.getDrawable(R.drawable.left_eye);
+ nose = context.getDrawable(R.drawable.nose);
+ collar = context.getDrawable(R.drawable.collar);
+ bowtie = context.getDrawable(R.drawable.bowtie);
+ drawingOrder = getDrawingOrder();
+ }
+ private Drawable[] getDrawingOrder() {
+ return new Drawable[] {
+ collar,
+ leftEar, leftEarInside, rightEar, rightEarInside,
+ head,
+ faceSpot,
+ cap,
+ leftEye, rightEye,
+ nose, mouth,
+ tail, tailCap, tailShadow,
+ foot1, leg1,
+ foot2, leg2,
+ foot3, leg3,
+ foot4, leg4,
+ leg2Shadow,
+ body, belly,
+ bowtie
+ };
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Food.java b/packages/EasterEgg/src/com/android/egg/neko/Food.java
new file mode 100644
index 000000000000..5c0f12e2639f
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/Food.java
@@ -0,0 +1,60 @@
+/*
+ * 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.egg.neko;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+
+import com.android.egg.R;
+
+public class Food {
+ private final int mType;
+
+ private static int[] sIcons;
+ private static String[] sNames;
+
+ public Food(int type) {
+ mType = type;
+ }
+
+ public Icon getIcon(Context context) {
+ if (sIcons == null) {
+ TypedArray icons = context.getResources().obtainTypedArray(R.array.food_icons);
+ sIcons = new int[icons.length()];
+ for (int i = 0; i < sIcons.length; i++) {
+ sIcons[i] = icons.getResourceId(i, 0);
+ }
+ icons.recycle();
+ }
+ return Icon.createWithResource(context, sIcons[mType]);
+ }
+
+ public String getName(Context context) {
+ if (sNames == null) {
+ sNames = context.getResources().getStringArray(R.array.food_names);
+ }
+ return sNames[mType];
+ }
+
+ public long getInterval(Context context) {
+ return context.getResources().getIntArray(R.array.food_intervals)[mType];
+ }
+
+ public int getType() {
+ return mType;
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
new file mode 100644
index 000000000000..88a7968da16c
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.widget.Toast;
+
+public class NekoActivationActivity extends Activity {
+ private void toastUp(String s) {
+ Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
+ toast.getView().setBackgroundDrawable(null);
+ toast.show();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ final PackageManager pm = getPackageManager();
+ final ComponentName cn = new ComponentName(this, NekoTile.class);
+ if (pm.getComponentEnabledSetting(cn) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (NekoLand.DEBUG) {
+ Log.v("Neko", "Disabling tile.");
+ }
+ pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ toastUp("\uD83D\uDEAB");
+ } else {
+ if (NekoLand.DEBUG) {
+ Log.v("Neko", "Enabling tile.");
+ }
+ pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ toastUp("\uD83D\uDC31");
+ }
+ finish();
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
new file mode 100644
index 000000000000..a2ffd3e21887
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
@@ -0,0 +1,105 @@
+/*
+ * 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.egg.neko;
+
+import android.support.annotation.NonNull;
+import android.app.Dialog;
+import android.content.Context;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.egg.R;
+
+import java.util.ArrayList;
+
+public class NekoDialog extends Dialog {
+
+ private final Adapter mAdapter;
+
+ public NekoDialog(@NonNull Context context) {
+ super(context, android.R.style.Theme_Material_Dialog_NoActionBar);
+ RecyclerView view = new RecyclerView(getContext());
+ mAdapter = new Adapter(getContext());
+ view.setLayoutManager(new GridLayoutManager(getContext(), 2));
+ view.setAdapter(mAdapter);
+ final float dp = context.getResources().getDisplayMetrics().density;
+ final int pad = (int)(16*dp);
+ view.setPadding(pad, pad, pad, pad);
+ setContentView(view);
+ }
+
+ private void onFoodSelected(Food food) {
+ PrefState prefs = new PrefState(getContext());
+ int currentState = prefs.getFoodState();
+ if (currentState == 0 && food.getType() != 0) {
+ NekoService.registerJob(getContext(), food.getInterval(getContext()));
+ }
+ prefs.setFoodState(food.getType());
+ dismiss();
+ }
+
+ private class Adapter extends RecyclerView.Adapter<Holder> {
+
+ private final Context mContext;
+ private final ArrayList<Food> mFoods = new ArrayList<>();
+
+ public Adapter(Context context) {
+ mContext = context;
+ int[] foods = context.getResources().getIntArray(R.array.food_names);
+ // skip food 0, you can't choose it
+ for (int i=1; i<foods.length; i++) {
+ mFoods.add(new Food(i));
+ }
+ }
+
+ @Override
+ public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new Holder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.food_layout, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(final Holder holder, int position) {
+ final Food food = mFoods.get(position);
+ ((ImageView) holder.itemView.findViewById(R.id.icon))
+ .setImageIcon(food.getIcon(mContext));
+ ((TextView) holder.itemView.findViewById(R.id.text))
+ .setText(food.getName(mContext));
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onFoodSelected(mFoods.get(holder.getAdapterPosition()));
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mFoods.size();
+ }
+ }
+
+ public static class Holder extends RecyclerView.ViewHolder {
+
+ public Holder(View itemView) {
+ super(itemView);
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
new file mode 100644
index 000000000000..e6a41774f3cb
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
@@ -0,0 +1,280 @@
+/*
+ * 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.egg.neko;
+
+import android.Manifest;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore.Images;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.egg.R;
+import com.android.egg.neko.PrefState.PrefsListener;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class NekoLand extends Activity implements PrefsListener {
+ public static boolean DEBUG = false;
+ public static boolean DEBUG_NOTIFICATIONS = false;
+
+ private static final int STORAGE_PERM_REQUEST = 123;
+
+ private static boolean CAT_GEN = false;
+ private PrefState mPrefs;
+ private CatAdapter mAdapter;
+ private Cat mPendingShareCat;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.neko_activity);
+ final ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setLogo(Cat.create(this));
+ actionBar.setDisplayUseLogoEnabled(false);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mPrefs = new PrefState(this);
+ mPrefs.setListener(this);
+ final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.holder);
+ mAdapter = new CatAdapter();
+ recyclerView.setAdapter(mAdapter);
+ recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
+ updateCats();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mPrefs.setListener(null);
+ }
+
+ private void updateCats() {
+ Cat[] cats;
+ if (CAT_GEN) {
+ cats = new Cat[50];
+ for (int i = 0; i < cats.length; i++) {
+ cats[i] = Cat.create(this);
+ }
+ } else {
+ cats = mPrefs.getCats().toArray(new Cat[0]);
+ }
+ mAdapter.setCats(cats);
+ }
+
+ private void onCatClick(Cat cat) {
+ if (CAT_GEN) {
+ mPrefs.addCat(cat);
+ new AlertDialog.Builder(NekoLand.this)
+ .setTitle("Cat added")
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ } else {
+ showNameDialog(cat);
+ }
+// noman.notify(1, cat.buildNotification(NekoLand.this).build());
+ }
+
+ private void onCatRemove(Cat cat) {
+ mPrefs.removeCat(cat);
+ }
+
+ private void showNameDialog(final Cat cat) {
+ Context context = new ContextThemeWrapper(this,
+ android.R.style.Theme_Material_Light_Dialog_NoActionBar);
+ // TODO: Move to XML, add correct margins.
+ View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null);
+ final EditText text = (EditText) view.findViewById(android.R.id.edit);
+ text.setText(cat.getName());
+ text.setSelection(cat.getName().length());
+ Drawable catIcon = cat.createLargeIcon(this).loadDrawable(this);
+ new AlertDialog.Builder(context)
+ .setTitle(" ")
+ .setIcon(catIcon)
+ .setView(view)
+ .setPositiveButton(android.R.string.ok, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ cat.setName(text.getText().toString().trim());
+ mPrefs.addCat(cat);
+ }
+ }).show();
+ }
+
+ @Override
+ public void onPrefsChanged() {
+ updateCats();
+ }
+
+ private class CatAdapter extends RecyclerView.Adapter<CatHolder> {
+
+ private Cat[] mCats;
+
+ public void setCats(Cat[] cats) {
+ mCats = cats;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public CatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new CatHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.cat_view, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(final CatHolder holder, int position) {
+ Context context = holder.itemView.getContext();
+ holder.imageView.setImageIcon(mCats[position].createLargeIcon(context));
+ holder.textView.setText(mCats[position].getName());
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onCatClick(mCats[holder.getAdapterPosition()]);
+ }
+ });
+ holder.itemView.setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ holder.contextGroup.removeCallbacks((Runnable) holder.contextGroup.getTag());
+ holder.contextGroup.setVisibility(View.VISIBLE);
+ Runnable hideAction = new Runnable() {
+ @Override
+ public void run() {
+ holder.contextGroup.setVisibility(View.INVISIBLE);
+ }
+ };
+ holder.contextGroup.setTag(hideAction);
+ holder.contextGroup.postDelayed(hideAction, 5000);
+ return true;
+ }
+ });
+ holder.delete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ holder.contextGroup.setVisibility(View.INVISIBLE);
+ holder.contextGroup.removeCallbacks((Runnable) holder.contextGroup.getTag());
+ onCatRemove(mCats[holder.getAdapterPosition()]);
+ }
+ });
+ holder.share.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Cat cat = mCats[holder.getAdapterPosition()];
+ if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ mPendingShareCat = cat;
+ requestPermissions(
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ STORAGE_PERM_REQUEST);
+ return;
+ }
+ shareCat(cat);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCats.length;
+ }
+ }
+
+ private void shareCat(Cat cat) {
+ final File dir = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
+ getString(R.string.directory_name));
+ if (!dir.exists() && !dir.mkdirs()) {
+ Log.e("NekoLand", "save: error: can't create Pictures directory");
+ return;
+ }
+ final File png = new File(dir, cat.getName().replaceAll("[/ #:]+", "_") + ".png");
+ Bitmap bitmap = cat.createBitmap(512, 512);
+ if (bitmap != null) {
+ try {
+ OutputStream os = new FileOutputStream(png);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);
+ os.close();
+ MediaScannerConnection.scanFile(
+ this,
+ new String[] {png.toString()},
+ new String[] {"image/png"},
+ null);
+ Uri uri = Uri.fromFile(png);
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName());
+ intent.setType("image/png");
+ startActivity(Intent.createChooser(intent, null));
+ } catch (IOException e) {
+ Log.e("NekoLand", "save: error: " + e);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+ if (requestCode == STORAGE_PERM_REQUEST) {
+ if (mPendingShareCat != null) {
+ shareCat(mPendingShareCat);
+ mPendingShareCat = null;
+ }
+ }
+ }
+
+ private static class CatHolder extends RecyclerView.ViewHolder {
+ private final ImageView imageView;
+ private final TextView textView;
+ private final View contextGroup;
+ private final View delete;
+ private final View share;
+
+ public CatHolder(View itemView) {
+ super(itemView);
+ imageView = (ImageView) itemView.findViewById(android.R.id.icon);
+ textView = (TextView) itemView.findViewById(android.R.id.title);
+ contextGroup = itemView.findViewById(R.id.contextGroup);
+ delete = itemView.findViewById(android.R.id.closeButton);
+ share = itemView.findViewById(android.R.id.shareText);
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
new file mode 100644
index 000000000000..5f01da879ebb
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.egg.neko;
+
+import android.support.annotation.Nullable;
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class NekoLockedActivity extends Activity implements OnDismissListener {
+
+ private NekoDialog mDialog;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+
+ mDialog = new NekoDialog(this);
+ mDialog.setOnDismissListener(this);
+ mDialog.show();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
new file mode 100644
index 000000000000..1ee385136f47
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
@@ -0,0 +1,136 @@
+/*
+ * 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.egg.neko;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.util.List;
+import android.util.Log;
+
+import com.android.egg.R;
+
+import java.util.Random;
+
+public class NekoService extends JobService {
+
+ private static final String TAG = "NekoService";
+
+ public static int JOB_ID = 42;
+
+ public static int CAT_NOTIFICATION = 1;
+
+ public static float CAT_CAPTURE_PROB = 1.0f; // generous
+
+ public static long SECONDS = 1000;
+ public static long MINUTES = 60 * SECONDS;
+
+ public static long INTERVAL_FLEX = 5 * MINUTES;
+
+ public static float INTERVAL_JITTER_FRAC = 0.25f;
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.v(TAG, "Starting job: " + String.valueOf(params));
+
+ NotificationManager noman = getSystemService(NotificationManager.class);
+ if (NekoLand.DEBUG_NOTIFICATIONS) {
+ final Bundle extras = new Bundle();
+ extras.putString("android.substName", getString(R.string.notification_name));
+ final int size = getResources()
+ .getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ final Cat cat = Cat.create(this);
+ final Notification.Builder builder
+ = cat.buildNotification(this)
+ .setContentTitle("DEBUG")
+ .setContentText("Ran job: " + params);
+ noman.notify(1, builder.build());
+ }
+
+ final PrefState prefs = new PrefState(this);
+ int food = prefs.getFoodState();
+ if (food != 0) {
+ prefs.setFoodState(0); // nom
+ final Random rng = new Random();
+ if (rng.nextFloat() <= CAT_CAPTURE_PROB) {
+ Cat cat;
+ List<Cat> cats = prefs.getCats();
+ final int[] probs = getResources().getIntArray(R.array.food_new_cat_prob);
+ final float new_cat_prob = (float)((food < probs.length) ? probs[food] : 50) / 100f;
+
+ if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) {
+ cat = Cat.create(this);
+ prefs.addCat(cat);
+ Log.v(TAG, "A new cat is here: " + cat.getName());
+ } else {
+ cat = cats.get(rng.nextInt(cats.size()));
+ Log.v(TAG, "A cat has returned: " + cat.getName());
+ }
+
+ final Notification.Builder builder = cat.buildNotification(this);
+ noman.notify(CAT_NOTIFICATION, builder.build());
+ }
+ }
+ cancelJob(this);
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return false;
+ }
+
+ public static void registerJob(Context context, long intervalMinutes) {
+ JobScheduler jss = context.getSystemService(JobScheduler.class);
+ jss.cancel(JOB_ID);
+ long interval = intervalMinutes * MINUTES;
+ long jitter = (long)(INTERVAL_JITTER_FRAC * interval);
+ interval += (long)(Math.random() * (2 * jitter)) - jitter;
+ final JobInfo jobInfo = new JobInfo.Builder(JOB_ID,
+ new ComponentName(context, NekoService.class))
+ .setPeriodic(interval, INTERVAL_FLEX)
+ .build();
+
+ Log.v(TAG, "A cat will visit in " + interval + "ms: " + String.valueOf(jobInfo));
+ jss.schedule(jobInfo);
+
+ if (NekoLand.DEBUG_NOTIFICATIONS) {
+ NotificationManager noman = context.getSystemService(NotificationManager.class);
+ noman.notify(500, new Notification.Builder(context)
+ .setSmallIcon(R.drawable.stat_icon)
+ .setContentTitle(String.format("Job scheduled in %d min", (interval / MINUTES)))
+ .setContentText(String.valueOf(jobInfo))
+ .setPriority(Notification.PRIORITY_MIN)
+ .setCategory(Notification.CATEGORY_SERVICE)
+ .setShowWhen(true)
+ .build());
+ }
+ }
+
+ public static void cancelJob(Context context) {
+ JobScheduler jss = context.getSystemService(JobScheduler.class);
+ Log.v(TAG, "Canceling job");
+ jss.cancel(JOB_ID);
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
new file mode 100644
index 000000000000..d5e143cade0f
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
@@ -0,0 +1,96 @@
+/*
+ * 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.egg.neko;
+
+import android.content.Intent;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+import android.util.Log;
+
+import com.android.egg.neko.PrefState.PrefsListener;
+
+public class NekoTile extends TileService implements PrefsListener {
+
+ private static final String TAG = "NekoTile";
+
+ private PrefState mPrefs;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mPrefs = new PrefState(this);
+ }
+
+ @Override
+ public void onStartListening() {
+ super.onStartListening();
+ mPrefs.setListener(this);
+ updateState();
+ }
+
+ @Override
+ public void onStopListening() {
+ super.onStopListening();
+ mPrefs.setListener(null);
+ }
+
+ @Override
+ public void onPrefsChanged() {
+ updateState();
+ }
+
+ private void updateState() {
+ Tile tile = getQsTile();
+ int foodState = mPrefs.getFoodState();
+ Food food = new Food(foodState);
+ tile.setIcon(food.getIcon(this));
+ tile.setLabel(food.getName(this));
+ tile.setState(foodState != 0 ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
+ tile.updateTile();
+ }
+
+ @Override
+ public void onClick() {
+ if (mPrefs.getFoodState() != 0) {
+ // there's already food loaded, let's empty it
+ mPrefs.setFoodState(0);
+ NekoService.cancelJob(this);
+ } else {
+ // time to feed the cats
+ if (isLocked()) {
+ if (isSecure()) {
+ Log.d(TAG, "startActivityAndCollapse");
+ Intent intent = new Intent(this, NekoLockedActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivityAndCollapse(intent);
+ } else {
+ unlockAndRun(new Runnable() {
+ @Override
+ public void run() {
+ showNekoDialog();
+ }
+ });
+ }
+ } else {
+ showNekoDialog();
+ }
+ }
+ }
+
+ private void showNekoDialog() {
+ Log.d(TAG, "showNekoDialog");
+ showDialog(new NekoDialog(this));
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
new file mode 100644
index 000000000000..5f54180bc2e0
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
@@ -0,0 +1,94 @@
+/*
+ * 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.egg.neko;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class PrefState implements OnSharedPreferenceChangeListener {
+
+ private static final String FILE_NAME = "mPrefs";
+
+ private static final String FOOD_STATE = "food";
+
+ private static final String CAT_KEY_PREFIX = "cat:";
+
+ private final Context mContext;
+ private final SharedPreferences mPrefs;
+ private PrefsListener mListener;
+
+ public PrefState(Context context) {
+ mContext = context;
+ mPrefs = mContext.getSharedPreferences(FILE_NAME, 0);
+ }
+
+ // Can also be used for renaming.
+ public void addCat(Cat cat) {
+ mPrefs.edit()
+ .putString(CAT_KEY_PREFIX + String.valueOf(cat.getSeed()), cat.getName())
+ .commit();
+ }
+
+ public void removeCat(Cat cat) {
+ mPrefs.edit()
+ .remove(CAT_KEY_PREFIX + String.valueOf(cat.getSeed()))
+ .commit();
+ }
+
+ public List<Cat> getCats() {
+ ArrayList<Cat> cats = new ArrayList<>();
+ Map<String, ?> map = mPrefs.getAll();
+ for (String key : map.keySet()) {
+ if (key.startsWith(CAT_KEY_PREFIX)) {
+ long seed = Long.parseLong(key.substring(CAT_KEY_PREFIX.length()));
+ Cat cat = new Cat(mContext, seed);
+ cat.setName(String.valueOf(map.get(key)));
+ cats.add(cat);
+ }
+ }
+ return cats;
+ }
+
+ public int getFoodState() {
+ return mPrefs.getInt(FOOD_STATE, 0);
+ }
+
+ public void setFoodState(int foodState) {
+ mPrefs.edit().putInt(FOOD_STATE, foodState).commit();
+ }
+
+ public void setListener(PrefsListener listener) {
+ mListener = listener;
+ if (mListener != null) {
+ mPrefs.registerOnSharedPreferenceChangeListener(this);
+ } else {
+ mPrefs.unregisterOnSharedPreferenceChangeListener(this);
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ mListener.onPrefsChanged();
+ }
+
+ public interface PrefsListener {
+ void onPrefsChanged();
+ }
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 25dc3575d5f8..eadb4bf4fdaf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -436,7 +436,6 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT" />
- <category android:name="com.android.internal.category.PLATLOGO" />
</intent-filter>
</activity>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 5eaea90479ce..87e87c8e9bb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -154,8 +154,8 @@ public class StackScrollAlgorithm {
float newNotificationEnd = newYTranslation + newHeight;
boolean isHeadsUp = (child instanceof ExpandableNotificationRow)
&& ((ExpandableNotificationRow) child).isPinned();
- if (newYTranslation < previousNotificationEnd && ambientState.isShadeExpanded()
- && !isHeadsUp) {
+ if (newYTranslation < previousNotificationEnd
+ && (!isHeadsUp || ambientState.isShadeExpanded())) {
// The previous view is overlapping on top, clip!
float overlapAmount = previousNotificationEnd - newYTranslation;
state.clipTopAmount = (int) overlapAmount;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 59e4f2849649..172025b38337 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -61,7 +61,7 @@ import java.util.Map;
class BluetoothManagerService extends IBluetoothManager.Stub {
private static final String TAG = "BluetoothManagerService";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -1494,7 +1494,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
BluetoothAdapter.STATE_TURNING_OFF);
- waitForOnOff(false, true);
+ boolean didDisableTimeout = !waitForOnOff(false, true);
bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
BluetoothAdapter.STATE_OFF);
@@ -1512,7 +1512,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mBluetoothLock.writeLock().unlock();
}
- SystemClock.sleep(100);
+ //
+ // If disabling Bluetooth times out, wait for an
+ // additional amount of time to ensure the process is
+ // shut down completely before attempting to restart.
+ //
+ if (didDisableTimeout) {
+ SystemClock.sleep(3000);
+ } else {
+ SystemClock.sleep(100);
+ }
mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
mState = BluetoothAdapter.STATE_OFF;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 798662910cbf..ee2fa51c8666 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -43,7 +43,6 @@ import android.os.TransactionTooLargeException;
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
@@ -751,6 +750,17 @@ public final class ActiveServices {
mAm.updateProcessForegroundLocked(proc, anyForeground, oomAdj);
}
+ private void updateWhitelistManagerLocked(ProcessRecord proc) {
+ proc.whitelistManager = false;
+ for (int i=proc.services.size()-1; i>=0; i--) {
+ ServiceRecord sr = proc.services.valueAt(i);
+ if (sr.whitelistManager) {
+ proc.whitelistManager = true;
+ break;
+ }
+ }
+ }
+
public void updateServiceConnectionActivitiesLocked(ProcessRecord clientProc) {
ArraySet<ProcessRecord> updatedProcesses = null;
for (int i = 0; i < clientProc.connections.size(); i++) {
@@ -997,6 +1007,9 @@ public final class ActiveServices {
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
b.client.hasAboveClient = true;
}
+ if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
+ s.whitelistManager = true;
+ }
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}
@@ -1019,6 +1032,9 @@ public final class ActiveServices {
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
s.app.treatLikeActivity = true;
}
+ if (s.whitelistManager) {
+ s.app.whitelistManager = true;
+ }
// This could have made the service more important.
mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
|| s.app.treatLikeActivity, b.client);
@@ -1132,9 +1148,7 @@ public final class ActiveServices {
if (r.binding.service.app != null) {
if (r.binding.service.app.whitelistManager) {
- // Must reset flag here because on computeOomAdjLocked() the service
- // connection will be gone...
- r.binding.service.app.whitelistManager = false;
+ updateWhitelistManagerLocked(r.binding.service.app);
}
// This could have made the service less important.
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
@@ -1807,6 +1821,10 @@ public final class ActiveServices {
}
}
+ if (r.whitelistManager) {
+ app.whitelistManager = true;
+ }
+
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
@@ -2018,6 +2036,9 @@ public final class ActiveServices {
r.stats.stopLaunchedLocked();
}
r.app.services.remove(r);
+ if (r.whitelistManager) {
+ updateWhitelistManagerLocked(r.app);
+ }
if (r.app.thread != null) {
updateServiceForegroundLocked(r.app, false);
try {
@@ -2085,6 +2106,14 @@ public final class ActiveServices {
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
b.client.updateHasAboveClientLocked();
}
+ // If this connection requested whitelist management, see if we should
+ // now clear that state.
+ if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
+ s.updateWhitelistManager();
+ if (!s.whitelistManager && s.app != null) {
+ updateWhitelistManagerLocked(s.app);
+ }
+ }
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}
@@ -2284,6 +2313,9 @@ public final class ActiveServices {
if (finishing) {
if (r.app != null && !r.app.persistent) {
r.app.services.remove(r);
+ if (r.whitelistManager) {
+ updateWhitelistManagerLocked(r.app);
+ }
}
r.app = null;
}
@@ -2379,6 +2411,9 @@ public final class ActiveServices {
service.app.removed = killProcess;
if (!service.app.persistent) {
service.app.services.remove(service);
+ if (service.whitelistManager) {
+ updateWhitelistManagerLocked(service.app);
+ }
}
}
service.app = null;
@@ -2497,6 +2532,8 @@ public final class ActiveServices {
updateServiceConnectionActivitiesLocked(app);
app.connections.clear();
+ app.whitelistManager = false;
+
// Clear app state from services.
for (int i = app.services.size() - 1; i >= 0; i--) {
ServiceRecord sr = app.services.valueAt(i);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d911f52e5de1..08b204af7fe7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19294,7 +19294,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
boolean mayBeTop = false;
- app.whitelistManager = false;
for (int is = app.services.size()-1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
@@ -19353,9 +19352,6 @@ public final class ActivityManagerService extends ActivityManagerNative
// Binding to ourself is not interesting.
continue;
}
- if ((cr.flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
- app.whitelistManager = true;
- }
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4059a675c4fd..4ead64b6915b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1706,7 +1706,8 @@ final class ActivityStack {
final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
final boolean stackVisibleBehind = stackVisibility == STACK_VISIBLE_ACTIVITY_BEHIND;
boolean behindFullscreenActivity = stackInvisible;
- boolean resumeNextActivity = isFocusable() && (isInStackLocked(starting) == null);
+ boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
+ && (isInStackLocked(starting) == null);
boolean behindTranslucentActivity = false;
final ActivityRecord visibleBehind = getVisibleBehindActivity();
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 0a081e9809ea..2bfc4021f887 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -90,6 +90,7 @@ final class ServiceRecord extends Binder {
ProcessRecord isolatedProc; // keep track of isolated process, if requested
ServiceState tracker; // tracking service execution, may be null
ServiceState restartTracker; // tracking service restart
+ boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
boolean delayed; // are we waiting to start this service in the background?
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
@@ -225,6 +226,9 @@ final class ServiceRecord extends Binder {
if (isolatedProc != null) {
pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
}
+ if (whitelistManager) {
+ pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
+ }
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
}
@@ -391,6 +395,19 @@ final class ServiceRecord extends Binder {
return false;
}
+ public void updateWhitelistManager() {
+ whitelistManager = false;
+ for (int conni=connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+ for (int i=0; i<cr.size(); i++) {
+ if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
+ whitelistManager = true;
+ return;
+ }
+ }
+ }
+ }
+
public void resetRestartCounter() {
restartCount = 0;
restartDelay = 0;
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 804be4ea1ae7..c371f9705285 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -197,7 +197,7 @@ public class SyncOperation {
} else if (value instanceof Boolean) {
syncExtrasBundle.putBoolean(key, (Boolean) value);
} else if (value instanceof Float) {
- syncExtrasBundle.putDouble(key, (Double) value);
+ syncExtrasBundle.putDouble(key, (double) (float) value);
} else if (value instanceof Double) {
syncExtrasBundle.putDouble(key, (Double) value);
} else if (value instanceof String) {
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index 9dce070221bc..a42d0cd4c831 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -22,6 +22,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
+import android.util.Slog;
import android.util.TimeUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -40,6 +41,7 @@ import java.util.List;
*/
public class ContentObserverController extends StateController {
private static final String TAG = "JobScheduler.Content";
+ private static final boolean DEBUG = false;
/**
* Maximum number of changing URIs we will batch together to report.
@@ -88,6 +90,9 @@ public class ContentObserverController extends StateController {
if (taskStatus.contentObserverJobInstance == null) {
taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
}
+ if (DEBUG) {
+ Slog.i(TAG, "Tracking content-trigger job " + taskStatus);
+ }
mTrackedTasks.add(taskStatus);
boolean havePendingUris = false;
// If there is a previous job associated with the new job, propagate over
@@ -175,6 +180,9 @@ public class ContentObserverController extends StateController {
taskStatus.contentObserverJobInstance = null;
}
}
+ if (DEBUG) {
+ Slog.i(TAG, "No longer tracking job " + taskStatus);
+ }
mTrackedTasks.remove(taskStatus);
}
}
@@ -194,16 +202,20 @@ public class ContentObserverController extends StateController {
}
final class ObserverInstance extends ContentObserver {
- final Uri mUri;
+ final JobInfo.TriggerContentUri mUri;
final ArraySet<JobInstance> mJobs = new ArraySet<>();
- public ObserverInstance(Handler handler, Uri uri) {
+ public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri) {
super(handler);
mUri = uri;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
+ if (DEBUG) {
+ Slog.i(TAG, "onChange(self=" + selfChange + ") for " + uri
+ + " when mUri=" + mUri);
+ }
synchronized (mLock) {
final int N = mJobs.size();
for (int i=0; i<N; i++) {
@@ -255,14 +267,25 @@ public class ContentObserverController extends StateController {
for (JobInfo.TriggerContentUri uri : uris) {
ObserverInstance obs = mObservers.get(uri);
if (obs == null) {
- obs = new ObserverInstance(mHandler, uri.getUri());
+ obs = new ObserverInstance(mHandler, uri);
mObservers.put(uri, obs);
+ final boolean andDescendants = (uri.getFlags() &
+ JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
+ if (DEBUG) {
+ Slog.v(TAG, "New observer " + obs + " for " + uri.getUri()
+ + " andDescendants=" + andDescendants);
+ }
mContext.getContentResolver().registerContentObserver(
uri.getUri(),
- (uri.getFlags() &
- JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)
- != 0,
+ andDescendants,
obs);
+ } else {
+ if (DEBUG) {
+ final boolean andDescendants = (uri.getFlags() &
+ JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
+ Slog.v(TAG, "Reusing existing observer " + obs + " for " + uri.getUri()
+ + " andDescendants=" + andDescendants);
+ }
}
obs.mJobs.add(this);
mMyObservers.add(obs);
@@ -315,8 +338,11 @@ public class ContentObserverController extends StateController {
final ObserverInstance obs = mMyObservers.get(i);
obs.mJobs.remove(this);
if (obs.mJobs.size() == 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Unregistering observer " + obs + " for " + obs.mUri.getUri());
+ }
mContext.getContentResolver().unregisterContentObserver(obs);
- mObservers.remove(obs);
+ mObservers.remove(obs.mUri);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 73850de8681e..11c65250f94c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2563,7 +2563,22 @@ public class NotificationManagerService extends SystemService {
+ " id=" + id + " notification=" + notification);
}
- markAsSentFromNotification(notification);
+ // Whitelist pending intents.
+ if (notification.allPendingIntents != null) {
+ final int intentCount = notification.allPendingIntents.size();
+ if (intentCount > 0) {
+ final ActivityManagerInternal am = LocalServices
+ .getService(ActivityManagerInternal.class);
+ final long duration = LocalServices.getService(
+ DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
+ for (int i = 0; i < intentCount; i++) {
+ PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
+ if (pendingIntent != null) {
+ am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
+ }
+ }
+ }
+ }
// Sanitize inputs
notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
@@ -2579,67 +2594,6 @@ public class NotificationManagerService extends SystemService {
idOut[0] = id;
}
- private static void markAsSentFromNotification(Notification notification) {
- final ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
- final long duration = LocalServices.getService(DeviceIdleController.LocalService.class)
- .getNotificationWhitelistDuration();
-
- int size = 0;
- if (notification.contentIntent != null) {
- am.setPendingIntentWhitelistDuration(notification.contentIntent.getTarget(), duration);
- }
- if (notification.deleteIntent != null) {
- am.setPendingIntentWhitelistDuration(notification.deleteIntent.getTarget(), duration);
- }
- if (notification.fullScreenIntent != null) {
- am.setPendingIntentWhitelistDuration(notification.fullScreenIntent.getTarget(),
- duration);
- }
- if (notification.actions != null) {
- for (Notification.Action action: notification.actions) {
- if (action.actionIntent == null) {
- continue;
- }
- am.setPendingIntentWhitelistDuration(action.actionIntent.getTarget(), duration);
- setPendingIntentWhitelistDuration(am, duration, action.getExtras());
- final RemoteInput[] remoteInputs = action.getRemoteInputs();
- if (remoteInputs != null) {
- for (RemoteInput remoteInput : remoteInputs) {
- setPendingIntentWhitelistDuration(am, duration, remoteInput.getExtras());
- }
- }
- }
- }
- }
-
- private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
- Bundle extras) {
- for (String key : extras.keySet()) {
- final Object value = extras.get(key);
- if (value instanceof Parcelable) {
- setPendingIntentWhitelistDuration(am, duration, (Parcelable) value);
- } else if (value instanceof Parcelable[]) {
- for (Parcelable parcelable : (Parcelable[]) value) {
- setPendingIntentWhitelistDuration(am, duration, parcelable);
- }
- } else if (value instanceof List) {
- for (Object element : (List <?>) value) {
- if (element instanceof Parcelable) {
- setPendingIntentWhitelistDuration(am, duration, (Parcelable) element);
- }
- }
- }
- }
- }
-
- private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
- Parcelable parcelable) {
- if (parcelable instanceof PendingIntent) {
- am.setPendingIntentWhitelistDuration(((PendingIntent) parcelable).getTarget(),
- duration);
- }
- }
-
private class EnqueueNotificationRunnable implements Runnable {
private final NotificationRecord r;
private final int userId;