diff options
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">"Муну тутум жөндөөлөрүнөн кайра иштетүү > Колдонмолор > Жүктөлүп алынган."</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 — complete, share, or dismiss them — 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 Binary files differnew file mode 100644 index 000000000000..87e6c8c37124 --- /dev/null +++ b/docs/html/images/cards/card-secrets-playbook_2x.jpg diff --git a/docs/html/images/gp-secrets-playbook-lg.png b/docs/html/images/gp-secrets-playbook-lg.png Binary files differnew file mode 100644 index 000000000000..08b2a338b0e6 --- /dev/null +++ b/docs/html/images/gp-secrets-playbook-lg.png diff --git a/docs/html/images/gp-secrets-playbook.png b/docs/html/images/gp-secrets-playbook.png Binary files differnew file mode 100644 index 000000000000..831376b9aef2 --- /dev/null +++ b/docs/html/images/gp-secrets-playbook.png 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(…); + 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…: for listed containers<br> + * MPEG4, AAC…: 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…: for listed containers<br> + * MPEG4, AAC…: 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; |