Merge "Import translations. DO NOT MERGE"
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index eb07c05..5fe2f02 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -12,5 +12,8 @@
LOCAL_PACKAGE_NAME := CorePerfTests
+# Use google-fonts/dancing-script for the performance metrics
+LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
+
include $(BUILD_PACKAGE)
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
new file mode 100644
index 0000000..11ee599
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.perftests;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.Typeface;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceCreatePerfTest {
+ // A font file name in asset directory.
+ private static final String TEST_FONT_NAME = "DancingScript-Regular.ttf";
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testCreate_fromFamily() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
+ }
+ }
+
+ @Test
+ public void testCreate_fromFamilyName() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.create("monospace", Typeface.NORMAL);
+ }
+ }
+
+ @Test
+ public void testCreate_fromAsset() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ final AssetManager am = context.getAssets();
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.createFromAsset(am, TEST_FONT_NAME);
+ }
+ }
+
+ @Test
+ public void testCreate_fromFile() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ final AssetManager am = context.getAssets();
+
+ File outFile = null;
+ try {
+ outFile = File.createTempFile("example", "ttf", context.getCacheDir());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ try (InputStream in = am.open(TEST_FONT_NAME);
+ OutputStream out = new FileOutputStream(outFile)) {
+ byte[] buf = new byte[1024];
+ int n = 0;
+ while ((n = in.read(buf)) != -1) {
+ out.write(buf, 0, n);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.createFromFile(outFile);
+ }
+
+ outFile.delete();
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index 7bd88c7..7f39e14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21462,6 +21462,7 @@
field public static final int AMR_NB = 3; // 0x3
field public static final int AMR_WB = 4; // 0x4
field public static final int DEFAULT = 0; // 0x0
+ field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index c26cf71..8d6532b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -22994,6 +22994,7 @@
field public static final int AMR_NB = 3; // 0x3
field public static final int AMR_WB = 4; // 0x4
field public static final int DEFAULT = 0; // 0x0
+ field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
diff --git a/api/test-current.txt b/api/test-current.txt
index 7757246..3d03569 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -21535,6 +21535,7 @@
field public static final int AMR_NB = 3; // 0x3
field public static final int AMR_WB = 4; // 0x4
field public static final int DEFAULT = 0; // 0x0
+ field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 672df6d..b3d76d7 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.util.Slog;
+import dalvik.annotation.optimization.CriticalNative;
+
/**
* Core timekeeping facilities.
*
@@ -124,7 +126,7 @@
}
duration = start + ms - uptimeMillis();
} while (duration > 0);
-
+
if (interrupted) {
// Important: we don't want to quietly eat an interrupt() event,
// so we make sure to re-interrupt the thread so that the next
@@ -132,7 +134,7 @@
Thread.currentThread().interrupt();
}
}
-
+
/**
* Sets the current wall time, in milliseconds. Requires the calling
* process to have appropriate permissions.
@@ -162,6 +164,7 @@
*
* @return milliseconds of non-sleep uptime since boot.
*/
+ @CriticalNative
native public static long uptimeMillis();
/**
@@ -169,6 +172,7 @@
*
* @return elapsed milliseconds since boot.
*/
+ @CriticalNative
native public static long elapsedRealtime();
/**
@@ -176,30 +180,34 @@
*
* @return elapsed nanoseconds since boot.
*/
+ @CriticalNative
public static native long elapsedRealtimeNanos();
/**
* Returns milliseconds running in the current thread.
- *
+ *
* @return elapsed milliseconds in the thread
*/
+ @CriticalNative
public static native long currentThreadTimeMillis();
/**
* Returns microseconds running in the current thread.
- *
+ *
* @return elapsed microseconds in the thread
- *
+ *
* @hide
*/
+ @CriticalNative
public static native long currentThreadTimeMicro();
/**
* Returns current wall time in microseconds.
- *
+ *
* @return elapsed microseconds in wall time
- *
+ *
* @hide
*/
+ @CriticalNative
public static native long currentTimeMicro();
}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 3ae28fd..7e8cc0b8 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -16,6 +16,8 @@
package android.os;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Writes trace events to the system trace buffer. These trace events can be
* collected and visualized using the Systrace tool.
@@ -91,14 +93,20 @@
private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
private static native long nativeGetEnabledTags();
- private static native void nativeTraceCounter(long tag, String name, int value);
- private static native void nativeTraceBegin(long tag, String name);
- private static native void nativeTraceEnd(long tag);
- private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
- private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
private static native void nativeSetAppTracingAllowed(boolean allowed);
private static native void nativeSetTracingEnabled(boolean allowed);
+ @FastNative
+ private static native void nativeTraceCounter(long tag, String name, int value);
+ @FastNative
+ private static native void nativeTraceBegin(long tag, String name);
+ @FastNative
+ private static native void nativeTraceEnd(long tag);
+ @FastNative
+ private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
+ @FastNative
+ private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
+
static {
// We configure two separate change callbacks, one in Trace.cpp and one here. The
// native callback reads the tags from the system property, and this callback
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5a9a1ea..4393992 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -16,6 +16,7 @@
package android.view;
+import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
import android.os.Looper;
@@ -47,6 +48,7 @@
private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
MessageQueue messageQueue);
private static native void nativeDispose(long receiverPtr);
+ @FastNative
private static native void nativeScheduleVsync(long receiverPtr);
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index fab5364..3e8d577 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -23,6 +23,9 @@
import android.os.SystemClock;
import android.util.SparseArray;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Object used to report movement (mouse, pen, finger, trackball) events.
* Motion events may hold either absolute or relative movements and other data,
@@ -1445,60 +1448,98 @@
float xOffset, float yOffset, float xPrecision, float yPrecision,
long downTimeNanos, long eventTimeNanos,
int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords);
- private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
- boolean keepHistory);
private static native void nativeDispose(long nativePtr);
private static native void nativeAddBatch(long nativePtr, long eventTimeNanos,
PointerCoords[] pointerCoords, int metaState);
-
- private static native int nativeGetDeviceId(long nativePtr);
- private static native int nativeGetSource(long nativePtr);
- private static native int nativeSetSource(long nativePtr, int source);
- private static native int nativeGetAction(long nativePtr);
- private static native void nativeSetAction(long nativePtr, int action);
- private static native boolean nativeIsTouchEvent(long nativePtr);
- private static native int nativeGetFlags(long nativePtr);
- private static native void nativeSetFlags(long nativePtr, int flags);
- private static native int nativeGetEdgeFlags(long nativePtr);
- private static native void nativeSetEdgeFlags(long nativePtr, int action);
- private static native int nativeGetMetaState(long nativePtr);
- private static native int nativeGetButtonState(long nativePtr);
- private static native void nativeSetButtonState(long nativePtr, int buttonState);
- private static native int nativeGetActionButton(long nativePtr);
- private static native void nativeSetActionButton(long nativePtr, int actionButton);
- private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY);
- private static native float nativeGetXOffset(long nativePtr);
- private static native float nativeGetYOffset(long nativePtr);
- private static native float nativeGetXPrecision(long nativePtr);
- private static native float nativeGetYPrecision(long nativePtr);
- private static native long nativeGetDownTimeNanos(long nativePtr);
- private static native void nativeSetDownTimeNanos(long nativePtr, long downTime);
-
- private static native int nativeGetPointerCount(long nativePtr);
- private static native int nativeGetPointerId(long nativePtr, int pointerIndex);
- private static native int nativeGetToolType(long nativePtr, int pointerIndex);
- private static native int nativeFindPointerIndex(long nativePtr, int pointerId);
-
- private static native int nativeGetHistorySize(long nativePtr);
- private static native long nativeGetEventTimeNanos(long nativePtr, int historyPos);
- private static native float nativeGetRawAxisValue(long nativePtr,
- int axis, int pointerIndex, int historyPos);
- private static native float nativeGetAxisValue(long nativePtr,
- int axis, int pointerIndex, int historyPos);
private static native void nativeGetPointerCoords(long nativePtr,
int pointerIndex, int historyPos, PointerCoords outPointerCoords);
private static native void nativeGetPointerProperties(long nativePtr,
int pointerIndex, PointerProperties outPointerProperties);
- private static native void nativeScale(long nativePtr, float scale);
- private static native void nativeTransform(long nativePtr, Matrix matrix);
-
private static native long nativeReadFromParcel(long nativePtr, Parcel parcel);
private static native void nativeWriteToParcel(long nativePtr, Parcel parcel);
private static native String nativeAxisToString(int axis);
private static native int nativeAxisFromString(String label);
+ // -------------- @FastNative -------------------------
+
+ @FastNative
+ private static native int nativeGetPointerId(long nativePtr, int pointerIndex);
+ @FastNative
+ private static native int nativeGetToolType(long nativePtr, int pointerIndex);
+ @FastNative
+ private static native long nativeGetEventTimeNanos(long nativePtr, int historyPos);
+ @FastNative
+ private static native float nativeGetRawAxisValue(long nativePtr,
+ int axis, int pointerIndex, int historyPos);
+ @FastNative
+ private static native float nativeGetAxisValue(long nativePtr,
+ int axis, int pointerIndex, int historyPos);
+
+ // -------------- @CriticalNative ----------------------
+
+ @CriticalNative
+ private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
+ boolean keepHistory);
+ @CriticalNative
+ private static native int nativeGetDeviceId(long nativePtr);
+ @CriticalNative
+ private static native int nativeGetSource(long nativePtr);
+ @CriticalNative
+ private static native int nativeSetSource(long nativePtr, int source);
+ @CriticalNative
+ private static native int nativeGetAction(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetAction(long nativePtr, int action);
+ @CriticalNative
+ private static native boolean nativeIsTouchEvent(long nativePtr);
+ @CriticalNative
+ private static native int nativeGetFlags(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetFlags(long nativePtr, int flags);
+ @CriticalNative
+ private static native int nativeGetEdgeFlags(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetEdgeFlags(long nativePtr, int action);
+ @CriticalNative
+ private static native int nativeGetMetaState(long nativePtr);
+ @CriticalNative
+ private static native int nativeGetButtonState(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetButtonState(long nativePtr, int buttonState);
+ @CriticalNative
+ private static native int nativeGetActionButton(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetActionButton(long nativePtr, int actionButton);
+ @CriticalNative
+ private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY);
+ @CriticalNative
+ private static native float nativeGetXOffset(long nativePtr);
+ @CriticalNative
+ private static native float nativeGetYOffset(long nativePtr);
+ @CriticalNative
+ private static native float nativeGetXPrecision(long nativePtr);
+ @CriticalNative
+ private static native float nativeGetYPrecision(long nativePtr);
+ @CriticalNative
+ private static native long nativeGetDownTimeNanos(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetDownTimeNanos(long nativePtr, long downTime);
+
+ @CriticalNative
+ private static native int nativeGetPointerCount(long nativePtr);
+ @CriticalNative
+ private static native int nativeFindPointerIndex(long nativePtr, int pointerId);
+
+ @CriticalNative
+ private static native int nativeGetHistorySize(long nativePtr);
+
+ @CriticalNative
+ private static native void nativeScale(long nativePtr, float scale);
+ @CriticalNative
+ private static native void nativeTransform(long nativePtr, long matrix);
+
private MotionEvent() {
}
@@ -2065,7 +2106,7 @@
public final int getPointerCount() {
return nativeGetPointerCount(mNativePtr);
}
-
+
/**
* Return the pointer identifier associated with a particular pointer
* data index in this event. The identifier tells you the actual pointer
@@ -2891,7 +2932,7 @@
throw new IllegalArgumentException("matrix must not be null");
}
- nativeTransform(mNativePtr, matrix);
+ nativeTransform(mNativePtr, matrix.native_instance);
}
/**
diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp
index ccb833a..2fade69 100644
--- a/core/jni/android_os_SystemClock.cpp
+++ b/core/jni/android_os_SystemClock.cpp
@@ -36,29 +36,18 @@
namespace android {
-/*
- * native public static long uptimeMillis();
- */
-static jlong android_os_SystemClock_uptimeMillis(JNIEnv* env,
- jobject clazz)
-{
- return (jlong)uptimeMillis();
-}
-
-/*
- * native public static long elapsedRealtime();
- */
-static jlong android_os_SystemClock_elapsedRealtime(JNIEnv* env,
- jobject clazz)
-{
- return (jlong)elapsedRealtime();
-}
+static_assert(std::is_same<int64_t, jlong>::value, "jlong isn't an int64_t");
+static_assert(std::is_same<decltype(uptimeMillis()), int64_t>::value,
+ "uptimeMillis signature change, expected int64_t return value");
+static_assert(std::is_same<decltype(elapsedRealtime()), int64_t>::value,
+ "uptimeMillis signature change, expected int64_t return value");
+static_assert(std::is_same<decltype(elapsedRealtimeNano()), int64_t>::value,
+ "uptimeMillis signature change, expected int64_t return value");
/*
* native public static long currentThreadTimeMillis();
*/
-static jlong android_os_SystemClock_currentThreadTimeMillis(JNIEnv* env,
- jobject clazz)
+static jlong android_os_SystemClock_currentThreadTimeMillis()
{
struct timespec tm;
@@ -70,8 +59,7 @@
/*
* native public static long currentThreadTimeMicro();
*/
-static jlong android_os_SystemClock_currentThreadTimeMicro(JNIEnv* env,
- jobject clazz)
+static jlong android_os_SystemClock_currentThreadTimeMicro()
{
struct timespec tm;
@@ -83,8 +71,7 @@
/*
* native public static long currentTimeMicro();
*/
-static jlong android_os_SystemClock_currentTimeMicro(JNIEnv* env,
- jobject clazz)
+static jlong android_os_SystemClock_currentTimeMicro()
{
struct timeval tv;
@@ -93,31 +80,22 @@
}
/*
- * public static native long elapsedRealtimeNano();
- */
-static jlong android_os_SystemClock_elapsedRealtimeNano(JNIEnv* env,
- jobject clazz)
-{
- return (jlong)elapsedRealtimeNano();
-}
-
-/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "uptimeMillis", "!()J",
- (void*) android_os_SystemClock_uptimeMillis },
- { "elapsedRealtime", "!()J",
- (void*) android_os_SystemClock_elapsedRealtime },
- { "currentThreadTimeMillis", "!()J",
+ // All of these are @CriticalNative, so we can defer directly to SystemClock.h for
+ // some of these
+ { "uptimeMillis", "()J", (void*) uptimeMillis },
+ { "elapsedRealtime", "()J", (void*) elapsedRealtime },
+ { "elapsedRealtimeNanos", "()J", (void*) elapsedRealtimeNano },
+
+ // SystemClock doesn't have an implementation for these that we can directly call
+ { "currentThreadTimeMillis", "()J",
(void*) android_os_SystemClock_currentThreadTimeMillis },
- { "currentThreadTimeMicro", "!()J",
+ { "currentThreadTimeMicro", "()J",
(void*) android_os_SystemClock_currentThreadTimeMicro },
- { "currentTimeMicro", "!()J",
+ { "currentTimeMicro", "()J",
(void*) android_os_SystemClock_currentTimeMicro },
- { "elapsedRealtimeNanos", "!()J",
- (void*) android_os_SystemClock_elapsedRealtimeNano },
};
int register_android_os_SystemClock(JNIEnv* env)
{
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 30fc47b..ea893f0 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -110,27 +110,30 @@
{ "nativeGetEnabledTags",
"()J",
(void*)android_os_Trace_nativeGetEnabledTags },
- { "nativeTraceCounter",
- "!(JLjava/lang/String;I)V",
- (void*)android_os_Trace_nativeTraceCounter },
- { "nativeTraceBegin",
- "!(JLjava/lang/String;)V",
- (void*)android_os_Trace_nativeTraceBegin },
- { "nativeTraceEnd",
- "!(J)V",
- (void*)android_os_Trace_nativeTraceEnd },
- { "nativeAsyncTraceBegin",
- "!(JLjava/lang/String;I)V",
- (void*)android_os_Trace_nativeAsyncTraceBegin },
- { "nativeAsyncTraceEnd",
- "!(JLjava/lang/String;I)V",
- (void*)android_os_Trace_nativeAsyncTraceEnd },
{ "nativeSetAppTracingAllowed",
"(Z)V",
(void*)android_os_Trace_nativeSetAppTracingAllowed },
{ "nativeSetTracingEnabled",
"(Z)V",
(void*)android_os_Trace_nativeSetTracingEnabled },
+
+ // ----------- @FastNative ----------------
+
+ { "nativeTraceCounter",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeTraceCounter },
+ { "nativeTraceBegin",
+ "(JLjava/lang/String;)V",
+ (void*)android_os_Trace_nativeTraceBegin },
+ { "nativeTraceEnd",
+ "(J)V",
+ (void*)android_os_Trace_nativeTraceEnd },
+ { "nativeAsyncTraceBegin",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceBegin },
+ { "nativeAsyncTraceEnd",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceEnd },
};
int register_android_os_Trace(JNIEnv* env) {
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index ea5a760..2eada3e 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -161,7 +161,8 @@
{ "nativeDispose",
"(J)V",
(void*)nativeDispose },
- { "nativeScheduleVsync", "!(J)V",
+ // @FastNative
+ { "nativeScheduleVsync", "(J)V",
(void*)nativeScheduleVsync }
};
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 0245d38..2132f3d 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -383,17 +383,6 @@
return 0;
}
-static jlong android_view_MotionEvent_nativeCopy(JNIEnv* env, jclass clazz,
- jlong destNativePtr, jlong sourceNativePtr, jboolean keepHistory) {
- MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
- if (!destEvent) {
- destEvent = new MotionEvent();
- }
- MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
- destEvent->copyFrom(sourceEvent, keepHistory);
- return reinterpret_cast<jlong>(destEvent);
-}
-
static void android_view_MotionEvent_nativeDispose(JNIEnv* env, jclass clazz,
jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -426,228 +415,6 @@
event->setMetaState(event->getMetaState() | metaState);
}
-static jint android_view_MotionEvent_nativeGetDeviceId(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getDeviceId();
-}
-
-static jint android_view_MotionEvent_nativeGetSource(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getSource();
-}
-
-static void android_view_MotionEvent_nativeSetSource(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint source) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setSource(source);
-}
-
-static jint android_view_MotionEvent_nativeGetAction(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getAction();
-}
-
-static void android_view_MotionEvent_nativeSetAction(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint action) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setAction(action);
-}
-
-static int android_view_MotionEvent_nativeGetActionButton(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getActionButton();
-}
-
-static void android_view_MotionEvent_nativeSetActionButton(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint button) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setActionButton(button);
-}
-
-static jboolean android_view_MotionEvent_nativeIsTouchEvent(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->isTouchEvent();
-}
-
-static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getFlags();
-}
-
-static void android_view_MotionEvent_nativeSetFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint flags) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setFlags(flags);
-}
-
-static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getEdgeFlags();
-}
-
-static void android_view_MotionEvent_nativeSetEdgeFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint edgeFlags) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setEdgeFlags(edgeFlags);
-}
-
-static jint android_view_MotionEvent_nativeGetMetaState(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getMetaState();
-}
-
-static jint android_view_MotionEvent_nativeGetButtonState(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getButtonState();
-}
-
-static void android_view_MotionEvent_nativeSetButtonState(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint buttonState) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setButtonState(buttonState);
-}
-
-static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz,
- jlong nativePtr, jfloat deltaX, jfloat deltaY) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->offsetLocation(deltaX, deltaY);
-}
-
-static jfloat android_view_MotionEvent_nativeGetXOffset(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getXOffset();
-}
-
-static jfloat android_view_MotionEvent_nativeGetYOffset(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getYOffset();
-}
-
-static jfloat android_view_MotionEvent_nativeGetXPrecision(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getXPrecision();
-}
-
-static jfloat android_view_MotionEvent_nativeGetYPrecision(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getYPrecision();
-}
-
-static jlong android_view_MotionEvent_nativeGetDownTimeNanos(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getDownTime();
-}
-
-static void android_view_MotionEvent_nativeSetDownTimeNanos(JNIEnv* env, jclass clazz,
- jlong nativePtr, jlong downTimeNanos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setDownTime(downTimeNanos);
-}
-
-static jint android_view_MotionEvent_nativeGetPointerCount(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->getPointerCount());
-}
-
-static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint pointerIndex) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return -1;
- }
- return event->getPointerId(pointerIndex);
-}
-
-static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint pointerIndex) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return -1;
- }
- return event->getToolType(pointerIndex);
-}
-
-static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint pointerId) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->findPointerIndex(pointerId));
-}
-
-static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->getHistorySize());
-}
-
-static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint historyPos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- if (historyPos == HISTORY_CURRENT) {
- return event->getEventTime();
- } else {
- size_t historySize = event->getHistorySize();
- if (!validateHistoryPos(env, historyPos, historySize)) {
- return 0;
- }
- return event->getHistoricalEventTime(historyPos);
- }
-}
-
-static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return 0;
- }
-
- if (historyPos == HISTORY_CURRENT) {
- return event->getRawAxisValue(axis, pointerIndex);
- } else {
- size_t historySize = event->getHistorySize();
- if (!validateHistoryPos(env, historyPos, historySize)) {
- return 0;
- }
- return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
- }
-}
-
-static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return 0;
- }
-
- if (historyPos == HISTORY_CURRENT) {
- return event->getAxisValue(axis, pointerIndex);
- } else {
- size_t historySize = event->getHistorySize();
- if (!validateHistoryPos(env, historyPos, historySize)) {
- return 0;
- }
- return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
- }
-}
-
static void android_view_MotionEvent_nativeGetPointerCoords(JNIEnv* env, jclass clazz,
jlong nativePtr, jint pointerIndex, jint historyPos, jobject outPointerCoordsObj) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -684,30 +451,6 @@
pointerPropertiesFromNative(env, pointerProperties, outPointerPropertiesObj);
}
-static void android_view_MotionEvent_nativeScale(JNIEnv* env, jclass clazz,
- jlong nativePtr, jfloat scale) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->scale(scale);
-}
-
-static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
- jlong nativePtr, jobject matrixObj) {
- SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-
- float m[9];
- m[0] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleX));
- m[1] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewX));
- m[2] = SkScalarToFloat(matrix->get(SkMatrix::kMTransX));
- m[3] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewY));
- m[4] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleY));
- m[5] = SkScalarToFloat(matrix->get(SkMatrix::kMTransY));
- m[6] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp0));
- m[7] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp1));
- m[8] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp2));
- event->transform(m);
-}
-
static jlong android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass clazz,
jlong nativePtr, jobject parcelObj) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -750,6 +493,243 @@
return static_cast<jint>(MotionEvent::getAxisFromLabel(axisLabel.c_str()));
}
+// ---------------- @FastNative ----------------------------------
+
+static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint pointerIndex) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return -1;
+ }
+ return event->getPointerId(pointerIndex);
+}
+
+static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint pointerIndex) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return -1;
+ }
+ return event->getToolType(pointerIndex);
+}
+
+static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint historyPos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ if (historyPos == HISTORY_CURRENT) {
+ return event->getEventTime();
+ } else {
+ size_t historySize = event->getHistorySize();
+ if (!validateHistoryPos(env, historyPos, historySize)) {
+ return 0;
+ }
+ return event->getHistoricalEventTime(historyPos);
+ }
+}
+
+static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint axis,
+ jint pointerIndex, jint historyPos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return 0;
+ }
+
+ if (historyPos == HISTORY_CURRENT) {
+ return event->getRawAxisValue(axis, pointerIndex);
+ } else {
+ size_t historySize = event->getHistorySize();
+ if (!validateHistoryPos(env, historyPos, historySize)) {
+ return 0;
+ }
+ return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
+ }
+}
+
+static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return 0;
+ }
+
+ if (historyPos == HISTORY_CURRENT) {
+ return event->getAxisValue(axis, pointerIndex);
+ } else {
+ size_t historySize = event->getHistorySize();
+ if (!validateHistoryPos(env, historyPos, historySize)) {
+ return 0;
+ }
+ return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
+ }
+}
+
+// ----------------- @CriticalNative ------------------------------
+
+static jlong android_view_MotionEvent_nativeCopy(jlong destNativePtr, jlong sourceNativePtr,
+ jboolean keepHistory) {
+ MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
+ if (!destEvent) {
+ destEvent = new MotionEvent();
+ }
+ MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
+ destEvent->copyFrom(sourceEvent, keepHistory);
+ return reinterpret_cast<jlong>(destEvent);
+}
+
+static jint android_view_MotionEvent_nativeGetDeviceId(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getDeviceId();
+}
+
+static jint android_view_MotionEvent_nativeGetSource(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getSource();
+}
+
+static void android_view_MotionEvent_nativeSetSource(jlong nativePtr, jint source) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setSource(source);
+}
+
+static jint android_view_MotionEvent_nativeGetAction(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getAction();
+}
+
+static void android_view_MotionEvent_nativeSetAction(jlong nativePtr, jint action) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setAction(action);
+}
+
+static int android_view_MotionEvent_nativeGetActionButton(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getActionButton();
+}
+
+static void android_view_MotionEvent_nativeSetActionButton(jlong nativePtr, jint button) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setActionButton(button);
+}
+
+static jboolean android_view_MotionEvent_nativeIsTouchEvent(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->isTouchEvent();
+}
+
+static jint android_view_MotionEvent_nativeGetFlags(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getFlags();
+}
+
+static void android_view_MotionEvent_nativeSetFlags(jlong nativePtr, jint flags) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setFlags(flags);
+}
+
+static jint android_view_MotionEvent_nativeGetEdgeFlags(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getEdgeFlags();
+}
+
+static void android_view_MotionEvent_nativeSetEdgeFlags(jlong nativePtr, jint edgeFlags) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setEdgeFlags(edgeFlags);
+}
+
+static jint android_view_MotionEvent_nativeGetMetaState(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getMetaState();
+}
+
+static jint android_view_MotionEvent_nativeGetButtonState(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getButtonState();
+}
+
+static void android_view_MotionEvent_nativeSetButtonState(jlong nativePtr, jint buttonState) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setButtonState(buttonState);
+}
+
+static void android_view_MotionEvent_nativeOffsetLocation(jlong nativePtr, jfloat deltaX,
+ jfloat deltaY) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->offsetLocation(deltaX, deltaY);
+}
+
+static jfloat android_view_MotionEvent_nativeGetXOffset(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getXOffset();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYOffset(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getYOffset();
+}
+
+static jfloat android_view_MotionEvent_nativeGetXPrecision(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getXPrecision();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYPrecision(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getYPrecision();
+}
+
+static jlong android_view_MotionEvent_nativeGetDownTimeNanos(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getDownTime();
+}
+
+static void android_view_MotionEvent_nativeSetDownTimeNanos(jlong nativePtr, jlong downTimeNanos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setDownTime(downTimeNanos);
+}
+
+static jint android_view_MotionEvent_nativeGetPointerCount(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->getPointerCount());
+}
+
+static jint android_view_MotionEvent_nativeFindPointerIndex(jlong nativePtr, jint pointerId) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->findPointerIndex(pointerId));
+}
+
+static jint android_view_MotionEvent_nativeGetHistorySize(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->getHistorySize());
+}
+
+static void android_view_MotionEvent_nativeScale(jlong nativePtr, jfloat scale) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->scale(scale);
+}
+
+static void android_view_MotionEvent_nativeTransform(jlong nativePtr, jlong matrixPtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
+
+ static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
+ float m[9];
+ matrix->get9(m);
+ event->transform(m);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMotionEventMethods[] = {
@@ -758,117 +738,12 @@
"(JIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
"[Landroid/view/MotionEvent$PointerCoords;)J",
(void*)android_view_MotionEvent_nativeInitialize },
- { "nativeCopy",
- "(JJZ)J",
- (void*)android_view_MotionEvent_nativeCopy },
{ "nativeDispose",
"(J)V",
(void*)android_view_MotionEvent_nativeDispose },
{ "nativeAddBatch",
"(JJ[Landroid/view/MotionEvent$PointerCoords;I)V",
(void*)android_view_MotionEvent_nativeAddBatch },
- { "nativeGetDeviceId",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetDeviceId },
- { "nativeGetSource",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetSource },
- { "nativeSetSource",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeSetSource },
- { "nativeGetAction",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetAction },
- { "nativeSetAction",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetAction },
- { "nativeGetActionButton",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetActionButton},
- { "nativeSetActionButton",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetActionButton},
- { "nativeIsTouchEvent",
- "!(J)Z",
- (void*)android_view_MotionEvent_nativeIsTouchEvent },
- { "nativeGetFlags",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetFlags },
- { "nativeSetFlags",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetFlags },
- { "nativeGetEdgeFlags",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetEdgeFlags },
- { "nativeSetEdgeFlags",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetEdgeFlags },
- { "nativeGetMetaState",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetMetaState },
- { "nativeGetButtonState",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetButtonState },
- { "nativeSetButtonState",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetButtonState },
- { "nativeOffsetLocation",
- "!(JFF)V",
- (void*)android_view_MotionEvent_nativeOffsetLocation },
- { "nativeGetXOffset",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetXOffset },
- { "nativeGetYOffset",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetYOffset },
- { "nativeGetXPrecision",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetXPrecision },
- { "nativeGetYPrecision",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetYPrecision },
- { "nativeGetDownTimeNanos",
- "!(J)J",
- (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
- { "nativeSetDownTimeNanos",
- "!(JJ)V",
- (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
- { "nativeGetPointerCount",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetPointerCount },
- { "nativeGetPointerId",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeGetPointerId },
- { "nativeGetToolType",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeGetToolType },
- { "nativeFindPointerIndex",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeFindPointerIndex },
- { "nativeGetHistorySize",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetHistorySize },
- { "nativeGetEventTimeNanos",
- "!(JI)J",
- (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
- { "nativeGetRawAxisValue",
- "!(JIII)F",
- (void*)android_view_MotionEvent_nativeGetRawAxisValue },
- { "nativeGetAxisValue",
- "!(JIII)F",
- (void*)android_view_MotionEvent_nativeGetAxisValue },
- { "nativeGetPointerCoords",
- "(JIILandroid/view/MotionEvent$PointerCoords;)V",
- (void*)android_view_MotionEvent_nativeGetPointerCoords },
- { "nativeGetPointerProperties",
- "(JILandroid/view/MotionEvent$PointerProperties;)V",
- (void*)android_view_MotionEvent_nativeGetPointerProperties },
- { "nativeScale",
- "!(JF)V",
- (void*)android_view_MotionEvent_nativeScale },
- { "nativeTransform",
- "(JLandroid/graphics/Matrix;)V",
- (void*)android_view_MotionEvent_nativeTransform },
{ "nativeReadFromParcel",
"(JLandroid/os/Parcel;)J",
(void*)android_view_MotionEvent_nativeReadFromParcel },
@@ -879,6 +754,116 @@
(void*)android_view_MotionEvent_nativeAxisToString },
{ "nativeAxisFromString", "(Ljava/lang/String;)I",
(void*)android_view_MotionEvent_nativeAxisFromString },
+ { "nativeGetPointerProperties",
+ "(JILandroid/view/MotionEvent$PointerProperties;)V",
+ (void*)android_view_MotionEvent_nativeGetPointerProperties },
+ { "nativeGetPointerCoords",
+ "(JIILandroid/view/MotionEvent$PointerCoords;)V",
+ (void*)android_view_MotionEvent_nativeGetPointerCoords },
+
+ // --------------- @FastNative ----------------------
+ { "nativeGetPointerId",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeGetPointerId },
+ { "nativeGetToolType",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeGetToolType },
+ { "nativeGetEventTimeNanos",
+ "(JI)J",
+ (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
+ { "nativeGetRawAxisValue",
+ "(JIII)F",
+ (void*)android_view_MotionEvent_nativeGetRawAxisValue },
+ { "nativeGetAxisValue",
+ "(JIII)F",
+ (void*)android_view_MotionEvent_nativeGetAxisValue },
+
+ // --------------- @CriticalNative ------------------
+
+ { "nativeCopy",
+ "(JJZ)J",
+ (void*)android_view_MotionEvent_nativeCopy },
+ { "nativeGetDeviceId",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetDeviceId },
+ { "nativeGetSource",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetSource },
+ { "nativeSetSource",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeSetSource },
+ { "nativeGetAction",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetAction },
+ { "nativeSetAction",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetAction },
+ { "nativeGetActionButton",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetActionButton},
+ { "nativeSetActionButton",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetActionButton},
+ { "nativeIsTouchEvent",
+ "(J)Z",
+ (void*)android_view_MotionEvent_nativeIsTouchEvent },
+ { "nativeGetFlags",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetFlags },
+ { "nativeSetFlags",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetFlags },
+ { "nativeGetEdgeFlags",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetEdgeFlags },
+ { "nativeSetEdgeFlags",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetEdgeFlags },
+ { "nativeGetMetaState",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetMetaState },
+ { "nativeGetButtonState",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetButtonState },
+ { "nativeSetButtonState",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetButtonState },
+ { "nativeOffsetLocation",
+ "(JFF)V",
+ (void*)android_view_MotionEvent_nativeOffsetLocation },
+ { "nativeGetXOffset",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetXOffset },
+ { "nativeGetYOffset",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetYOffset },
+ { "nativeGetXPrecision",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetXPrecision },
+ { "nativeGetYPrecision",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetYPrecision },
+ { "nativeGetDownTimeNanos",
+ "(J)J",
+ (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
+ { "nativeSetDownTimeNanos",
+ "(JJ)V",
+ (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
+ { "nativeGetPointerCount",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetPointerCount },
+ { "nativeFindPointerIndex",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeFindPointerIndex },
+ { "nativeGetHistorySize",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetHistorySize },
+ { "nativeScale",
+ "(JF)V",
+ (void*)android_view_MotionEvent_nativeScale },
+ { "nativeTransform",
+ "(JJ)V",
+ (void*)android_view_MotionEvent_nativeTransform },
};
int register_android_view_MotionEvent(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 649cc1e..309bb2f 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -954,7 +954,7 @@
const char* const kClassPathName = "android/view/ThreadedRenderer";
static const JNINativeMethod gMethods[] = {
- { "nSupportsOpenGL", "!()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL },
+ { "nSupportsOpenGL", "()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL },
{ "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas },
{ "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
{ "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
diff --git a/core/res/res/color/background_cache_hint_selector_device_default.xml b/core/res/res/color/background_cache_hint_selector_device_default.xml
new file mode 100644
index 0000000..3cb4bbc
--- /dev/null
+++ b/core/res/res/color/background_cache_hint_selector_device_default.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_accelerated="false" android:color="?attr/colorBackground" />
+ <item android:color="@android:color/transparent" />
+</selector>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 083a869..7cb46fe 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -1357,7 +1357,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB disk"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB pohrana"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Uredi"</string>
- <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozorenje o korištenju podataka"</string>
+ <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozorenje o prijenosu podataka"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"Dodirnite za prikaz upotrebe i postavki."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Dostignut limit za 2G-3G podatke"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Dostignut limit za 4G podatke"</string>
@@ -1598,7 +1598,7 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Ažurirao administrator"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Izbrisao administrator"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Da bi se trajanje baterije produžilo, opcija za štednju baterije minimizira rad uređaja i ograničava vibriranje, usluge lokacije i većinu prijenosa podataka u pozadini. E-pošta, poruke i druge aplikacije koje se oslanjaju na sinhronizaciju ne mogu biti ažurirane dok ih ne otvorite.\n\nŠtednja baterije se automatski isključi prilikom punjenja uređaja."</string>
- <string name="data_saver_description" msgid="6015391409098303235">"Da bi se smanjilo korištenje podataka, usluga Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali se to može desiti rjeđe. To može značiti, naprimjer, da se slike ne prikazuju sve dok ih ne dodirnete."</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"Da bi se smanjio prijenos podataka, usluga Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali se to može desiti rjeđe. To može značiti, naprimjer, da se slike ne prikazuju sve dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"Uključiti Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"Uključi"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index af47a9a..beb6a67 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1329,7 +1329,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ഡ്രൈവ്"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB സ്റ്റോറേജ്"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"എഡിറ്റുചെയ്യുക"</string>
- <string name="data_usage_warning_title" msgid="3620440638180218181">"ഡാറ്റാ ഉപയോഗ അലേർട്ട്"</string>
+ <string name="data_usage_warning_title" msgid="3620440638180218181">"ഡാറ്റാ ഉപയോഗ മുന്നറിയിപ്പ്"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"ഉപയോഗവും ക്രമീകരണവും കാണാൻ ടാപ്പുചെയ്യുക."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ഡാറ്റ പരിധിയിലെത്തി"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ഡാറ്റ പരിധിയിലെത്തി"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 87884b7..d3476aa 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -363,7 +363,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ਐਪ ਨੂੰ ਤੁਹਾਡਾ ਅਨੁਮਾਨਿਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਨੈੱਟਵਰਕ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸਰੋਤ ਵਰਤਦੇ ਹੋਏ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਰਾਹੀਂ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਜਿਵੇਂ ਸੈਲ ਟਾਵਰ ਅਤੇ Wi-Fi. ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਚਾਲੂ ਅਤੇ ਐਪ ਨੂੰ ਉਹਨਾਂ ਨੂੰ ਵਰਤਣ ਲਈ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਤੇ ਹੋਣੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ। ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਇਹ ਅਨੁਮਾਨ ਲਗਾਉਣ ਲਈ ਕਰ ਸਕਦੇ ਹਨ ਕਿ ਤੁਸੀਂ ਕਿੱਥੇ ਹੋ।"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ਆਪਣੀਆਂ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ਔਪ ਨੂੰ ਗਲੋਬਲ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਵੌਲਿਊਮ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ਐਪ ਨੂੰ ਮਾਈਕ੍ਰੋਫੋਨ ਨਾਲ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਅਨੁਮਤੀ ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 84868a2..a7571ef 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -218,7 +218,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Opcje telefonu"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Blokada ekranu"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Wyłącz"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Alarmowe"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Alarmowy"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Zgłoszenie błędu"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Zgłoś błąd"</string>
<string name="bugreport_message" msgid="398447048750350456">"Informacje o bieżącym stanie urządzenia zostaną zebrane i wysłane e-mailem. Przygotowanie zgłoszenia błędu do wysłania chwilę potrwa, więc zachowaj cierpliwość."</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 5c1e5ba..5b7cf74 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1099,7 +1099,7 @@
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct yoniq"</string>
<string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Sozlamalarni ochish uchun bosing"</string>
<string name="accept" msgid="1645267259272829559">"Qabul qilish"</string>
- <string name="decline" msgid="2112225451706137894">"Rad qilish"</string>
+ <string name="decline" msgid="2112225451706137894">"Rad etish"</string>
<string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"taklif jo‘natildi"</string>
<string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"ulanish taklifi"</string>
<string name="wifi_p2p_from_message" msgid="570389174731951769">"Kimdan:"</string>
@@ -1113,7 +1113,7 @@
<string name="sms_control_title" msgid="7296612781128917719">"SMS xabarlar yuborilmoqda"</string>
<string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> katta miqdordagi SMS xabarlarini jo‘natmoqda. Ushbu ilovaga xabarlar jo‘natishni davom ettirishga ruxsat berasizmi?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"Ruxsat berish"</string>
- <string name="sms_control_no" msgid="625438561395534982">"Rad qilish"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"Rad etish"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>ga xabar jo‘natishni xohlaydi."</string>
<string name="sms_short_code_details" msgid="5873295990846059400">"Bunda, mobil hisobingizdan "<b>"to‘lov olinishi mumkin"</b>"."</string>
<string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Bunda, mobil hisobingizdan to‘lov olinishi mumkin."</b></string>
@@ -1223,7 +1223,7 @@
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Ushbu so‘rovga ruxsat berishni xohlaysizmi?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"Ruxsat so‘rovi"</string>
<string name="allow" msgid="7225948811296386551">"Ruxsat berish"</string>
- <string name="deny" msgid="2081879885755434506">"Rad qilish"</string>
+ <string name="deny" msgid="2081879885755434506">"Rad etish"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"Ruxsat so‘raldi"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"<xliff:g id="ACCOUNT">%s</xliff:g> hisobi uchun\nruxsat so‘raldi"</string>
<string name="forward_intent_to_owner" msgid="1207197447013960896">"Siz ushbu ilovadan ishchi profilingizdan tashqarida foydalanmoqdasiz"</string>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 2313b26..0471444 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -32,12 +32,12 @@
-->
<resources>
<!-- Theme used for the intent picker activity. -->
- <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material">
+ <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault">
<item name="colorControlActivated">?attr/colorControlHighlight</item>
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
</style>
<!-- Use a dark theme for watches. -->
- <style name="Theme.DeviceDefault.System" parent="Theme.Material" />
+ <style name="Theme.DeviceDefault.System" />
</resources>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 27ee27b..171f5cb 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -32,4 +32,11 @@
<color name="accent_device_default_light">@color/accent_material_light</color>
<color name="accent_device_default_dark">@color/accent_material_dark</color>
<color name="accent_device_default_50">@color/material_deep_teal_50</color>
+
+ <color name="background_device_default_dark">@color/background_material_dark</color>
+ <color name="background_device_default_light">@color/background_material_light</color>
+ <color name="background_floating_device_default_dark">@color/background_floating_material_dark</color>
+ <color name="background_floating_device_default_light">@color/background_floating_material_light</color>
+ <color name="button_normal_device_default_dark">@color/btn_default_material_dark</color>
+ <color name="button_normal_device_default_light">@color/btn_default_material_light</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 85838ed..3d1acdf5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2274,21 +2274,6 @@
<!-- Sprint need a 70 ms delay for 3way call -->
<integer name="config_cdma_3waycall_flash_delay">0</integer>
- <!-- If there is no preload VM number in the sim card, carriers such as
- Verizon require to load a default vm number from the configurantion.
- Define config_default_vm_number for this purpose. And there are two
- optional formats for this configuration as below:
- (1)<item>voicemail number</item>
- (2)<item>voicemail number;gid</item>
- The logic to pick up the correct voicemail number:
- (1) If the config_default_vm_number array has no gid special item, the last one will be
- picked
- (2) If the config_default_vm_number array has gid special item and it matches the current
- sim's gid, it will be picked.
- (3) If the config_default_vm_number array has gid special item but it doesn't match the
- current sim's gid, the last one without gid will be picked -->
- <string-array translatable="false" name="config_default_vm_number" />
-
<!--SIM does not save, but the voice mail number to be changed. -->
<bool name="editable_voicemailnumber">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 62997f7..019a5e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2266,7 +2266,6 @@
<java-symbol type="attr" name="closeItemLayout" />
<java-symbol type="layout" name="resolver_different_item_header" />
<java-symbol type="integer" name="config_cdma_3waycall_flash_delay"/>
- <java-symbol type="array" name="config_default_vm_number" />
<java-symbol type="attr" name="windowBackgroundFallback" />
<java-symbol type="id" name="textSpacerNoButtons" />
<java-symbol type="array" name="dial_string_replace" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0e98ade..abb0c8f 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -203,45 +203,42 @@
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
-
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+ <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
- <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <item name="windowFullscreen">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar.Overscan">
+ <item name="windowFullscreen">true</item>
+ <item name="windowOverscan">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor">
+ <item name="windowTranslucentStatus">true</item>
+ <item name="windowTranslucentNavigation">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -261,32 +258,29 @@
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+ <item name="colorBackground">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Dialog.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
for a regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -307,66 +301,52 @@
<!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.DialogWhenLarge" parent="@style/Theme.DeviceDefault" />
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.NoActionBar" />
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.Dialog.Presentation" parent="@style/Theme.DeviceDefault.NoActionBar.Fullscreen" />
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
- <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Panel">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowFrame">@null</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowAnimationStyle">@null</item>
+ <item name="windowIsFloating">true</item>
+ <item name="backgroundDimEnabled">false</item>
+ <item name="windowIsTranslucent">true</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them. -->
- <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Wallpaper">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowShowWallpaper">true</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them and without an action bar. -->
- <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar">
+ <item name="windowNoTitle">true</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
{@link android.inputmethodservice.InputMethodService} class.-->
- <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.InputMethod">
+ <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+ <item name="imeFullscreenBackground">@drawable/screen_background_selector_light</item>
+ <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+ <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -551,53 +531,58 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+ <item name="colorButtonNormal">@color/button_normal_device_default_light</item>
</style>
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
inverse color profile. -->
- <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar">
+ <style name="Theme.DeviceDefault.Light.DarkActionBar">
+ <item name="actionBarWidgetTheme">@null</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.Material.Dark.ActionBar</item>
+ <item name="popupTheme">@style/ThemeOverlay.Material.Light</item>
+
<!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
- <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
This theme sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
+ <item name="windowFullscreen">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
and extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan">
+ <item name="windowFullscreen">true</item>
+ <item name="windowOverscan">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor">
+ <item name="windowTranslucentStatus">true</item>
+ <item name="windowTranslucentNavigation">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -617,32 +602,29 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Dialog.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
width for a regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -651,11 +633,6 @@
<item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
<item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
<item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -664,39 +641,19 @@
<item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
<item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
<item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="@style/Theme.DeviceDefault.Light" />
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.Light.NoActionBar" />
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="@style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen" />
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
@@ -706,6 +663,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -715,6 +675,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
@@ -722,6 +685,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -729,6 +695,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -738,6 +707,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- DeviceDefault theme for a window that should use Settings theme colors but has
@@ -749,6 +721,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -758,6 +733,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
@@ -766,6 +744,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
@@ -774,6 +755,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -782,6 +766,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- Theme used for the intent picker activity. -->
@@ -800,6 +787,9 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
</style>
<!-- DeviceDefault theme for the default system theme. -->
diff --git a/docs/html/wear/preview/downloads.jd b/docs/html/wear/preview/downloads.jd
index 83a3f98..da6a06d 100644
--- a/docs/html/wear/preview/downloads.jd
+++ b/docs/html/wear/preview/downloads.jd
@@ -653,6 +653,12 @@
"{@docRoot}studio/intro/update.html#sdk-manager">SDK Manager</a>.
</p>
+ <p>
+ After you create a virtual device as described below, follow the steps for
+ <a href="#set_up_a_phone">setting up a phone</a> with the beta version of
+ the Android Wear companion app.
+ </p>
+
<p>Create a new virtual device in Android Studio as follows:
</p>
@@ -696,7 +702,9 @@
</p>
<ol>
- <li>On the phone, install the Android Wear app from Google Play.
+ <li>Follow the steps for
+ <a href="#set_up_a_phone">setting up a phone</a> with the beta version of
+ the Android Wear companion app.
</li>
<li>On the phone, enable Developer Options and USB Debugging.
@@ -740,7 +748,7 @@
in the <a href=
"{@docRoot}tools/devices/emulator.html">Android Emulator</a>. For more
information about using virtual devices, see <a href=
- "{@docRoot}tools/devices/managing-avds.html">
+ "{@docRoot}studio/run/managing-avds.html">
Create and Manage Virtual Devices</a>.
</p>
diff --git a/docs/html/wear/preview/features/app-distribution.jd b/docs/html/wear/preview/features/app-distribution.jd
index 319efa6..afc9516 100644
--- a/docs/html/wear/preview/features/app-distribution.jd
+++ b/docs/html/wear/preview/features/app-distribution.jd
@@ -132,6 +132,9 @@
<pre>
android {
+ // Allows you to reference product flavors in your
+ // phone module's build.gradle file
+ publishNonDefault true
...
defaultConfig
{
@@ -148,6 +151,7 @@
minSdkVersion 24
}
}
+}
</pre>
<p>
@@ -158,7 +162,7 @@
<pre>
dependencies {
...
- wearApp project(path: ':wearable', configuration: 'wear1Release')
+ wearApp project(path: ':wear', configuration: 'wear1Release')
}
</pre>
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 171532b..4a42e2b 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -1837,6 +1837,7 @@
Asset* AssetManager::SharedZip::getResourceTableAsset()
{
+ AutoMutex _l(gLock);
ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
return mResourceTableAsset;
}
@@ -1846,10 +1847,10 @@
{
AutoMutex _l(gLock);
if (mResourceTableAsset == NULL) {
- mResourceTableAsset = asset;
// This is not thread safe the first time it is called, so
// do it here with the global lock held.
asset->getBuffer(true);
+ mResourceTableAsset = asset;
return asset;
}
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index dd20a76..8c36ab5 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -229,19 +229,6 @@
}
}
-static OffscreenBuffer* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
- return renderState.layerPool().get(renderState, width, height);
-}
-
-static void destroyLayer(OffscreenBuffer* layer) {
- RenderState& renderState = layer->renderState;
- renderState.layerPool().putOrDelete(layer);
-}
-
-static bool layerMatchesWidthAndHeight(OffscreenBuffer* layer, int width, int height) {
- return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height;
-}
-
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
@@ -251,34 +238,15 @@
|| CC_UNLIKELY(properties().getWidth() == 0)
|| CC_UNLIKELY(properties().getHeight() == 0)) {
if (CC_UNLIKELY(mLayer)) {
- destroyLayer(mLayer);
- mLayer = nullptr;
+ renderthread::CanvasContext::destroyLayer(this);
}
return;
}
- bool transformUpdateNeeded = false;
- if (!mLayer) {
- mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+ if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) {
damageSelf(info);
- transformUpdateNeeded = true;
- } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
- // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
- // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
- RenderState& renderState = mLayer->renderState;
- if (properties().fitsOnLayer()) {
- mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
- } else {
- destroyLayer(mLayer);
- mLayer = nullptr;
- }
- damageSelf(info);
- transformUpdateNeeded = true;
}
- SkRect dirty;
- info.damageAccumulator->peekAtDirty(&dirty);
-
if (!mLayer) {
Caches::getInstance().dumpMemoryUsage();
if (info.errorHandler) {
@@ -296,13 +264,8 @@
return;
}
- if (transformUpdateNeeded && mLayer) {
- // update the transform in window of the layer to reset its origin wrt light source position
- Matrix4 windowTransform;
- info.damageAccumulator->computeCurrentTransform(&windowTransform);
- mLayer->setWindowTransform(windowTransform);
- }
-
+ SkRect dirty;
+ info.damageAccumulator->peekAtDirty(&dirty);
info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
// There might be prefetched layers that need to be accounted for.
@@ -451,8 +414,7 @@
void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
if (mLayer) {
- destroyLayer(mLayer);
- mLayer = nullptr;
+ renderthread::CanvasContext::destroyLayer(this);
}
if (mDisplayList) {
for (auto&& child : mDisplayList->getChildren()) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index a0679b1..da93c13 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -199,6 +199,7 @@
}
OffscreenBuffer* getLayer() const { return mLayer; }
OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
+ void setLayer(OffscreenBuffer* layer) { mLayer = layer; }
// Note: The position callbacks are relying on the listener using
// the frameNumber to appropriately batch/synchronize these transactions.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 43471e5..0f2d55b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -85,6 +85,18 @@
return nullptr;
}
+void CanvasContext::destroyLayer(RenderNode* node) {
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ OpenGLPipeline::destroyLayer(node);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+}
+
CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory,
std::unique_ptr<IRenderPipeline> renderPipeline)
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 7ebe0ae..652cddd 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -68,6 +68,23 @@
RenderNode* rootRenderNode, IContextFactory* contextFactory);
virtual ~CanvasContext();
+ /**
+ * Update or create a layer specific for the provided RenderNode. The layer
+ * attached to the node will be specific to the RenderPipeline used by this
+ * context
+ *
+ * @return true if the layer has been created or updated
+ */
+ bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator) {
+ return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator);
+ }
+
+ /**
+ * Destroy any layers that have been attached to the provided RenderNode removing
+ * any state that may have been set during createOrUpdateLayer().
+ */
+ static void destroyLayer(RenderNode* node);
+
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 3250fed..97cdf7f 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -67,6 +67,8 @@
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const BakedOpRenderer::LightInfo& lightInfo) = 0;
virtual TaskManager* getTaskManager() = 0;
+ virtual bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) = 0;
virtual ~IRenderPipeline() {}
};
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index c8971f8..c758f6c 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -183,6 +183,46 @@
return &Caches::getInstance().tasks;
}
+static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
+ return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
+}
+
+bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) {
+ RenderState& renderState = mRenderThread.renderState();
+ OffscreenBufferPool& layerPool = renderState.layerPool();
+ bool transformUpdateNeeded = false;
+ if (node->getLayer() == nullptr) {
+ node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
+ transformUpdateNeeded = true;
+ } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
+ // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
+ // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
+ if (node->properties().fitsOnLayer()) {
+ node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
+ } else {
+ destroyLayer(node);
+ }
+ transformUpdateNeeded = true;
+ }
+
+ if (transformUpdateNeeded && node->getLayer()) {
+ // update the transform in window of the layer to reset its origin wrt light source position
+ Matrix4 windowTransform;
+ damageAccumulator.computeCurrentTransform(&windowTransform);
+ node->getLayer()->setWindowTransform(windowTransform);
+ }
+
+ return transformUpdateNeeded;
+}
+
+void OpenGLPipeline::destroyLayer(RenderNode* node) {
+ if (OffscreenBuffer* layer = node->getLayer()) {
+ layer->renderState.layerPool().putOrDelete(layer);
+ node->setLayer(nullptr);
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index e08fd9b..34d9bc0 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -56,6 +56,9 @@
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const BakedOpRenderer::LightInfo& lightInfo) override;
TaskManager* getTaskManager() override;
+ bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) override;
+ static void destroyLayer(RenderNode* node);
private:
EglManager& mEglManager;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index abbace1..bd68fec 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -389,8 +389,8 @@
/** @hide Stream over a socket, limited to a single stream */
public static final int OUTPUT_FORMAT_RTP_AVP = 7;
- /** @hide H.264/AAC data encapsulated in MPEG2/TS */
- public static final int OUTPUT_FORMAT_MPEG2TS = 8;
+ /** H.264/AAC data encapsulated in MPEG2/TS */
+ public static final int MPEG_2_TS = 8;
/** VP8/VORBIS data in a WEBM container */
public static final int WEBM = 9;
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 88b566a..5f0d3df 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -31,7 +31,7 @@
struct AMessage;
struct AString;
struct ICrypto;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
struct MediaCodec;
struct PersistentSurface;
class Surface;
diff --git a/packages/Keyguard/res/values-da/strings.xml b/packages/Keyguard/res/values-da/strings.xml
index ebea6ee..b98a253 100644
--- a/packages/Keyguard/res/values-da/strings.xml
+++ b/packages/Keyguard/res/values-da/strings.xml
@@ -128,5 +128,5 @@
<item quantity="one">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
<item quantity="other">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Kan ikke genkendes"</string>
+ <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ikke genkendt"</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index a8f286d..812d33a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -21,8 +21,6 @@
import android.util.Log;
import android.util.Pair;
-import com.android.settingslib.applications.InterestingConfigChanges;
-
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -33,8 +31,6 @@
private static CategoryManager sInstance;
- private final InterestingConfigChanges mInterestingConfigChanges;
-
// Tile cache (key: <packageName, activityName>, value: tile)
private final Map<Pair<String, String>, Tile> mTileByComponentCache;
@@ -51,12 +47,11 @@
}
CategoryManager() {
- mInterestingConfigChanges = new InterestingConfigChanges();
mTileByComponentCache = new ArrayMap<>();
mCategoryByKeyMap = new ArrayMap<>();
}
- public DashboardCategory getTilesByCategory(Context context, String categoryKey) {
+ public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
tryInitCategories(context);
final DashboardCategory category = mCategoryByKeyMap.get(categoryKey);
@@ -66,19 +61,17 @@
return category;
}
- public List<DashboardCategory> getCategories(Context context) {
+ public synchronized List<DashboardCategory> getCategories(Context context) {
tryInitCategories(context);
return mCategories;
}
- public void reloadAllCategoriesForConfigChange(Context context) {
- if (mInterestingConfigChanges.applyNewConfig(context.getResources())) {
- mCategories = null;
- tryInitCategories(context);
- }
+ public synchronized void reloadAllCategories(Context context) {
+ mCategories = null;
+ tryInitCategories(context);
}
- public void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
+ public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
if (mCategories == null) {
Log.w(TAG, "Category is null, skipping blacklist update");
}
@@ -93,7 +86,7 @@
}
}
- private void tryInitCategories(Context context) {
+ private synchronized void tryInitCategories(Context context) {
if (mCategories == null) {
mTileByComponentCache.clear();
mCategoryByKeyMap.clear();
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 50867eb..bb6c558 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -434,7 +434,7 @@
@Override
protected Void doInBackground(Void... params) {
- mCategoryManager.reloadAllCategoriesForConfigChange(SettingsDrawerActivity.this);
+ mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this);
return null;
}
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index ca91502..6ce6dbe 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -238,7 +238,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G prijenos podataka je pauzirano"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilni podaci su pauzirani"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Prijenos podataka je pauziran"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dostigli ste ograničenje za podatke koje ste postavili. Više ne koristite mobilne podatke.\n\nUkoliko nastavite koristiti mobilne podatke, mogući su troškovi za korištenje podataka."</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dostigli ste ograničenje za prijenos podataka koje ste postavili. Više ne koristite mobilne podatke.\n\nUkoliko nastavite koristiti mobilne podatke, mogući su troškovi za prijenos podataka."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nastavi"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nema internet veze"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi veza aktivna"</string>
@@ -313,7 +313,7 @@
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavještenja"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svjetiljka"</string>
<string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Mobilni podaci"</string>
- <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Korištenje podataka"</string>
+ <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Prijenos podataka"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podataka"</string>
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Prekoračeno"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Iskorišteno <xliff:g id="DATA_USED">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 4485883..905c865 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -237,7 +237,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data ခေတ္တရပ်တန့်သည်"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"cellular data ခေတ္တရပ်တန့်သည်"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ဒေတာ ခေတ္တရပ်တန့်သည်"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"သင်သတ်မှတ်ထားသော ဒေတာကန့်သတ်ချက်သို့ ရောက်နေပါပြီ။ သင်သည် ဆယ်လူလာဒေတာကို သုံးနေတော့မည်မဟုတ်ပါ။\n\nသင်ဆက်လုပ်မည်ဆိုလျှင် ဒေတာသုံးစွဲမှုအတွက် အခငွေ ကျသင့်မှုရှိနိုင်ပါသည်။"</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"သင်သတ်မှတ်ထားသော ဒေတာကန့်သတ်ချက်သို့ ရောက်နေပါပြီ။ သင်သည် ဆယ်လူလာဒေတာကို အသုံးမပြုတော့ပါ။\n\nသင်ဆက်လုပ်မည်ဆိုလျှင် ဒေတာသုံးစွဲမှုအတွက် အခငွေ ကျသင့်မှုရှိနိုင်ပါသည်။"</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ပြန်ဆက်လုပ်ရန်"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"အင်တာနက်မရှိ"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ကြိုးမဲ့ဆက်သွယ်မှု"</string>
diff --git a/services/core/java/com/android/server/policy/RecentApplicationsBackground.java b/services/core/java/com/android/server/policy/RecentApplicationsBackground.java
deleted file mode 100644
index 694a110..0000000
--- a/services/core/java/com/android/server/policy/RecentApplicationsBackground.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * A vertical linear layout. However, instead of drawing the background
- * behnd the items, it draws the background outside the items based on the
- * padding. If there isn't enough room to draw both, it clips the background
- * instead of the contents.
- */
-public class RecentApplicationsBackground extends LinearLayout {
- private static final String TAG = "RecentApplicationsBackground";
-
- private boolean mBackgroundSizeChanged;
- private Drawable mBackground;
- private Rect mTmp0 = new Rect();
- private Rect mTmp1 = new Rect();
-
- public RecentApplicationsBackground(Context context) {
- this(context, null);
- init();
- }
-
- public RecentApplicationsBackground(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
-
- private void init() {
- mBackground = getBackground();
- setBackgroundDrawable(null);
- setPadding(0, 0, 0, 0);
- setGravity(Gravity.CENTER);
- }
-
- @Override
- protected boolean setFrame(int left, int top, int right, int bottom) {
- setWillNotDraw(false);
- if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
- mBackgroundSizeChanged = true;
- }
- return super.setFrame(left, top, right, bottom);
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return who == mBackground || super.verifyDrawable(who);
- }
-
- @Override
- public void jumpDrawablesToCurrentState() {
- super.jumpDrawablesToCurrentState();
- if (mBackground != null) mBackground.jumpToCurrentState();
- }
-
- @Override
- protected void drawableStateChanged() {
- Drawable d = mBackground;
- if (d != null && d.isStateful()) {
- d.setState(getDrawableState());
- }
- super.drawableStateChanged();
- }
-
- @Override
- public void draw(Canvas canvas) {
- final Drawable background = mBackground;
- if (background != null) {
- if (mBackgroundSizeChanged) {
- mBackgroundSizeChanged = false;
- Rect chld = mTmp0;
- Rect bkg = mTmp1;
- mBackground.getPadding(bkg);
- getChildBounds(chld);
- // This doesn't clamp to this view's bounds, which is what we want,
- // so that the drawing is clipped.
- final int top = chld.top - bkg.top;
- final int bottom = chld.bottom + bkg.bottom;
- // The background here is a gradient that wants to
- // extend the full width of the screen (whatever that
- // may be).
- int left, right;
- if (false) {
- // This limits the width of the drawable.
- left = chld.left - bkg.left;
- right = chld.right + bkg.right;
- } else {
- // This expands it to full width.
- left = 0;
- right = getRight();
- }
- background.setBounds(left, top, right, bottom);
- }
- }
- mBackground.draw(canvas);
-
- if (false) {
- android.graphics.Paint p = new android.graphics.Paint();
- p.setColor(0x88ffff00);
- canvas.drawRect(background.getBounds(), p);
- }
- canvas.drawARGB((int)(0.75*0xff), 0, 0, 0);
-
- super.draw(canvas);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mBackground.setCallback(this);
- setWillNotDraw(false);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mBackground.setCallback(null);
- }
-
- private void getChildBounds(Rect r) {
- r.left = r.top = Integer.MAX_VALUE;
- r.bottom = r.right = Integer.MIN_VALUE;
- final int N = getChildCount();
- for (int i=0; i<N; i++) {
- View v = getChildAt(i);
- if (v.getVisibility() == View.VISIBLE) {
- r.left = Math.min(r.left, v.getLeft());
- r.top = Math.min(r.top, v.getTop());
- r.right = Math.max(r.right, v.getRight());
- r.bottom = Math.max(r.bottom, v.getBottom());
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3dfcfe7..508cf24 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -368,11 +368,9 @@
@Override
boolean isVisible() {
- if (hidden) {
- // TODO: Should this be checking hiddenRequested instead of hidden?
- return false;
- }
- return super.isVisible();
+ // If the app token isn't hidden then it is considered visible and there is no need to check
+ // its children windows to see if they are visible.
+ return !hidden;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf360ba..aa4d7be3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1302,7 +1302,22 @@
@Override
boolean isVisible() {
- if ((mAppToken == null || !mAppToken.hiddenRequested) && isVisibleUnchecked()) {
+ // TODO: The check for hiddenRequested is commented out below, because the window can still
+ // be visible on screen when the flag is true. We would like the isVisible() method to
+ // return an answer closer to if the window is truly visible (can't be an exact answer
+ // without checking the surface state), so comment out the check for now so we can test to
+ // see what problem it causes.
+ // If it doesn't cause any issues, then we can remove just before we lock down the current
+ // release (O) and also consolidate this method with #isVisibleUnchecked() and possibly
+ // other methods like isVisibleNow().
+ // If it does cause problems, then we can look if there are other ways to solve the problem.
+ // If there isn't then uncomment and document here why it is needed.
+ if (/*(mAppToken == null || !mAppToken.hiddenRequested) && */isVisibleUnchecked()
+ // TODO: The window isn't considered visible when the token is hidden, however
+ // uncommenting the check below breaks the visual transition from an app to the launcher
+ // if the home buttons is pressed. Need to investigate an fix that issue before
+ // uncommenting.
+ /* && !mToken.hidden*/) {
// Is this window visible? It is not visible if there is no surface, or we are in the
// process of running an exit animation that will remove the surface, or its app token
// has been hidden.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6c7d136..dc83ac0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -966,7 +966,7 @@
mDtDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set((int) x, (int) y);
+ mWin.mShownPosition.set(Math.round(x), Math.round(y));
// Now set the alpha... but because our current hardware
// can't do alpha transformation on a non-opaque surface,
@@ -1059,7 +1059,7 @@
mDtDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set((int) x, (int) y);
+ mWin.mShownPosition.set(Math.round(x), Math.round(y));
mShownAlpha = mAlpha;
} else {
diff --git a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
index da4d150..e4473ad 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
@@ -147,6 +147,7 @@
userInfo.id = TEST_DEMO_USER;
when(mUm.createUser(anyString(), anyInt())).thenReturn(userInfo);
+ setCameraPackage(TEST_CAMERA_PKG);
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
assertEquals(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED + " property not set",
"1", mInjector.systemPropertiesGet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED));
@@ -157,7 +158,6 @@
assertTrue("Not registered for " + Intent.ACTION_SCREEN_OFF,
intentFilter.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
- setCameraPackage(TEST_CAMERA_PKG);
// Wait for the setup to complete.
mLatch.await(SETUP_COMPLETE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
ArgumentCaptor<Integer> flags = ArgumentCaptor.forClass(Integer.class);
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 18a1fbf..b57d4db 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -24,7 +24,10 @@
sources := \
compile/IdAssigner.cpp \
compile/InlineXmlFormatParser.cpp \
+ compile/NinePatch.cpp \
compile/Png.cpp \
+ compile/PngChunkFilter.cpp \
+ compile/PngCrunch.cpp \
compile/PseudolocaleGenerator.cpp \
compile/Pseudolocalizer.cpp \
compile/XmlIdCollector.cpp \
@@ -34,6 +37,7 @@
flatten/XmlFlattener.cpp \
io/File.cpp \
io/FileSystem.cpp \
+ io/Io.cpp \
io/ZipArchive.cpp \
link/AutoVersioner.cpp \
link/ManifestFixer.cpp \
@@ -84,6 +88,7 @@
testSources := \
compile/IdAssigner_test.cpp \
compile/InlineXmlFormatParser_test.cpp \
+ compile/NinePatch_test.cpp \
compile/PseudolocaleGenerator_test.cpp \
compile/Pseudolocalizer_test.cpp \
compile/XmlIdCollector_test.cpp \
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index e0f37ec..dbd8062 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -36,6 +36,8 @@
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <google/protobuf/io/coded_stream.h>
+#include <android-base/errors.h>
+#include <android-base/file.h>
#include <dirent.h>
#include <fstream>
#include <string>
@@ -359,6 +361,9 @@
static bool compileXml(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling XML");
+ }
std::unique_ptr<xml::XmlResource> xmlRes;
{
@@ -431,9 +436,43 @@
return true;
}
+class BigBufferOutputStream : public io::OutputStream {
+public:
+ explicit BigBufferOutputStream(BigBuffer* buffer) : mBuffer(buffer) {
+ }
+
+ bool Next(void** data, int* len) override {
+ size_t count;
+ *data = mBuffer->nextBlock(&count);
+ *len = static_cast<int>(count);
+ return true;
+ }
+
+ void BackUp(int count) override {
+ mBuffer->backUp(count);
+ }
+
+ int64_t ByteCount() const override {
+ return mBuffer->size();
+ }
+
+ bool HadError() const override {
+ return false;
+ }
+
+private:
+ BigBuffer* mBuffer;
+
+ DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
+};
+
static bool compilePng(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling PNG");
+ }
+
BigBuffer buffer(4096);
ResourceFile resFile;
resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
@@ -441,16 +480,90 @@
resFile.source = pathData.source;
{
- std::ifstream fin(pathData.source.path, std::ifstream::binary);
- if (!fin) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
+ std::string content;
+ if (!android::base::ReadFileToString(pathData.source.path, &content)) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source)
+ << android::base::SystemErrorCodeToString(errno));
return false;
}
- Png png(context->getDiagnostics());
- if (!png.process(pathData.source, &fin, &buffer, {})) {
+ BigBuffer crunchedPngBuffer(4096);
+ BigBufferOutputStream crunchedPngBufferOut(&crunchedPngBuffer);
+
+ // Ensure that we only keep the chunks we care about if we end up
+ // using the original PNG instead of the crunched one.
+ PngChunkFilter pngChunkFilter(content);
+ std::unique_ptr<Image> image = readPng(context, &pngChunkFilter);
+ if (!image) {
return false;
}
+
+ std::unique_ptr<NinePatch> ninePatch;
+ if (pathData.extension == "9.png") {
+ std::string err;
+ ninePatch = NinePatch::create(image->rows.get(), image->width, image->height, &err);
+ if (!ninePatch) {
+ context->getDiagnostics()->error(DiagMessage() << err);
+ return false;
+ }
+
+ // Remove the 1px border around the NinePatch.
+ // Basically the row array is shifted up by 1, and the length is treated
+ // as height - 2.
+ // For each row, shift the array to the left by 1, and treat the length as width - 2.
+ image->width -= 2;
+ image->height -= 2;
+ memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
+ for (int32_t h = 0; h < image->height; h++) {
+ memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
+ }
+
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "9-patch: " << *ninePatch);
+ }
+ }
+
+ // Write the crunched PNG.
+ if (!writePng(context, image.get(), ninePatch.get(), &crunchedPngBufferOut, {})) {
+ return false;
+ }
+
+ if (ninePatch != nullptr
+ || crunchedPngBufferOut.ByteCount() <= pngChunkFilter.ByteCount()) {
+ // No matter what, we must use the re-encoded PNG, even if it is larger.
+ // 9-patch images must be re-encoded since their borders are stripped.
+ buffer.appendBuffer(std::move(crunchedPngBuffer));
+ } else {
+ // The re-encoded PNG is larger than the original, and there is
+ // no mandatory transformation. Use the original.
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "original PNG is smaller than crunched PNG"
+ << ", using original");
+ }
+
+ PngChunkFilter pngChunkFilterAgain(content);
+ BigBuffer filteredPngBuffer(4096);
+ BigBufferOutputStream filteredPngBufferOut(&filteredPngBuffer);
+ io::copy(&filteredPngBufferOut, &pngChunkFilterAgain);
+ buffer.appendBuffer(std::move(filteredPngBuffer));
+ }
+
+ if (context->verbose()) {
+ // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
+ // This will help catch exotic cases where the new code may generate larger PNGs.
+ std::stringstream legacyStream(content);
+ BigBuffer legacyBuffer(4096);
+ Png png(context->getDiagnostics());
+ if (!png.process(pathData.source, &legacyStream, &legacyBuffer, {})) {
+ return false;
+ }
+
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "legacy=" << legacyBuffer.size()
+ << " new=" << buffer.size());
+ }
}
if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
@@ -463,6 +576,10 @@
static bool compileFile(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling file");
+ }
+
BigBuffer buffer(256);
ResourceFile resFile;
resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
diff --git a/tools/aapt2/compile/Image.h b/tools/aapt2/compile/Image.h
new file mode 100644
index 0000000..fda6a3a
--- /dev/null
+++ b/tools/aapt2/compile/Image.h
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_COMPILE_IMAGE_H
+#define AAPT_COMPILE_IMAGE_H
+
+#include <android-base/macros.h>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * An in-memory image, loaded from disk, with pixels in RGBA_8888 format.
+ */
+class Image {
+public:
+ explicit Image() = default;
+
+ /**
+ * A `height` sized array of pointers, where each element points to a
+ * `width` sized row of RGBA_8888 pixels.
+ */
+ std::unique_ptr<uint8_t*[]> rows;
+
+ /**
+ * The width of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
+ * format limitations.
+ */
+ int32_t width = 0;
+
+ /**
+ * The height of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
+ * format limitations.
+ */
+ int32_t height = 0;
+
+ /**
+ * Buffer to the raw image data stored sequentially.
+ * Use `rows` to access the data on a row-by-row basis.
+ */
+ std::unique_ptr<uint8_t[]> data;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Image);
+};
+
+/**
+ * A range of pixel values, starting at 'start' and ending before 'end' exclusive. Or rather [a, b).
+ */
+struct Range {
+ int32_t start = 0;
+ int32_t end = 0;
+
+ explicit Range() = default;
+ inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {
+ }
+};
+
+inline bool operator==(const Range& left, const Range& right) {
+ return left.start == right.start && left.end == right.end;
+}
+
+/**
+ * Inset lengths from all edges of a rectangle. `left` and `top` are measured from the left and top
+ * edges, while `right` and `bottom` are measured from the right and bottom edges, respectively.
+ */
+struct Bounds {
+ int32_t left = 0;
+ int32_t top = 0;
+ int32_t right = 0;
+ int32_t bottom = 0;
+
+ explicit Bounds() = default;
+ inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b) :
+ left(l), top(t), right(r), bottom(b) {
+ }
+
+ bool nonZero() const;
+};
+
+inline bool Bounds::nonZero() const {
+ return left != 0 || top != 0 || right != 0 || bottom != 0;
+}
+
+inline bool operator==(const Bounds& left, const Bounds& right) {
+ return left.left == right.left && left.top == right.top &&
+ left.right == right.right && left.bottom == right.bottom;
+}
+
+/**
+ * Contains 9-patch data from a source image. All measurements exclude the 1px border of the
+ * source 9-patch image.
+ */
+class NinePatch {
+public:
+ static std::unique_ptr<NinePatch> create(uint8_t** rows,
+ const int32_t width, const int32_t height,
+ std::string* errOut);
+
+ /**
+ * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
+ * with format 0xAARRGGBB (the way 9-patch expects it).
+ */
+ static uint32_t packRGBA(const uint8_t* pixel);
+
+ /**
+ * 9-patch content padding/insets. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ Bounds padding;
+
+ /**
+ * Optical layout bounds/insets. This overrides the padding for
+ * layout purposes. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ * See https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
+ */
+ Bounds layoutBounds;
+
+ /**
+ * Outline of the image, calculated based on opacity.
+ */
+ Bounds outline;
+
+ /**
+ * The computed radius of the outline. If non-zero, the outline is a rounded-rect.
+ */
+ float outlineRadius = 0.0f;
+
+ /**
+ * The largest alpha value within the outline.
+ */
+ uint32_t outlineAlpha = 0x000000ffu;
+
+ /**
+ * Horizontal regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> horizontalStretchRegions;
+
+ /**
+ * Vertical regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> verticalStretchRegions;
+
+ /**
+ * The colors within each region, fixed or stretchable.
+ * For w*h regions, the color of region (x,y) is addressable
+ * via index y*w + x.
+ */
+ std::vector<uint32_t> regionColors;
+
+ /**
+ * Returns serialized data containing the original basic 9-patch meta data.
+ * Optical layout bounds and round rect outline data must be serialized
+ * separately using serializeOpticalLayoutBounds() and serializeRoundedRectOutline().
+ */
+ std::unique_ptr<uint8_t[]> serializeBase(size_t* outLen) const;
+
+ /**
+ * Serializes the layout bounds.
+ */
+ std::unique_ptr<uint8_t[]> serializeLayoutBounds(size_t* outLen) const;
+
+ /**
+ * Serializes the rounded-rect outline.
+ */
+ std::unique_ptr<uint8_t[]> serializeRoundedRectOutline(size_t* outLen) const;
+
+private:
+ explicit NinePatch() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(NinePatch);
+};
+
+::std::ostream& operator<<(::std::ostream& out, const Range& range);
+::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds);
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch);
+
+} // namespace aapt
+
+#endif /* AAPT_COMPILE_IMAGE_H */
diff --git a/tools/aapt2/compile/NinePatch.cpp b/tools/aapt2/compile/NinePatch.cpp
new file mode 100644
index 0000000..408ecf7
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -0,0 +1,671 @@
+/*
+ * 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.
+ */
+
+#include "compile/Image.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+// Colors in the format 0xAARRGGBB (the way 9-patch expects it).
+constexpr static const uint32_t kColorOpaqueWhite = 0xffffffffu;
+constexpr static const uint32_t kColorOpaqueBlack = 0xff000000u;
+constexpr static const uint32_t kColorOpaqueRed = 0xffff0000u;
+
+constexpr static const uint32_t kPrimaryColor = kColorOpaqueBlack;
+constexpr static const uint32_t kSecondaryColor = kColorOpaqueRed;
+
+/**
+ * Returns the alpha value encoded in the 0xAARRGBB encoded pixel.
+ */
+static uint32_t getAlpha(uint32_t color);
+
+/**
+ * Determines whether a color on an ImageLine is valid.
+ * A 9patch image may use a transparent color as neutral,
+ * or a fully opaque white color as neutral, based on the
+ * pixel color at (0,0) of the image. One or the other is fine,
+ * but we need to ensure consistency throughout the image.
+ */
+class ColorValidator {
+public:
+ virtual ~ColorValidator() = default;
+
+ /**
+ * Returns true if the color specified is a neutral color
+ * (no padding, stretching, or optical bounds).
+ */
+ virtual bool isNeutralColor(uint32_t color) const = 0;
+
+ /**
+ * Returns true if the color is either a neutral color
+ * or one denoting padding, stretching, or optical bounds.
+ */
+ bool isValidColor(uint32_t color) const {
+ switch (color) {
+ case kPrimaryColor:
+ case kSecondaryColor:
+ return true;
+ }
+ return isNeutralColor(color);
+ }
+};
+
+// Walks an ImageLine and records Ranges of primary and secondary colors.
+// The primary color is black and is used to denote a padding or stretching range,
+// depending on which border we're iterating over.
+// The secondary color is red and is used to denote optical bounds.
+//
+// An ImageLine is a templated-interface that would look something like this if it
+// were polymorphic:
+//
+// class ImageLine {
+// public:
+// virtual int32_t getLength() const = 0;
+// virtual uint32_t getColor(int32_t idx) const = 0;
+// };
+//
+template <typename ImageLine>
+static bool fillRanges(const ImageLine* imageLine,
+ const ColorValidator* colorValidator,
+ std::vector<Range>* primaryRanges,
+ std::vector<Range>* secondaryRanges,
+ std::string* err) {
+ const int32_t length = imageLine->getLength();
+
+ uint32_t lastColor = 0xffffffffu;
+ for (int32_t idx = 1; idx < length - 1; idx++) {
+ const uint32_t color = imageLine->getColor(idx);
+ if (!colorValidator->isValidColor(color)) {
+ *err = "found an invalid color";
+ return false;
+ }
+
+ if (color != lastColor) {
+ // We are ending a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (lastColor == kPrimaryColor) {
+ primaryRanges->back().end = idx - 1;
+ } else if (lastColor == kSecondaryColor) {
+ secondaryRanges->back().end = idx - 1;
+ }
+
+ // We are starting a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (color == kPrimaryColor) {
+ primaryRanges->push_back(Range(idx - 1, length - 2));
+ } else if (color == kSecondaryColor) {
+ secondaryRanges->push_back(Range(idx - 1, length - 2));
+ }
+ lastColor = color;
+ }
+ }
+ return true;
+}
+
+/**
+ * Iterates over a row in an image. Implements the templated ImageLine interface.
+ */
+class HorizontalImageLine {
+public:
+ explicit HorizontalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t length) :
+ mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
+ }
+
+ inline int32_t getLength() const {
+ return mLength;
+ }
+
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(mRows[mYOffset] + (idx + mXOffset) * 4);
+ }
+
+private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
+
+ DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
+};
+
+/**
+ * Iterates over a column in an image. Implements the templated ImageLine interface.
+ */
+class VerticalImageLine {
+public:
+ explicit VerticalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t length) :
+ mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
+ }
+
+ inline int32_t getLength() const {
+ return mLength;
+ }
+
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(mRows[mYOffset + idx] + (mXOffset * 4));
+ }
+
+private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
+
+ DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
+};
+
+class DiagonalImageLine {
+public:
+ explicit DiagonalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t xStep, int32_t yStep, int32_t length) :
+ mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mXStep(xStep), mYStep(yStep),
+ mLength(length) {
+ }
+
+ inline int32_t getLength() const {
+ return mLength;
+ }
+
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(
+ mRows[mYOffset + (idx * mYStep)] + ((idx + mXOffset) * mXStep) * 4);
+ }
+
+private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
+
+ DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
+};
+
+class TransparentNeutralColorValidator : public ColorValidator {
+public:
+ bool isNeutralColor(uint32_t color) const override {
+ return getAlpha(color) == 0;
+ }
+};
+
+class WhiteNeutralColorValidator : public ColorValidator {
+public:
+ bool isNeutralColor(uint32_t color) const override {
+ return color == kColorOpaqueWhite;
+ }
+};
+
+inline static uint32_t getAlpha(uint32_t color) {
+ return (color & 0xff000000u) >> 24;
+}
+
+static bool populateBounds(const std::vector<Range>& padding,
+ const std::vector<Range>& layoutBounds,
+ const std::vector<Range>& stretchRegions,
+ const int32_t length,
+ int32_t* paddingStart, int32_t* paddingEnd,
+ int32_t* layoutStart, int32_t* layoutEnd,
+ const StringPiece& edgeName,
+ std::string* err) {
+ if (padding.size() > 1) {
+ std::stringstream errStream;
+ errStream << "too many padding sections on " << edgeName << " border";
+ *err = errStream.str();
+ return false;
+ }
+
+ *paddingStart = 0;
+ *paddingEnd = 0;
+ if (!padding.empty()) {
+ const Range& range = padding.front();
+ *paddingStart = range.start;
+ *paddingEnd = length - range.end;
+ } else if (!stretchRegions.empty()) {
+ // No padding was defined. Compute the padding from the first and last
+ // stretch regions.
+ *paddingStart = stretchRegions.front().start;
+ *paddingEnd = length - stretchRegions.back().end;
+ }
+
+ if (layoutBounds.size() > 2) {
+ std::stringstream errStream;
+ errStream << "too many layout bounds sections on " << edgeName << " border";
+ *err = errStream.str();
+ return false;
+ }
+
+ *layoutStart = 0;
+ *layoutEnd = 0;
+ if (layoutBounds.size() >= 1) {
+ const Range& range = layoutBounds.front();
+ // If there is only one layout bound segment, it might not start at 0, but then it should
+ // end at length.
+ if (range.start != 0 && range.end != length) {
+ std::stringstream errStream;
+ errStream << "layout bounds on " << edgeName << " border must start at edge";
+ *err = errStream.str();
+ return false;
+ }
+ *layoutStart = range.end;
+
+ if (layoutBounds.size() >= 2) {
+ const Range& range = layoutBounds.back();
+ if (range.end != length) {
+ std::stringstream errStream;
+ errStream << "layout bounds on " << edgeName << " border must start at edge";
+ *err = errStream.str();
+ return false;
+ }
+ *layoutEnd = length - range.start;
+ }
+ }
+ return true;
+}
+
+static int32_t calculateSegmentCount(const std::vector<Range>& stretchRegions, int32_t length) {
+ if (stretchRegions.size() == 0) {
+ return 0;
+ }
+
+ const bool startIsFixed = stretchRegions.front().start != 0;
+ const bool endIsFixed = stretchRegions.back().end != length;
+ int32_t modifier = 0;
+ if (startIsFixed && endIsFixed) {
+ modifier = 1;
+ } else if (!startIsFixed && !endIsFixed) {
+ modifier = -1;
+ }
+ return static_cast<int32_t>(stretchRegions.size()) * 2 + modifier;
+}
+
+static uint32_t getRegionColor(uint8_t** rows, const Bounds& region) {
+ // Sample the first pixel to compare against.
+ const uint32_t expectedColor = NinePatch::packRGBA(rows[region.top] + region.left * 4);
+ for (int32_t y = region.top; y < region.bottom; y++) {
+ const uint8_t* row = rows[y];
+ for (int32_t x = region.left; x < region.right; x++) {
+ const uint32_t color = NinePatch::packRGBA(row + x * 4);
+ if (getAlpha(color) == 0) {
+ // The color is transparent.
+ // If the expectedColor is not transparent, NO_COLOR.
+ if (getAlpha(expectedColor) != 0) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
+ } else if (color != expectedColor) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
+ }
+ }
+
+ if (getAlpha(expectedColor) == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return expectedColor;
+}
+
+// Fills outColors with each 9-patch section's colour. If the whole section is transparent,
+// it gets the special TRANSPARENT colour. If the whole section is the same colour, it is assigned
+// that colour. Otherwise it gets the special NO_COLOR colour.
+//
+// Note that the rows contain the 9-patch 1px border, and the indices in the stretch regions are
+// already offset to exclude the border. This means that each time the rows are accessed,
+// the indices must be offset by 1.
+//
+// width and height also include the 9-patch 1px border.
+static void calculateRegionColors(uint8_t** rows,
+ const std::vector<Range>& horizontalStretchRegions,
+ const std::vector<Range>& verticalStretchRegions,
+ const int32_t width, const int32_t height,
+ std::vector<uint32_t>* outColors) {
+ int32_t nextTop = 0;
+ Bounds bounds;
+ auto rowIter = verticalStretchRegions.begin();
+ while (nextTop != height) {
+ if (rowIter != verticalStretchRegions.end()) {
+ if (nextTop != rowIter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = nextTop + 1;
+ bounds.bottom = rowIter->start + 1;
+ nextTop = rowIter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = rowIter->start + 1;
+ bounds.bottom = rowIter->end + 1;
+ nextTop = rowIter->end;
+ ++rowIter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = nextTop + 1;
+ bounds.bottom = height + 1;
+ nextTop = height;
+ }
+
+ int32_t nextLeft = 0;
+ auto colIter = horizontalStretchRegions.begin();
+ while (nextLeft != width) {
+ if (colIter != horizontalStretchRegions.end()) {
+ if (nextLeft != colIter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = nextLeft + 1;
+ bounds.right = colIter->start + 1;
+ nextLeft = colIter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = colIter->start + 1;
+ bounds.right = colIter->end + 1;
+ nextLeft = colIter->end;
+ ++colIter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = nextLeft + 1;
+ bounds.right = width + 1;
+ nextLeft = width;
+ }
+ outColors->push_back(getRegionColor(rows, bounds));
+ }
+ }
+}
+
+// Calculates the insets of a row/column of pixels based on where the largest alpha value begins
+// (on both sides).
+template <typename ImageLine>
+static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart, int32_t* outEnd) {
+ *outStart = 0;
+ *outEnd = 0;
+
+ const int32_t length = imageLine->getLength();
+ if (length < 3) {
+ return;
+ }
+
+ // If the length is odd, we want both sides to process the center pixel,
+ // so we use two different midpoints (to account for < and <= in the different loops).
+ const int32_t mid2 = length / 2;
+ const int32_t mid1 = mid2 + (length % 2);
+
+ uint32_t maxAlpha = 0;
+ for (int32_t i = 0; i < mid1 && maxAlpha != 0xff; i++) {
+ uint32_t alpha = getAlpha(imageLine->getColor(i));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ *outStart = i;
+ }
+ }
+
+ maxAlpha = 0;
+ for (int32_t i = length - 1; i >= mid2 && maxAlpha != 0xff; i--) {
+ uint32_t alpha = getAlpha(imageLine->getColor(i));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ *outEnd = length - (i + 1);
+ }
+ }
+ return;
+}
+
+template <typename ImageLine>
+static uint32_t findMaxAlpha(const ImageLine* imageLine) {
+ const int32_t length = imageLine->getLength();
+ uint32_t maxAlpha = 0;
+ for (int32_t idx = 0; idx < length && maxAlpha != 0xff; idx++) {
+ uint32_t alpha = getAlpha(imageLine->getColor(idx));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ }
+ }
+ return maxAlpha;
+}
+
+// Pack the pixels in as 0xAARRGGBB (as 9-patch expects it).
+uint32_t NinePatch::packRGBA(const uint8_t* pixel) {
+ return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
+}
+
+std::unique_ptr<NinePatch> NinePatch::create(uint8_t** rows,
+ const int32_t width, const int32_t height,
+ std::string* err) {
+ if (width < 3 || height < 3) {
+ *err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
+ return {};
+ }
+
+ std::vector<Range> horizontalPadding;
+ std::vector<Range> horizontalOpticalBounds;
+ std::vector<Range> verticalPadding;
+ std::vector<Range> verticalOpticalBounds;
+ std::vector<Range> unexpectedRanges;
+ std::unique_ptr<ColorValidator> colorValidator;
+
+ if (rows[0][3] == 0) {
+ colorValidator = util::make_unique<TransparentNeutralColorValidator>();
+ } else if (packRGBA(rows[0]) == kColorOpaqueWhite) {
+ colorValidator = util::make_unique<WhiteNeutralColorValidator>();
+ } else {
+ *err = "top-left corner pixel must be either opaque white or transparent";
+ return {};
+ }
+
+ // Private constructor, can't use make_unique.
+ auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
+
+ HorizontalImageLine topRow(rows, 0, 0, width);
+ if (!fillRanges(&topRow, colorValidator.get(), &ninePatch->horizontalStretchRegions,
+ &unexpectedRanges, err)) {
+ return {};
+ }
+
+ if (!unexpectedRanges.empty()) {
+ const Range& range = unexpectedRanges[0];
+ std::stringstream errStream;
+ errStream << "found unexpected optical bounds (red pixel) on top border "
+ << "at x=" << range.start + 1;
+ *err = errStream.str();
+ return {};
+ }
+
+ VerticalImageLine leftCol(rows, 0, 0, height);
+ if (!fillRanges(&leftCol, colorValidator.get(), &ninePatch->verticalStretchRegions,
+ &unexpectedRanges, err)) {
+ return {};
+ }
+
+ if (!unexpectedRanges.empty()) {
+ const Range& range = unexpectedRanges[0];
+ std::stringstream errStream;
+ errStream << "found unexpected optical bounds (red pixel) on left border "
+ << "at y=" << range.start + 1;
+ return {};
+ }
+
+ HorizontalImageLine bottomRow(rows, 0, height - 1, width);
+ if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
+ &horizontalOpticalBounds, err)) {
+ return {};
+ }
+
+ if (!populateBounds(horizontalPadding, horizontalOpticalBounds,
+ ninePatch->horizontalStretchRegions, width - 2,
+ &ninePatch->padding.left, &ninePatch->padding.right,
+ &ninePatch->layoutBounds.left, &ninePatch->layoutBounds.right,
+ "bottom", err)) {
+ return {};
+ }
+
+ VerticalImageLine rightCol(rows, width - 1, 0, height);
+ if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
+ &verticalOpticalBounds, err)) {
+ return {};
+ }
+
+ if (!populateBounds(verticalPadding, verticalOpticalBounds,
+ ninePatch->verticalStretchRegions, height - 2,
+ &ninePatch->padding.top, &ninePatch->padding.bottom,
+ &ninePatch->layoutBounds.top, &ninePatch->layoutBounds.bottom,
+ "right", err)) {
+ return {};
+ }
+
+ // Fill the region colors of the 9-patch.
+ const int32_t numRows = calculateSegmentCount(ninePatch->horizontalStretchRegions, width - 2);
+ const int32_t numCols = calculateSegmentCount(ninePatch->verticalStretchRegions, height - 2);
+ if ((int64_t) numRows * (int64_t) numCols > 0x7f) {
+ *err = "too many regions in 9-patch";
+ return {};
+ }
+
+ ninePatch->regionColors.reserve(numRows * numCols);
+ calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
+ ninePatch->verticalStretchRegions,
+ width - 2, height - 2,
+ &ninePatch->regionColors);
+
+ // Compute the outline based on opacity.
+
+ // Find left and right extent of 9-patch content on center row.
+ HorizontalImageLine midRow(rows, 1, height / 2, width - 2);
+ findOutlineInsets(&midRow, &ninePatch->outline.left, &ninePatch->outline.right);
+
+ // Find top and bottom extent of 9-patch content on center column.
+ VerticalImageLine midCol(rows, width / 2, 1, height - 2);
+ findOutlineInsets(&midCol, &ninePatch->outline.top, &ninePatch->outline.bottom);
+
+ const int32_t outlineWidth = (width - 2) - ninePatch->outline.left - ninePatch->outline.right;
+ const int32_t outlineHeight = (height - 2) - ninePatch->outline.top - ninePatch->outline.bottom;
+
+ // Find the largest alpha value within the outline area.
+ HorizontalImageLine outlineMidRow(rows,
+ 1 + ninePatch->outline.left,
+ 1 + ninePatch->outline.top + (outlineHeight / 2),
+ outlineWidth);
+ VerticalImageLine outlineMidCol(rows,
+ 1 + ninePatch->outline.left + (outlineWidth / 2),
+ 1 + ninePatch->outline.top,
+ outlineHeight);
+ ninePatch->outlineAlpha = std::max(findMaxAlpha(&outlineMidRow), findMaxAlpha(&outlineMidCol));
+
+ // Assuming the image is a round rect, compute the radius by marching
+ // diagonally from the top left corner towards the center.
+ DiagonalImageLine diagonal(rows, 1 + ninePatch->outline.left, 1 + ninePatch->outline.top,
+ 1, 1, std::min(outlineWidth, outlineHeight));
+ int32_t topLeft, bottomRight;
+ findOutlineInsets(&diagonal, &topLeft, &bottomRight);
+
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ ninePatch->outlineRadius = 3.4142f * topLeft;
+ return ninePatch;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
+ android::Res_png_9patch data;
+ data.numXDivs = static_cast<uint8_t>(horizontalStretchRegions.size()) * 2;
+ data.numYDivs = static_cast<uint8_t>(verticalStretchRegions.size()) * 2;
+ data.numColors = static_cast<uint8_t>(regionColors.size());
+ data.paddingLeft = padding.left;
+ data.paddingRight = padding.right;
+ data.paddingTop = padding.top;
+ data.paddingBottom = padding.bottom;
+
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
+ android::Res_png_9patch::serialize(data,
+ (const int32_t*) horizontalStretchRegions.data(),
+ (const int32_t*) verticalStretchRegions.data(),
+ regionColors.data(),
+ buffer.get());
+ *outLen = data.serializedSize();
+ return buffer;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeLayoutBounds(size_t* outLen) const {
+ size_t chunkLen = sizeof(uint32_t) * 4;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+ uint8_t* cursor = buffer.get();
+
+ memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
+ cursor += sizeof(layoutBounds.left);
+
+ memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
+ cursor += sizeof(layoutBounds.top);
+
+ memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
+ cursor += sizeof(layoutBounds.right);
+
+ memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
+ cursor += sizeof(layoutBounds.bottom);
+
+ *outLen = chunkLen;
+ return buffer;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(size_t* outLen) const {
+ size_t chunkLen = sizeof(uint32_t) * 6;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+ uint8_t* cursor = buffer.get();
+
+ memcpy(cursor, &outline.left, sizeof(outline.left));
+ cursor += sizeof(outline.left);
+
+ memcpy(cursor, &outline.top, sizeof(outline.top));
+ cursor += sizeof(outline.top);
+
+ memcpy(cursor, &outline.right, sizeof(outline.right));
+ cursor += sizeof(outline.right);
+
+ memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
+ cursor += sizeof(outline.bottom);
+
+ *((float*) cursor) = outlineRadius;
+ cursor += sizeof(outlineRadius);
+
+ *((uint32_t*) cursor) = outlineAlpha;
+
+ *outLen = chunkLen;
+ return buffer;
+}
+
+::std::ostream& operator<<(::std::ostream& out, const Range& range) {
+ return out << "[" << range.start << ", " << range.end << ")";
+}
+
+::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds) {
+ return out << "l=" << bounds.left
+ << " t=" << bounds.top
+ << " r=" << bounds.right
+ << " b=" << bounds.bottom;
+}
+
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch) {
+ return out << "padding: " << ninePatch.padding
+ << ", bounds: " << ninePatch.layoutBounds
+ << ", outline: " << ninePatch.outline
+ << " rad=" << ninePatch.outlineRadius
+ << " alpha=" << ninePatch.outlineAlpha;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch_test.cpp b/tools/aapt2/compile/NinePatch_test.cpp
new file mode 100644
index 0000000..ac4ee02
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch_test.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+#include "compile/Image.h"
+#include "test/Test.h"
+
+namespace aapt {
+
+// Pixels are in RGBA_8888 packing.
+
+#define RED "\xff\x00\x00\xff"
+#define BLUE "\x00\x00\xff\xff"
+#define GREEN "\xff\x00\x00\xff"
+#define GR_70 "\xff\x00\x00\xb3"
+#define GR_50 "\xff\x00\x00\x80"
+#define GR_20 "\xff\x00\x00\x33"
+#define BLACK "\x00\x00\x00\xff"
+#define WHITE "\xff\xff\xff\xff"
+#define TRANS "\x00\x00\x00\x00"
+
+static uint8_t* k2x2[] = {
+ (uint8_t*) WHITE WHITE,
+ (uint8_t*) WHITE WHITE,
+};
+
+static uint8_t* kMixedNeutralColor3x3[] = {
+ (uint8_t*) WHITE BLACK TRANS,
+ (uint8_t*) TRANS RED TRANS,
+ (uint8_t*) WHITE WHITE WHITE,
+};
+
+static uint8_t* kTransparentNeutralColor3x3[] = {
+ (uint8_t*) TRANS BLACK TRANS,
+ (uint8_t*) BLACK RED BLACK,
+ (uint8_t*) TRANS BLACK TRANS,
+};
+
+static uint8_t* kSingleStretch7x6[] = {
+ (uint8_t*) WHITE WHITE BLACK BLACK BLACK WHITE WHITE,
+ (uint8_t*) WHITE RED RED RED RED RED WHITE,
+ (uint8_t*) BLACK RED RED RED RED RED WHITE,
+ (uint8_t*) BLACK RED RED RED RED RED WHITE,
+ (uint8_t*) WHITE RED RED RED RED RED WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kMultipleStretch10x7[] = {
+ (uint8_t*) WHITE WHITE BLACK WHITE BLACK BLACK WHITE BLACK WHITE WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) WHITE RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kPadding6x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE BLACK BLACK WHITE WHITE,
+};
+
+static uint8_t* kLayoutBoundsWrongEdge3x3[] = {
+ (uint8_t*) WHITE RED WHITE,
+ (uint8_t*) RED WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE,
+};
+
+static uint8_t* kLayoutBoundsNotEdgeAligned5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE RED WHITE WHITE,
+};
+
+static uint8_t* kLayoutBounds5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE RED WHITE RED WHITE,
+};
+
+static uint8_t* kAsymmetricLayoutBounds5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE RED WHITE WHITE WHITE,
+};
+
+static uint8_t* kPaddingAndLayoutBounds5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE RED BLACK RED WHITE,
+};
+
+static uint8_t* kColorfulImage5x5[] = {
+ (uint8_t*) WHITE BLACK WHITE BLACK WHITE,
+ (uint8_t*) BLACK RED BLUE GREEN WHITE,
+ (uint8_t*) BLACK RED GREEN GREEN WHITE,
+ (uint8_t*) WHITE TRANS BLUE GREEN WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineOpaque10x10[] = {
+ (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineTranslucent10x10[] = {
+ (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineOffsetTranslucent12x10[] = {
+ (uint8_t*) WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineRadius5x5[] = {
+ (uint8_t*) WHITE BLACK BLACK BLACK WHITE,
+ (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*) BLACK GREEN GREEN GREEN WHITE,
+ (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+};
+
+TEST(NinePatchTest, Minimum3x3) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(k2x2, 2, 2, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, MixedNeutralColors) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kMixedNeutralColor3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, TransparentNeutralColor) {
+ std::string err;
+ EXPECT_NE(nullptr, NinePatch::create(kTransparentNeutralColor3x3, 3, 3, &err));
+}
+
+TEST(NinePatchTest, SingleStretchRegion) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kSingleStretch7x6, 7, 6, &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
+
+ EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
+ EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
+}
+
+TEST(NinePatchTest, MultipleStretchRegions) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
+
+ EXPECT_EQ(Range(1, 2), ninePatch->horizontalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
+ EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
+
+ EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
+}
+
+TEST(NinePatchTest, InferPaddingFromStretchRegions) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->padding);
+}
+
+TEST(NinePatchTest, Padding) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPadding6x5, 6, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+}
+
+TEST(NinePatchTest, LayoutBoundsAreOnWrongEdge) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, LayoutBoundsMustTouchEdges) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, LayoutBounds) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+
+ ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
+}
+
+TEST(NinePatchTest, PaddingAndLayoutBounds) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPaddingAndLayoutBounds5x5, 5, 5,
+ &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+}
+
+TEST(NinePatchTest, RegionColorsAreCorrect) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kColorfulImage5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ std::vector<uint32_t> expectedColors = {
+ NinePatch::packRGBA((uint8_t*) RED),
+ (uint32_t) android::Res_png_9patch::NO_COLOR,
+ NinePatch::packRGBA((uint8_t*) GREEN),
+ (uint32_t) android::Res_png_9patch::TRANSPARENT_COLOR,
+ NinePatch::packRGBA((uint8_t*) BLUE),
+ NinePatch::packRGBA((uint8_t*) GREEN),
+ };
+ EXPECT_EQ(expectedColors, ninePatch->regionColors);
+}
+
+TEST(NinePatchTest, OutlineFromOpaqueImage) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOpaque10x10, 10, 10, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(2, 2, 2, 2), ninePatch->outline);
+ EXPECT_EQ(0x000000ffu, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineFromTranslucentImage) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineTranslucent10x10, 10, 10,
+ &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(3, 3, 3, 3), ninePatch->outline);
+ EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineFromOffCenterImage) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10,
+ &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ // TODO(adamlesinski): The old AAPT algorithm searches from the outside to the middle
+ // for each inset. If the outline is shifted, the search may not find a closer bounds.
+ // This check should be:
+ // EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
+ // but until I know what behaviour I'm breaking, I will leave it at the incorrect:
+ EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
+
+ EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineRadius) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineRadius5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(0, 0, 0, 0), ninePatch->outline);
+ EXPECT_EQ(3.4142f, ninePatch->outlineRadius);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index f835b06e..4a15d95 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -17,10 +17,14 @@
#ifndef AAPT_PNG_H
#define AAPT_PNG_H
-#include "util/BigBuffer.h"
#include "Diagnostics.h"
#include "Source.h"
+#include "compile/Image.h"
+#include "io/Io.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/BigBuffer.h"
+#include <android-base/macros.h>
#include <iostream>
#include <string>
@@ -40,8 +44,51 @@
private:
IDiagnostics* mDiag;
+
+ DISALLOW_COPY_AND_ASSIGN(Png);
};
+/**
+ * An InputStream that filters out unimportant PNG chunks.
+ */
+class PngChunkFilter : public io::InputStream {
+public:
+ explicit PngChunkFilter(const StringPiece& data);
+
+ bool Next(const void** buffer, int* len) override;
+ void BackUp(int count) override;
+ bool Skip(int count) override;
+
+ int64_t ByteCount() const override {
+ return static_cast<int64_t>(mWindowStart);
+ }
+
+ bool HadError() const override {
+ return mError;
+ }
+
+private:
+ bool consumeWindow(const void** buffer, int* len);
+
+ StringPiece mData;
+ size_t mWindowStart = 0;
+ size_t mWindowEnd = 0;
+ bool mError = false;
+
+ DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
+};
+
+/**
+ * Reads a PNG from the InputStream into memory as an RGBA Image.
+ */
+std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in);
+
+/**
+ * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream as a PNG.
+ */
+bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
+ io::OutputStream* out, const PngOptions& options);
+
} // namespace aapt
#endif // AAPT_PNG_H
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
new file mode 100644
index 0000000..70a881f
--- /dev/null
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#include "compile/Png.h"
+#include "io/Io.h"
+#include "util/StringPiece.h"
+
+namespace aapt {
+
+static constexpr const char* kPngSignature = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a";
+
+// Useful helper function that encodes individual bytes into a uint32
+// at compile time.
+constexpr uint32_t u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
+ return (((uint32_t) a) << 24)
+ | (((uint32_t) b) << 16)
+ | (((uint32_t) c) << 8)
+ | ((uint32_t) d);
+}
+
+// Whitelist of PNG chunk types that we want to keep in the resulting PNG.
+enum PngChunkTypes {
+ kPngChunkIHDR = u32(73, 72, 68, 82),
+ kPngChunkIDAT = u32(73, 68, 65, 84),
+ kPngChunkIEND = u32(73, 69, 78, 68),
+ kPngChunkPLTE = u32(80, 76, 84, 69),
+ kPngChunktRNS = u32(116, 82, 78, 83),
+ kPngChunksRGB = u32(115, 82, 71, 66),
+};
+
+static uint32_t peek32LE(const char* data) {
+ uint32_t word = ((uint32_t) data[0]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t) data[1]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t) data[2]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t) data[3]) & 0x000000ff;
+ return word;
+}
+
+static bool isPngChunkWhitelisted(uint32_t type) {
+ switch (type) {
+ case kPngChunkIHDR:
+ case kPngChunkIDAT:
+ case kPngChunkIEND:
+ case kPngChunkPLTE:
+ case kPngChunktRNS:
+ case kPngChunksRGB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+PngChunkFilter::PngChunkFilter(const StringPiece& data) : mData(data) {
+ if (util::stringStartsWith(mData, kPngSignature)) {
+ mWindowStart = 0;
+ mWindowEnd = strlen(kPngSignature);
+ } else {
+ mError = true;
+ }
+}
+
+bool PngChunkFilter::consumeWindow(const void** buffer, int* len) {
+ if (mWindowStart != mWindowEnd) {
+ // We have bytes to give from our window.
+ const int bytesRead = (int) (mWindowEnd - mWindowStart);
+ *buffer = mData.data() + mWindowStart;
+ *len = bytesRead;
+ mWindowStart = mWindowEnd;
+ return true;
+ }
+ return false;
+}
+
+bool PngChunkFilter::Next(const void** buffer, int* len) {
+ if (mError) {
+ return false;
+ }
+
+ // In case BackUp was called, we must consume the window.
+ if (consumeWindow(buffer, len)) {
+ return true;
+ }
+
+ // Advance the window as far as possible (until we meet a chunk that
+ // we want to strip).
+ while (mWindowEnd < mData.size()) {
+ // Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
+ const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
+
+ // Is there enough room for a chunk header?
+ if (mData.size() - mWindowStart < kMinChunkHeaderSize) {
+ mError = true;
+ return false;
+ }
+
+ // Verify the chunk length.
+ const uint32_t chunkLen = peek32LE(mData.data() + mWindowEnd);
+ if (((uint64_t) chunkLen) + ((uint64_t) mWindowEnd) + sizeof(uint32_t) > mData.size()) {
+ // Overflow.
+ mError = true;
+ return false;
+ }
+
+ // Do we strip this chunk?
+ const uint32_t chunkType = peek32LE(mData.data() + mWindowEnd + sizeof(uint32_t));
+ if (isPngChunkWhitelisted(chunkType)) {
+ // Advance the window to include this chunk.
+ mWindowEnd += kMinChunkHeaderSize + chunkLen;
+ } else {
+ // We want to strip this chunk. If we accumulated a window,
+ // we must return the window now.
+ if (mWindowStart != mWindowEnd) {
+ break;
+ }
+
+ // The window is empty, so we can advance past this chunk
+ // and keep looking for the next good chunk,
+ mWindowEnd += kMinChunkHeaderSize + chunkLen;
+ mWindowStart = mWindowEnd;
+ }
+ }
+
+ if (consumeWindow(buffer, len)) {
+ return true;
+ }
+ return false;
+}
+
+void PngChunkFilter::BackUp(int count) {
+ if (mError) {
+ return;
+ }
+ mWindowStart -= count;
+}
+
+bool PngChunkFilter::Skip(int count) {
+ if (mError) {
+ return false;
+ }
+
+ const void* buffer;
+ int len;
+ while (count > 0) {
+ if (!Next(&buffer, &len)) {
+ return false;
+ }
+ if (len > count) {
+ BackUp(len - count);
+ count = 0;
+ } else {
+ count -= len;
+ }
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/PngCrunch.cpp b/tools/aapt2/compile/PngCrunch.cpp
new file mode 100644
index 0000000..a2e3f4f
--- /dev/null
+++ b/tools/aapt2/compile/PngCrunch.cpp
@@ -0,0 +1,724 @@
+/*
+ * 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.
+ */
+
+#include "compile/Png.h"
+
+#include <algorithm>
+#include <android-base/errors.h>
+#include <android-base/macros.h>
+#include <png.h>
+#include <unordered_map>
+#include <unordered_set>
+#include <zlib.h>
+
+namespace aapt {
+
+// Size in bytes of the PNG signature.
+constexpr size_t kPngSignatureSize = 8u;
+
+/**
+ * Custom deleter that destroys libpng read and info structs.
+ */
+class PngReadStructDeleter {
+public:
+ explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr) :
+ mReadPtr(readPtr), mInfoPtr(infoPtr) {
+ }
+
+ ~PngReadStructDeleter() {
+ png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
+ }
+
+private:
+ png_structp mReadPtr;
+ png_infop mInfoPtr;
+
+ DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
+};
+
+/**
+ * Custom deleter that destroys libpng write and info structs.
+ */
+class PngWriteStructDeleter {
+public:
+ explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr) :
+ mWritePtr(writePtr), mInfoPtr(infoPtr) {
+ }
+
+ ~PngWriteStructDeleter() {
+ png_destroy_write_struct(&mWritePtr, &mInfoPtr);
+ }
+
+private:
+ png_structp mWritePtr;
+ png_infop mInfoPtr;
+
+ DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
+};
+
+// Custom warning logging method that uses IDiagnostics.
+static void logWarning(png_structp pngPtr, png_const_charp warningMsg) {
+ IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
+ diag->warn(DiagMessage() << warningMsg);
+}
+
+// Custom error logging method that uses IDiagnostics.
+static void logError(png_structp pngPtr, png_const_charp errorMsg) {
+ IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
+ diag->error(DiagMessage() << errorMsg);
+}
+
+static void readDataFromStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
+ io::InputStream* in = (io::InputStream*) png_get_io_ptr(pngPtr);
+
+ const void* inBuffer;
+ int inLen;
+ if (!in->Next(&inBuffer, &inLen)) {
+ if (in->HadError()) {
+ std::string err = in->GetError();
+ png_error(pngPtr, err.c_str());
+ }
+ return;
+ }
+
+ const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
+ memcpy(buffer, inBuffer, bytesRead);
+ if (bytesRead != static_cast<size_t>(inLen)) {
+ in->BackUp(inLen - static_cast<int>(bytesRead));
+ }
+}
+
+static void writeDataToStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
+ io::OutputStream* out = (io::OutputStream*) png_get_io_ptr(pngPtr);
+
+ void* outBuffer;
+ int outLen;
+ while (len > 0) {
+ if (!out->Next(&outBuffer, &outLen)) {
+ if (out->HadError()) {
+ std::string err = out->GetError();
+ png_error(pngPtr, err.c_str());
+ }
+ return;
+ }
+
+ const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
+ memcpy(outBuffer, buffer, bytesWritten);
+
+ // Advance the input buffer.
+ buffer += bytesWritten;
+ len -= bytesWritten;
+
+ // Advance the output buffer.
+ outLen -= static_cast<int>(bytesWritten);
+ }
+
+ // If the entire output buffer wasn't used, backup.
+ if (outLen > 0) {
+ out->BackUp(outLen);
+ }
+}
+
+std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in) {
+ // Read the first 8 bytes of the file looking for the PNG signature.
+ // Bail early if it does not match.
+ const png_byte* signature;
+ int bufferSize;
+ if (!in->Next((const void**) &signature, &bufferSize)) {
+ context->getDiagnostics()->error(DiagMessage()
+ << android::base::SystemErrorCodeToString(errno));
+ return {};
+ }
+
+ if (static_cast<size_t>(bufferSize) < kPngSignatureSize
+ || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "file signature does not match PNG signature");
+ return {};
+ }
+
+ // Start at the beginning of the first chunk.
+ in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
+
+ // Create and initialize the png_struct with the default error and warning handlers.
+ // The header version is also passed in to ensure that this was built against the same
+ // version of libpng.
+ png_structp readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (readPtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "failed to create libpng read png_struct");
+ return {};
+ }
+
+ // Create and initialize the memory for image header and data.
+ png_infop infoPtr = png_create_info_struct(readPtr);
+ if (infoPtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage() << "failed to create libpng read png_info");
+ png_destroy_read_struct(&readPtr, nullptr, nullptr);
+ return {};
+ }
+
+ // Automatically release PNG resources at end of scope.
+ PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
+
+ // libpng uses longjmp to jump to an error handling routine.
+ // setjmp will only return true if it was jumped to, aka there was
+ // an error.
+ if (setjmp(png_jmpbuf(readPtr))) {
+ return {};
+ }
+
+ // Handle warnings ourselves via IDiagnostics.
+ png_set_error_fn(readPtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+
+ // Set up the read functions which read from our custom data sources.
+ png_set_read_fn(readPtr, (png_voidp) in, readDataFromStream);
+
+ // Skip the signature that we already read.
+ png_set_sig_bytes(readPtr, kPngSignatureSize);
+
+ // Read the chunk headers.
+ png_read_info(readPtr, infoPtr);
+
+ // Extract image meta-data from the various chunk headers.
+ uint32_t width, height;
+ int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
+ png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceMethod,
+ &compressionMethod, &filterMethod);
+
+ // When the image is read, expand it so that it is in RGBA 8888 format
+ // so that image handling is uniform.
+
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(readPtr);
+ }
+
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+ png_set_expand_gray_1_2_4_to_8(readPtr);
+ }
+
+ if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(readPtr);
+ }
+
+ if (bitDepth == 16) {
+ png_set_strip_16(readPtr);
+ }
+
+ if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
+ png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
+ }
+
+ if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(readPtr);
+ }
+
+ if (interlaceMethod != PNG_INTERLACE_NONE) {
+ png_set_interlace_handling(readPtr);
+ }
+
+ // Once all the options for reading have been set, we need to flush
+ // them to libpng.
+ png_read_update_info(readPtr, infoPtr);
+
+ // 9-patch uses int32_t to index images, so we cap the image dimensions to something
+ // that can always be represented by 9-patch.
+ if (width > std::numeric_limits<int32_t>::max() ||
+ height > std::numeric_limits<int32_t>::max()) {
+ context->getDiagnostics()->error(DiagMessage() << "PNG image dimensions are too large: "
+ << width << "x" << height);
+ return {};
+ }
+
+ std::unique_ptr<Image> outputImage = util::make_unique<Image>();
+ outputImage->width = static_cast<int32_t>(width);
+ outputImage->height = static_cast<int32_t>(height);
+
+ const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+ assert(rowBytes == 4 * width); // RGBA
+
+ // Allocate one large block to hold the image.
+ outputImage->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
+
+ // Create an array of rows that index into the data block.
+ outputImage->rows = std::unique_ptr<uint8_t*[]>(new uint8_t*[height]);
+ for (uint32_t h = 0; h < height; h++) {
+ outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
+ }
+
+ // Actually read the image pixels.
+ png_read_image(readPtr, outputImage->rows.get());
+
+ // Finish reading. This will read any other chunks after the image data.
+ png_read_end(readPtr, infoPtr);
+
+ return outputImage;
+}
+
+/**
+ * Experimentally chosen constant to be added to the overhead of using color type
+ * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
+ * Without this, many small PNGs encoded with palettes are larger after compression than
+ * the same PNGs encoded as RGBA.
+ */
+constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
+
+// Pick a color type by which to encode the image, based on which color type will take
+// the least amount of disk space.
+//
+// 9-patch images traditionally have not been encoded with palettes.
+// The original rationale was to avoid dithering until after scaling,
+// but I don't think this would be an issue with palettes. Either way,
+// our naive size estimation tends to be wrong for small images like 9-patches
+// and using palettes balloons the size of the resulting 9-patch.
+// In order to not regress in size, restrict 9-patch to not use palettes.
+
+// The options are:
+//
+// - RGB
+// - RGBA
+// - RGB + cheap alpha
+// - Color palette
+// - Color palette + cheap alpha
+// - Color palette + alpha palette
+// - Grayscale
+// - Grayscale + cheap alpha
+// - Grayscale + alpha
+//
+static int pickColorType(int32_t width, int32_t height,
+ bool grayScale, bool convertibleToGrayScale, bool hasNinePatch,
+ size_t colorPaletteSize, size_t alphaPaletteSize) {
+ const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
+ const size_t alphaChunkSize = 16 + alphaPaletteSize;
+ const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
+ const size_t colorDataChunkSize = 16 + 3 * width * height;
+ const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
+ const size_t paletteDataChunkSize = 16 + width * height;
+
+ if (grayScale) {
+ if (alphaPaletteSize == 0) {
+ // This is the smallest the data can be.
+ return PNG_COLOR_TYPE_GRAY;
+ } else if (colorPaletteSize <= 256 && !hasNinePatch) {
+ // This grayscale has alpha and can fit within a palette.
+ // See if it is worth fitting into a palette.
+ const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
+ paletteDataChunkSize + kPaletteOverheadConstant;
+ if (grayScaleAlphaDataChunkSize > paletteThreshold) {
+ return PNG_COLOR_TYPE_PALETTE;
+ }
+ }
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+
+
+ if (colorPaletteSize <= 256 && !hasNinePatch) {
+ // This image can fit inside a palette. Let's see if it is worth it.
+ size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
+ size_t totalSizeWithoutPalette = colorDataChunkSize;
+ if (alphaPaletteSize > 0) {
+ totalSizeWithPalette += alphaPaletteSize;
+ totalSizeWithoutPalette = colorAlphaDataChunkSize;
+ }
+
+ if (totalSizeWithoutPalette > totalSizeWithPalette + kPaletteOverheadConstant) {
+ return PNG_COLOR_TYPE_PALETTE;
+ }
+ }
+
+ if (convertibleToGrayScale) {
+ if (alphaPaletteSize == 0) {
+ return PNG_COLOR_TYPE_GRAY;
+ } else {
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+ }
+
+ if (alphaPaletteSize == 0) {
+ return PNG_COLOR_TYPE_RGB;
+ }
+ return PNG_COLOR_TYPE_RGBA;
+}
+
+// Assigns indices to the color and alpha palettes, encodes them, and then invokes
+// png_set_PLTE/png_set_tRNS.
+// This must be done before writing image data.
+// Image data must be transformed to use the indices assigned within the palette.
+static void writePalette(png_structp writePtr, png_infop writeInfoPtr,
+ std::unordered_map<uint32_t, int>* colorPalette,
+ std::unordered_set<uint32_t>* alphaPalette) {
+ assert(colorPalette->size() <= 256);
+ assert(alphaPalette->size() <= 256);
+
+ // Populate the PNG palette struct and assign indices to the color
+ // palette.
+
+ // Colors in the alpha palette should have smaller indices.
+ // This will ensure that we can truncate the alpha palette if it is
+ // smaller than the color palette.
+ int index = 0;
+ for (uint32_t color : *alphaPalette) {
+ (*colorPalette)[color] = index++;
+ }
+
+ // Assign the rest of the entries.
+ for (auto& entry : *colorPalette) {
+ if (entry.second == -1) {
+ entry.second = index++;
+ }
+ }
+
+ // Create the PNG color palette struct.
+ auto colorPaletteBytes = std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
+
+ std::unique_ptr<png_byte[]> alphaPaletteBytes;
+ if (!alphaPalette->empty()) {
+ alphaPaletteBytes = std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
+ }
+
+ for (const auto& entry : *colorPalette) {
+ const uint32_t color = entry.first;
+ const int index = entry.second;
+ assert(index >= 0);
+ assert(static_cast<size_t>(index) < colorPalette->size());
+
+ png_colorp slot = colorPaletteBytes.get() + index;
+ slot->red = color >> 24;
+ slot->green = color >> 16;
+ slot->blue = color >> 8;
+
+ const png_byte alpha = color & 0x000000ff;
+ if (alpha != 0xff && alphaPaletteBytes) {
+ assert(static_cast<size_t>(index) < alphaPalette->size());
+ alphaPaletteBytes[index] = alpha;
+ }
+ }
+
+ // The bytes get copied here, so it is safe to release colorPaletteBytes at the end of function
+ // scope.
+ png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(), colorPalette->size());
+
+ if (alphaPaletteBytes) {
+ png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(), alphaPalette->size(),
+ nullptr);
+ }
+}
+
+// Write the 9-patch custom PNG chunks to writeInfoPtr. This must be done before
+// writing image data.
+static void writeNinePatch(png_structp writePtr, png_infop writeInfoPtr,
+ const NinePatch* ninePatch) {
+ // The order of the chunks is important.
+ // 9-patch code in older platforms expects the 9-patch chunk to
+ // be last.
+
+ png_unknown_chunk unknownChunks[3];
+ memset(unknownChunks, 0, sizeof(unknownChunks));
+
+ size_t index = 0;
+ size_t chunkLen = 0;
+
+ std::unique_ptr<uint8_t[]> serializedOutline =
+ ninePatch->serializeRoundedRectOutline(&chunkLen);
+ strcpy((char*) unknownChunks[index].name, "npOl");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep) serializedOutline.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
+
+ std::unique_ptr<uint8_t[]> serializedLayoutBounds;
+ if (ninePatch->layoutBounds.nonZero()) {
+ serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
+ strcpy((char*) unknownChunks[index].name, "npLb");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep) serializedLayoutBounds.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
+ }
+
+ std::unique_ptr<uint8_t[]> serializedNinePatch = ninePatch->serializeBase(&chunkLen);
+ strcpy((char*) unknownChunks[index].name, "npTc");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep) serializedNinePatch.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
+
+ // Handle all unknown chunks. We are manually setting the chunks here,
+ // so we will only ever handle our custom chunks.
+ png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
+
+ // Set the actual chunks here. The data gets copied, so our buffers can
+ // safely go out of scope.
+ png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
+}
+
+bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
+ io::OutputStream* out, const PngOptions& options) {
+ // Create and initialize the write png_struct with the default error and warning handlers.
+ // The header version is also passed in to ensure that this was built against the same
+ // version of libpng.
+ png_structp writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ nullptr, nullptr, nullptr);
+ if (writePtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "failed to create libpng write png_struct");
+ return false;
+ }
+
+ // Allocate memory to store image header data.
+ png_infop writeInfoPtr = png_create_info_struct(writePtr);
+ if (writeInfoPtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage() << "failed to create libpng write png_info");
+ png_destroy_write_struct(&writePtr, nullptr);
+ return false;
+ }
+
+ // Automatically release PNG resources at end of scope.
+ PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
+
+ // libpng uses longjmp to jump to error handling routines.
+ // setjmp will return true only if it was jumped to, aka, there was an error.
+ if (setjmp(png_jmpbuf(writePtr))) {
+ return false;
+ }
+
+ // Handle warnings with our IDiagnostics.
+ png_set_error_fn(writePtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+
+ // Set up the write functions which write to our custom data sources.
+ png_set_write_fn(writePtr, (png_voidp) out, writeDataToStream, nullptr);
+
+ // We want small files and can take the performance hit to achieve this goal.
+ png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+
+ // Begin analysis of the image data.
+ // Scan the entire image and determine if:
+ // 1. Every pixel has R == G == B (grayscale)
+ // 2. Every pixel has A == 255 (opaque)
+ // 3. There are no more than 256 distinct RGBA colors (palette).
+ std::unordered_map<uint32_t, int> colorPalette;
+ std::unordered_set<uint32_t> alphaPalette;
+ bool needsToZeroRGBChannelsOfTransparentPixels = false;
+ bool grayScale = true;
+ int maxGrayDeviation = 0;
+
+ for (int32_t y = 0; y < image->height; y++) {
+ const uint8_t* row = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int red = *row++;
+ int green = *row++;
+ int blue = *row++;
+ int alpha = *row++;
+
+ if (alpha == 0) {
+ // The color is completely transparent.
+ // For purposes of palettes and grayscale optimization,
+ // treat all channels as 0x00.
+ needsToZeroRGBChannelsOfTransparentPixels =
+ needsToZeroRGBChannelsOfTransparentPixels ||
+ (red != 0 || green != 0 || blue != 0);
+ red = green = blue = 0;
+ }
+
+ // Insert the color into the color palette.
+ const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
+ colorPalette[color] = -1;
+
+ // If the pixel has non-opaque alpha, insert it into the
+ // alpha palette.
+ if (alpha != 0xff) {
+ alphaPalette.insert(color);
+ }
+
+ // Check if the image is indeed grayscale.
+ if (grayScale) {
+ if (red != green || red != blue) {
+ grayScale = false;
+ }
+ }
+
+ // Calculate the gray scale deviation so that it can be compared
+ // with the threshold.
+ maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
+ maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
+ maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
+ }
+ }
+
+ if (context->verbose()) {
+ DiagMessage msg;
+ msg << " paletteSize=" << colorPalette.size()
+ << " alphaPaletteSize=" << alphaPalette.size()
+ << " maxGrayDeviation=" << maxGrayDeviation
+ << " grayScale=" << (grayScale ? "true" : "false");
+ context->getDiagnostics()->note(msg);
+ }
+
+ const bool convertibleToGrayScale = maxGrayDeviation <= options.grayScaleTolerance;
+
+ const int newColorType = pickColorType(image->width, image->height, grayScale,
+ convertibleToGrayScale, ninePatch != nullptr,
+ colorPalette.size(), alphaPalette.size());
+
+ if (context->verbose()) {
+ DiagMessage msg;
+ msg << "encoding PNG ";
+ if (ninePatch) {
+ msg << "(with 9-patch) as ";
+ }
+ switch (newColorType) {
+ case PNG_COLOR_TYPE_GRAY:
+ msg << "GRAY";
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ msg << "GRAY + ALPHA";
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ msg << "RGB";
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ msg << "RGBA";
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ msg << "PALETTE";
+ break;
+ default:
+ msg << "unknown type " << newColorType;
+ break;
+ }
+ context->getDiagnostics()->note(msg);
+ }
+
+ png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8, newColorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ if (newColorType & PNG_COLOR_MASK_PALETTE) {
+ // Assigns indices to the palette, and writes the encoded palette to the libpng writePtr.
+ writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
+ png_set_filter(writePtr, 0, PNG_NO_FILTERS);
+ } else {
+ png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+ }
+
+ if (ninePatch) {
+ writeNinePatch(writePtr, writeInfoPtr, ninePatch);
+ }
+
+ // Flush our updates to the header.
+ png_write_info(writePtr, writeInfoPtr);
+
+ // Write out each row of image data according to its encoding.
+ if (newColorType == PNG_COLOR_TYPE_PALETTE) {
+ // 1 byte/pixel.
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *inRow++;
+ int gg = *inRow++;
+ int bb = *inRow++;
+ int aa = *inRow++;
+ if (aa == 0) {
+ // Zero out color channels when transparent.
+ rr = gg = bb = 0;
+ }
+
+ const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
+ const int idx = colorPalette[color];
+ assert(idx != -1);
+ outRow[x] = static_cast<png_byte>(idx);
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else if (newColorType == PNG_COLOR_TYPE_GRAY || newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = inRow[x * 4];
+ int gg = inRow[x * 4 + 1];
+ int bb = inRow[x * 4 + 2];
+ int aa = inRow[x * 4 + 3];
+ if (aa == 0) {
+ // Zero out the gray channel when transparent.
+ rr = gg = bb = 0;
+ }
+
+ if (grayScale) {
+ // The image was already grayscale, red == green == blue.
+ outRow[x * bpp] = inRow[x * 4];
+ } else {
+ // The image is convertible to grayscale, use linear-luminance of
+ // sRGB colorspace: https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
+ outRow[x * bpp] = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
+ }
+
+ if (bpp == 2) {
+ // Write out alpha if we have it.
+ outRow[x * bpp + 1] = aa;
+ }
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else if (newColorType == PNG_COLOR_TYPE_RGB || newColorType == PNG_COLOR_TYPE_RGBA) {
+ const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
+ if (needsToZeroRGBChannelsOfTransparentPixels) {
+ // The source RGBA data can't be used as-is, because we need to zero out the RGB
+ // values of transparent pixels.
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *inRow++;
+ int gg = *inRow++;
+ int bb = *inRow++;
+ int aa = *inRow++;
+ if (aa == 0) {
+ // Zero out the RGB channels when transparent.
+ rr = gg = bb = 0;
+ }
+ outRow[x * bpp] = rr;
+ outRow[x * bpp + 1] = gg;
+ outRow[x * bpp + 2] = bb;
+ if (bpp == 4) {
+ outRow[x * bpp + 3] = aa;
+ }
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else {
+ // The source image can be used as-is, just tell libpng whether or not to ignore
+ // the alpha channel.
+ if (newColorType == PNG_COLOR_TYPE_RGB) {
+ // Delete the extraneous alpha values that we appended to our buffer
+ // when reading the original values.
+ png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
+ }
+ png_write_image(writePtr, image->rows.get());
+ }
+ } else {
+ assert(false && "unreachable");
+ }
+
+ png_write_end(writePtr, writeInfoPtr);
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png b/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
new file mode 100644
index 0000000..0522a99
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
new file mode 100644
index 0000000..baf9fff
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
new file mode 100644
index 0000000..7b331e1
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
new file mode 100644
index 0000000..0ec6c70
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
new file mode 100644
index 0000000..e05708a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
new file mode 100644
index 0000000..a11377a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
new file mode 100644
index 0000000..6803e42
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
new file mode 100644
index 0000000..1a3731b
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
new file mode 100644
index 0000000..489ace2
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/io/Io.cpp b/tools/aapt2/io/Io.cpp
new file mode 100644
index 0000000..963c21c
--- /dev/null
+++ b/tools/aapt2/io/Io.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#include "io/Io.h"
+
+#include <algorithm>
+#include <cstring>
+
+namespace aapt {
+namespace io {
+
+bool copy(OutputStream* out, InputStream* in) {
+ const void* inBuffer;
+ int inLen;
+ while (in->Next(&inBuffer, &inLen)) {
+ void* outBuffer;
+ int outLen;
+ if (!out->Next(&outBuffer, &outLen)) {
+ return !out->HadError();
+ }
+
+ const int bytesToCopy = std::min(inLen, outLen);
+ memcpy(outBuffer, inBuffer, bytesToCopy);
+ out->BackUp(outLen - bytesToCopy);
+ in->BackUp(inLen - bytesToCopy);
+ }
+ return !in->HadError();
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h
new file mode 100644
index 0000000..e1e9107
--- /dev/null
+++ b/tools/aapt2/io/Io.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_IO_IO_H
+#define AAPT_IO_IO_H
+
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <string>
+
+namespace aapt {
+namespace io {
+
+/**
+ * InputStream interface that inherits from protobuf's ZeroCopyInputStream,
+ * but adds error handling methods to better report issues.
+ *
+ * The code style here matches the protobuf style.
+ */
+class InputStream : public google::protobuf::io::ZeroCopyInputStream {
+public:
+ virtual std::string GetError() const {
+ return {};
+ }
+
+ virtual bool HadError() const = 0;
+};
+
+/**
+ * OutputStream interface that inherits from protobuf's ZeroCopyOutputStream,
+ * but adds error handling methods to better report issues.
+ *
+ * The code style here matches the protobuf style.
+ */
+class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
+public:
+ virtual std::string GetError() const {
+ return {};
+ }
+
+ virtual bool HadError() const = 0;
+};
+
+/**
+ * Copies the data from in to out. Returns true if there was no error.
+ * If there was an error, check the individual streams' HadError/GetError
+ * methods.
+ */
+bool copy(OutputStream* out, InputStream* in);
+
+} // namespace io
+} // namespace aapt
+
+#endif /* AAPT_IO_IO_H */
diff --git a/tools/aapt2/util/BigBuffer.cpp b/tools/aapt2/util/BigBuffer.cpp
index c88e3c1..de4ecd2 100644
--- a/tools/aapt2/util/BigBuffer.cpp
+++ b/tools/aapt2/util/BigBuffer.cpp
@@ -49,4 +49,29 @@
return mBlocks.back().buffer.get();
}
+void* BigBuffer::nextBlock(size_t* outSize) {
+ if (!mBlocks.empty()) {
+ Block& block = mBlocks.back();
+ if (block.size != block.mBlockSize) {
+ void* outBuffer = block.buffer.get() + block.size;
+ size_t size = block.mBlockSize - block.size;
+ block.size = block.mBlockSize;
+ mSize += size;
+ *outSize = size;
+ return outBuffer;
+ }
+ }
+
+ // Zero-allocate the block's buffer.
+ Block block = {};
+ block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[mBlockSize]());
+ assert(block.buffer);
+ block.size = mBlockSize;
+ block.mBlockSize = mBlockSize;
+ mBlocks.push_back(std::move(block));
+ mSize += mBlockSize;
+ *outSize = mBlockSize;
+ return mBlocks.back().buffer.get();
+}
+
} // namespace aapt
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index ba8532f..685614f 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -82,6 +82,20 @@
T* nextBlock(size_t count = 1);
/**
+ * Returns the next block available and puts the size in outCount.
+ * This is useful for grabbing blocks where the size doesn't matter.
+ * Use backUp() to give back any bytes that were not used.
+ */
+ void* nextBlock(size_t* outCount);
+
+ /**
+ * Backs up count bytes. This must only be called after nextBlock()
+ * and can not be larger than sizeof(T) * count of the last nextBlock()
+ * call.
+ */
+ void backUp(size_t count);
+
+ /**
* Moves the specified BigBuffer into this one. When this method
* returns, buffer is empty.
*/
@@ -97,6 +111,8 @@
*/
void align4();
+ size_t getBlockSize() const;
+
const_iterator begin() const;
const_iterator end() const;
@@ -123,6 +139,10 @@
return mSize;
}
+inline size_t BigBuffer::getBlockSize() const {
+ return mBlockSize;
+}
+
template <typename T>
inline T* BigBuffer::nextBlock(size_t count) {
static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
@@ -130,6 +150,12 @@
return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
}
+inline void BigBuffer::backUp(size_t count) {
+ Block& block = mBlocks.back();
+ block.size -= count;
+ mSize -= count;
+}
+
inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
mSize += buffer.mSize;
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 4300a67..266c003 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -39,6 +39,9 @@
using const_iterator = const TChar*;
using difference_type = size_t;
+ // End of string marker.
+ constexpr static const size_t npos = static_cast<size_t>(-1);
+
BasicStringPiece();
BasicStringPiece(const BasicStringPiece<TChar>& str);
BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit)
@@ -48,7 +51,7 @@
BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
- BasicStringPiece<TChar> substr(size_t start, size_t len) const;
+ BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
BasicStringPiece<TChar>::const_iterator end) const;
@@ -81,6 +84,9 @@
//
template <typename TChar>
+constexpr const size_t BasicStringPiece<TChar>::npos;
+
+template <typename TChar>
inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
}
@@ -127,7 +133,11 @@
template <typename TChar>
inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
- if (start + len > mLength) {
+ if (len == npos) {
+ len = mLength - start;
+ }
+
+ if (start > mLength || start + len > mLength) {
return BasicStringPiece<TChar>();
}
return BasicStringPiece<TChar>(mData + start, len);