Merge "Always call finishBackup() if performFullBackup() succeeded" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 06e98ef..b2689bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25492,6 +25492,8 @@
public static final class Telephony.Mms.Intents {
field public static final java.lang.String CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED";
field public static final java.lang.String DELETED_CONTENTS = "deleted_contents";
+ field public static final java.lang.String MMS_DOWNLOAD_ACTION = "android.provider.Telephony.MMS_DOWNLOAD";
+ field public static final java.lang.String MMS_SEND_ACTION = "android.provider.Telephony.MMS_SEND";
}
public static final class Telephony.Mms.Outbox implements android.provider.Telephony.BaseMmsColumns {
@@ -25595,8 +25597,10 @@
field public static final java.lang.String SMS_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_CB_RECEIVED";
field public static final java.lang.String SMS_DELIVER_ACTION = "android.provider.Telephony.SMS_DELIVER";
field public static final java.lang.String SMS_EMERGENCY_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
+ field public static final java.lang.String SMS_FILTER_ACTION = "android.provider.Telephony.SMS_FILTER";
field public static final java.lang.String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
field public static final java.lang.String SMS_REJECTED_ACTION = "android.provider.Telephony.SMS_REJECTED";
+ field public static final java.lang.String SMS_SEND_ACTION = "android.provider.Telephony.SMS_SEND";
field public static final java.lang.String SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION = "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED";
field public static final java.lang.String WAP_PUSH_DELIVER_ACTION = "android.provider.Telephony.WAP_PUSH_DELIVER";
field public static final java.lang.String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
@@ -28782,9 +28786,13 @@
public final class SmsManager {
method public java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
method public static android.telephony.SmsManager getDefault();
+ method public void injectSmsPdu(byte[], java.lang.String, android.app.PendingIntent);
method public void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
method public void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
method public void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
+ method public void updateMmsDownloadStatus(int, byte[]);
+ method public void updateMmsSendStatus(int, boolean);
+ method public void updateSmsSendStatus(int, boolean);
field public static final int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1
field public static final int RESULT_ERROR_NO_SERVICE = 4; // 0x4
field public static final int RESULT_ERROR_NULL_PDU = 3; // 0x3
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b7f1ff9..d6c17ae 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -998,8 +998,8 @@
final InstallSessionParams params = new InstallSessionParams();
params.installFlags = PackageManager.INSTALL_ALL_USERS;
- params.mode = InstallSessionParams.MODE_FULL_INSTALL;
- params.progressMax = -1;
+ params.setModeFullInstall();
+ params.setProgressMax(-1);
String opt;
while ((opt = nextOption()) != null) {
@@ -1021,10 +1021,11 @@
} else if (opt.equals("-d")) {
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else if (opt.equals("-p")) {
- params.mode = InstallSessionParams.MODE_INHERIT_EXISTING;
+ params.setModeInheritExisting();
} else if (opt.equals("-S")) {
- params.deltaSize = Long.parseLong(nextOptionData());
- params.progressMax = (int) params.deltaSize;
+ final long deltaSize = Long.parseLong(nextOptionData());
+ params.setDeltaSize(deltaSize);
+ params.setProgressMax((int) params.deltaSize);
} else if (opt.equals("--abi")) {
params.abiOverride = checkAbiArgument(nextOptionData());
} else {
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index ac74ca1..8a53e08 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -64,6 +64,8 @@
int mInitialMinute;
boolean mIs24HourView;
+ private boolean mIsCanceled;
+
/**
* @param context Parent.
* @param callBack How parent is notified.
@@ -124,10 +126,13 @@
mTimePicker.setDismissCallback(new TimePicker.TimePickerDismissCallback() {
@Override
public void dismiss(TimePicker view, boolean isCancel, int hourOfDay, int minute) {
+ mIsCanceled = isCancel;
if (!isCancel) {
mTimeSetCallback.onTimeSet(view, hourOfDay, minute);
+ TimePickerDialog.this.dismiss();
+ } else {
+ TimePickerDialog.this.cancel();
}
- TimePickerDialog.this.dismiss();
}
});
mTimePicker.setIs24HourView(mIs24HourView);
@@ -150,7 +155,7 @@
}
private void tryNotifyTimeSet() {
- if (mTimeSetCallback != null) {
+ if (mTimeSetCallback != null && !mIsCanceled) {
mTimePicker.clearFocus();
mTimeSetCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
mTimePicker.getCurrentMinute());
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 6cda905..3f10b92 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -75,9 +75,7 @@
public static final int DENSITY_400 = 400;
/**
- * Standard quantized DPI for extra-extra-high-density screens. Applications
- * should not generally worry about this density; relying on XHIGH graphics
- * being scaled up to it should be sufficient for almost all cases.
+ * Standard quantized DPI for extra-extra-high-density screens.
*/
public static final int DENSITY_XXHIGH = 480;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 64366e5..1f7acec 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -39,6 +39,7 @@
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <cutils/sched_policy.h>
+#include <private/android_filesystem_config.h>
#include <utils/String8.h>
#include <selinux/android.h>
#include <processgroup/processgroup.h>
@@ -536,8 +537,15 @@
jint debug_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose) {
+ // Grant CAP_WAKE_ALARM to the Bluetooth process.
+ jlong capabilities = 0;
+ if (uid == AID_BLUETOOTH) {
+ capabilities |= (1LL << CAP_WAKE_ALARM);
+ }
+
return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
- rlimits, 0, 0, mount_external, se_info, se_name, false, fdsToClose);
+ rlimits, capabilities, capabilities, mount_external, se_info,
+ se_name, false, fdsToClose);
}
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
diff --git a/core/res/res/drawable/btn_check_material_anim.xml b/core/res/res/drawable/btn_check_material_anim.xml
index 73b8a3e..1e05e84 100644
--- a/core/res/res/drawable/btn_check_material_anim.xml
+++ b/core/res/res/drawable/btn_check_material_anim.xml
@@ -30,28 +30,28 @@
<transition android:fromId="@+id/off" android:toId="@+id/on">
<animation-list>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_000" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_000" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_001" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_001" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_002" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_002" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_003" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_003" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_004" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_004" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_005" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_005" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_006" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_006" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_mtrl_007" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_007" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
<bitmap android:src="@drawable/btn_check_to_on_mtrl_008" android:tint="?attr/colorControlActivated" />
@@ -106,28 +106,28 @@
<bitmap android:src="@drawable/btn_check_to_off_mtrl_007" android:tint="?attr/colorControlActivated" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_008" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_008" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_009" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_009" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_010" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_010" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_011" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_011" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_012" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_012" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_013" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_013" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_014" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_014" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_mtrl_015" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_015" android:tint="?attr/colorControlNormal" />
</item>
</animation-list>
</transition>
diff --git a/docs/html/google/play/billing/billing_overview.jd b/docs/html/google/play/billing/billing_overview.jd
index 301d911..12f8c9a 100644
--- a/docs/html/google/play/billing/billing_overview.jd
+++ b/docs/html/google/play/billing/billing_overview.jd
@@ -38,6 +38,16 @@
features that you need to understand in order to add In-app
Billing features into your application.</p>
+<p class="note"><b>Note</b>: Ensure that you comply with applicable laws in the countries where you
+distribute apps. For example, in EU countries, laws based on the
+<a href="http://ec.europa.eu/justice/consumer-marketing/unfair-trade/unfair-practices/">Unfair
+Commercial Practices Directive</a> prohibit direct exhortations to children to buy advertised
+products or to persuade their parents or other adults to buy advertised products for them.
+See the
+<a href="http://ec.europa.eu/consumers/enforcement/docs/common_position_on_online_games_en.pdf">position
+of the EU consumer protection authorities</a> for more information on this and other topics.
+</p>
+
<h2 id="api">In-app Billing API</h2>
<p>Your application accesses the In-app Billing service using an API that is
exposed by the Google Play app that is installed on the device. The Google Play
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index b77a72a..71b15d5 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -349,8 +349,7 @@
<input id="agree" type="checkbox" name="agree" value="1" onclick="onAgreeChecked()" />
<label id="agreeLabel" for="agree">I have read and agree with the above terms and conditions</label>
</p>
-<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return
-onDownloadNdkForRealz(this);"></a></p>
+<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return onDownloadNdkForRealz(this);"></a></p>
</div>
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index d5aa5f8..2cb7b03 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -16,8 +16,6 @@
package android.graphics.drawable;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.annotation.NonNull;
@@ -160,48 +158,51 @@
}
private boolean selectTransition(int toIndex) {
- if (toIndex == mTransitionToIndex) {
- // Already animating to that keyframe.
- return true;
- }
-
+ final int fromIndex;
final Transition currentTransition = mTransition;
if (currentTransition != null) {
if (toIndex == mTransitionToIndex) {
+ // Already animating to that keyframe.
return true;
- } else if (toIndex == mTransitionFromIndex) {
+ } else if (toIndex == mTransitionFromIndex && currentTransition.canReverse()) {
// Reverse the current animation.
currentTransition.reverse();
- mTransitionFromIndex = mTransitionToIndex;
- mTransitionToIndex = toIndex;
+ mTransitionToIndex = mTransitionFromIndex;
+ mTransitionFromIndex = toIndex;
return true;
}
+ // Start the next transition from the end of the current one.
+ fromIndex = mTransitionToIndex;
+
// Changing animation, end the current animation.
currentTransition.stop();
- mTransition = null;
+ } else {
+ fromIndex = getCurrentIndex();
}
// Reset state.
+ mTransition = null;
mTransitionFromIndex = -1;
mTransitionToIndex = -1;
final AnimatedStateListState state = mState;
- final int fromIndex = getCurrentIndex();
final int fromId = state.getKeyframeIdAt(fromIndex);
final int toId = state.getKeyframeIdAt(toIndex);
-
if (toId == 0 || fromId == 0) {
// Missing a keyframe ID.
return false;
}
final int transitionIndex = state.indexOfTransition(fromId, toId);
- if (transitionIndex < 0 || !selectDrawable(transitionIndex)) {
+ if (transitionIndex < 0) {
// Couldn't select a transition.
return false;
}
+ // This may fail if we're already on the transition, but that's okay!
+ selectDrawable(transitionIndex);
+
final Transition transition;
final Drawable d = getCurrent();
if (d instanceof AnimationDrawable) {
@@ -500,17 +501,6 @@
return this;
}
- private final AnimatorListenerAdapter mAnimListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator anim) {
- selectDrawable(mTransitionToIndex);
-
- mTransitionToIndex = -1;
- mTransitionFromIndex = -1;
- mTransition = null;
- }
- };
-
static class AnimatedStateListState extends StateListState {
private static final int REVERSE_SHIFT = 32;
private static final int REVERSE_MASK = 0x1;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b32d3dd..78a99e0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -456,9 +456,11 @@
} else {
// Launch the activity anew with the desired animation
Intent i = new Intent(task.key.baseIntent);
- i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
- | Intent.FLAG_ACTIVITY_TASK_ON_HOME
- | Intent.FLAG_ACTIVITY_NEW_TASK);
+ i.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ if ((i.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == 0) {
+ i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
try {
UserHandle taskUser = new UserHandle(task.userId);
if (launchOpts != null) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 190e87c..db915e2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -76,6 +76,10 @@
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
+ /** Historical sessions kept around for debugging purposes */
+ @GuardedBy("mSessions")
+ private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
+
private RemoteCallbackList<IPackageInstallerObserver> mObservers = new RemoteCallbackList<>();
private static final FilenameFilter sStageFilter = new FilenameFilter() {
@@ -344,18 +348,29 @@
}
void dump(IndentingPrintWriter pw) {
- pw.println("Active install sessions:");
- pw.increaseIndent();
synchronized (mSessions) {
- final int N = mSessions.size();
+ pw.println("Active install sessions:");
+ pw.increaseIndent();
+ int N = mSessions.size();
for (int i = 0; i < N; i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
session.dump(pw);
pw.println();
}
+ pw.println();
+ pw.decreaseIndent();
+
+ pw.println("Historical install sessions:");
+ pw.increaseIndent();
+ N = mHistoricalSessions.size();
+ for (int i = 0; i < N; i++) {
+ final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
+ session.dump(pw);
+ pw.println();
+ }
+ pw.println();
+ pw.decreaseIndent();
}
- pw.println();
- pw.decreaseIndent();
}
class Callback {
@@ -367,6 +382,7 @@
notifySessionFinished(session.sessionId, success);
synchronized (mSessions) {
mSessions.remove(session.sessionId);
+ mHistoricalSessions.put(session.sessionId, session);
}
writeSessionsAsync();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 31d9704..0e6a3f0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -65,6 +65,7 @@
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG = "PackageInstaller";
+ private static final boolean LOGD = true;
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -435,35 +436,25 @@
*/
private void spliceExistingFilesIntoStage() throws PackageManagerException {
final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
- final File existingDir = new File(app.getBaseCodePath());
- try {
- linkTreeIgnoringExisting(existingDir, sessionStageDir);
- } catch (ErrnoException e) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed to splice into stage");
- }
- }
+ int n = 0;
+ final File[] oldFiles = new File(app.getCodePath()).listFiles();
+ if (!ArrayUtils.isEmpty(oldFiles)) {
+ for (File oldFile : oldFiles) {
+ if (!PackageParser.isApkFile(oldFile)) continue;
- /**
- * Recursively hard link all files from source directory tree to target.
- * When a file already exists in the target tree, it leaves that file
- * intact.
- */
- private void linkTreeIgnoringExisting(File sourceDir, File targetDir) throws ErrnoException {
- final File[] sourceContents = sourceDir.listFiles();
- if (ArrayUtils.isEmpty(sourceContents)) return;
-
- for (File sourceFile : sourceContents) {
- final File targetFile = new File(targetDir, sourceFile.getName());
-
- if (sourceFile.isDirectory()) {
- targetFile.mkdir();
- linkTreeIgnoringExisting(sourceFile, targetFile);
- } else {
- Libcore.os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
+ final File newFile = new File(sessionStageDir, oldFile.getName());
+ try {
+ Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath());
+ n++;
+ } catch (ErrnoException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to splice into stage", e);
+ }
}
}
+
+ if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage");
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0ad3a68..727cff0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10026,9 +10026,6 @@
}
}
- // Nuke any cached code
- deleteCodeCacheDirsLI(pkgName);
-
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
@@ -10066,6 +10063,7 @@
deletedPkg = false;
} else {
// Successfully deleted the old package. Now proceed with re-installation
+ deleteCodeCacheDirsLI(pkgName);
try {
final PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,
scanMode | SCAN_UPDATE_TIME, System.currentTimeMillis(), user, abiOverride);
@@ -10177,6 +10175,8 @@
}
// Successfully disabled the old package. Now proceed with re-installation
+ deleteCodeCacheDirsLI(packageName);
+
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
diff --git a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java
new file mode 100644
index 0000000..a3ec2cc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2014 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;
+
+import java.awt.Composite;
+import java.awt.CompositeContext;
+import java.awt.RenderingHints;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+
+/*
+ * (non-Javadoc)
+ * The class is adapted from a demo tool for Blending Modes written by
+ * Romain Guy (romainguy@android.com). The tool is available at
+ * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
+ */
+public final class BlendComposite implements Composite {
+ public enum BlendingMode {
+ NORMAL,
+ AVERAGE,
+ MULTIPLY,
+ SCREEN,
+ DARKEN,
+ LIGHTEN,
+ OVERLAY,
+ HARD_LIGHT,
+ SOFT_LIGHT,
+ DIFFERENCE,
+ NEGATION,
+ EXCLUSION,
+ COLOR_DODGE,
+ INVERSE_COLOR_DODGE,
+ SOFT_DODGE,
+ COLOR_BURN,
+ INVERSE_COLOR_BURN,
+ SOFT_BURN,
+ REFLECT,
+ GLOW,
+ FREEZE,
+ HEAT,
+ ADD,
+ SUBTRACT,
+ STAMP,
+ RED,
+ GREEN,
+ BLUE,
+ HUE,
+ SATURATION,
+ COLOR,
+ LUMINOSITY
+ }
+
+ public static final BlendComposite Normal = new BlendComposite(BlendingMode.NORMAL);
+ public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE);
+ public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
+ public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
+ public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
+ public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
+ public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
+ public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT);
+ public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT);
+ public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE);
+ public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION);
+ public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION);
+ public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE);
+ public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE);
+ public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE);
+ public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN);
+ public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN);
+ public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN);
+ public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT);
+ public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW);
+ public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE);
+ public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT);
+ public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
+ public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT);
+ public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP);
+ public static final BlendComposite Red = new BlendComposite(BlendingMode.RED);
+ public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN);
+ public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE);
+ public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE);
+ public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION);
+ public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR);
+ public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY);
+
+ private float alpha;
+ private BlendingMode mode;
+
+ private BlendComposite(BlendingMode mode) {
+ this(mode, 1.0f);
+ }
+
+ private BlendComposite(BlendingMode mode, float alpha) {
+ this.mode = mode;
+ setAlpha(alpha);
+ }
+
+ public static BlendComposite getInstance(BlendingMode mode) {
+ return new BlendComposite(mode);
+ }
+
+ public static BlendComposite getInstance(BlendingMode mode, float alpha) {
+ return new BlendComposite(mode, alpha);
+ }
+
+ public BlendComposite derive(BlendingMode mode) {
+ return this.mode == mode ? this : new BlendComposite(mode, getAlpha());
+ }
+
+ public BlendComposite derive(float alpha) {
+ return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha);
+ }
+
+ public float getAlpha() {
+ return alpha;
+ }
+
+ public BlendingMode getMode() {
+ return mode;
+ }
+
+ private void setAlpha(float alpha) {
+ if (alpha < 0.0f || alpha > 1.0f) {
+ throw new IllegalArgumentException(
+ "alpha must be comprised between 0.0f and 1.0f");
+ }
+
+ this.alpha = alpha;
+ }
+
+ @Override
+ public int hashCode() {
+ return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof BlendComposite)) {
+ return false;
+ }
+
+ BlendComposite bc = (BlendComposite) obj;
+
+ if (mode != bc.mode) {
+ return false;
+ }
+
+ return alpha == bc.alpha;
+ }
+
+ public CompositeContext createContext(ColorModel srcColorModel,
+ ColorModel dstColorModel,
+ RenderingHints hints) {
+ return new BlendingContext(this);
+ }
+
+ private static final class BlendingContext implements CompositeContext {
+ private final Blender blender;
+ private final BlendComposite composite;
+
+ private BlendingContext(BlendComposite composite) {
+ this.composite = composite;
+ this.blender = Blender.getBlenderFor(composite);
+ }
+
+ public void dispose() {
+ }
+
+ public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
+ if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
+ dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
+ dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
+ throw new IllegalStateException(
+ "Source and destination must store pixels as INT.");
+ }
+
+ int width = Math.min(src.getWidth(), dstIn.getWidth());
+ int height = Math.min(src.getHeight(), dstIn.getHeight());
+
+ float alpha = composite.getAlpha();
+
+ int[] srcPixel = new int[4];
+ int[] dstPixel = new int[4];
+ int[] result = new int[4];
+ int[] srcPixels = new int[width];
+ int[] dstPixels = new int[width];
+
+ for (int y = 0; y < height; y++) {
+ dstIn.getDataElements(0, y, width, 1, dstPixels);
+ if (alpha != 0) {
+ src.getDataElements(0, y, width, 1, srcPixels);
+ for (int x = 0; x < width; x++) {
+ // pixels are stored as INT_ARGB
+ // our arrays are [R, G, B, A]
+ int pixel = srcPixels[x];
+ srcPixel[0] = (pixel >> 16) & 0xFF;
+ srcPixel[1] = (pixel >> 8) & 0xFF;
+ srcPixel[2] = (pixel ) & 0xFF;
+ srcPixel[3] = (pixel >> 24) & 0xFF;
+
+ pixel = dstPixels[x];
+ dstPixel[0] = (pixel >> 16) & 0xFF;
+ dstPixel[1] = (pixel >> 8) & 0xFF;
+ dstPixel[2] = (pixel ) & 0xFF;
+ dstPixel[3] = (pixel >> 24) & 0xFF;
+
+ result = blender.blend(srcPixel, dstPixel, result);
+
+ // mixes the result with the opacity
+ if (alpha == 1) {
+ dstPixels[x] = (result[3] & 0xFF) << 24 |
+ (result[0] & 0xFF) << 16 |
+ (result[1] & 0xFF) << 8 |
+ result[2] & 0xFF;
+ } else {
+ dstPixels[x] =
+ ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
+ ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
+ ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 |
+ (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
+ }
+
+ }
+ }
+ dstOut.setDataElements(0, y, width, 1, dstPixels);
+ }
+ }
+ }
+
+ private static abstract class Blender {
+ public abstract int[] blend(int[] src, int[] dst, int[] result);
+
+ private static void RGBtoHSL(int r, int g, int b, float[] hsl) {
+ float var_R = (r / 255f);
+ float var_G = (g / 255f);
+ float var_B = (b / 255f);
+
+ float var_Min;
+ float var_Max;
+ float del_Max;
+
+ if (var_R > var_G) {
+ var_Min = var_G;
+ var_Max = var_R;
+ } else {
+ var_Min = var_R;
+ var_Max = var_G;
+ }
+ if (var_B > var_Max) {
+ var_Max = var_B;
+ }
+ if (var_B < var_Min) {
+ var_Min = var_B;
+ }
+
+ del_Max = var_Max - var_Min;
+
+ float H, S, L;
+ L = (var_Max + var_Min) / 2f;
+
+ if (del_Max - 0.01f <= 0.0f) {
+ H = 0;
+ S = 0;
+ } else {
+ if (L < 0.5f) {
+ S = del_Max / (var_Max + var_Min);
+ } else {
+ S = del_Max / (2 - var_Max - var_Min);
+ }
+
+ float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max;
+ float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max;
+ float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max;
+
+ if (var_R == var_Max) {
+ H = del_B - del_G;
+ } else if (var_G == var_Max) {
+ H = (1 / 3f) + del_R - del_B;
+ } else {
+ H = (2 / 3f) + del_G - del_R;
+ }
+ if (H < 0) {
+ H += 1;
+ }
+ if (H > 1) {
+ H -= 1;
+ }
+ }
+
+ hsl[0] = H;
+ hsl[1] = S;
+ hsl[2] = L;
+ }
+
+ private static void HSLtoRGB(float h, float s, float l, int[] rgb) {
+ int R, G, B;
+
+ if (s - 0.01f <= 0.0f) {
+ R = (int) (l * 255.0f);
+ G = (int) (l * 255.0f);
+ B = (int) (l * 255.0f);
+ } else {
+ float var_1, var_2;
+ if (l < 0.5f) {
+ var_2 = l * (1 + s);
+ } else {
+ var_2 = (l + s) - (s * l);
+ }
+ var_1 = 2 * l - var_2;
+
+ R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f)));
+ G = (int) (255.0f * hue2RGB(var_1, var_2, h));
+ B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f)));
+ }
+
+ rgb[0] = R;
+ rgb[1] = G;
+ rgb[2] = B;
+ }
+
+ private static float hue2RGB(float v1, float v2, float vH) {
+ if (vH < 0.0f) {
+ vH += 1.0f;
+ }
+ if (vH > 1.0f) {
+ vH -= 1.0f;
+ }
+ if ((6.0f * vH) < 1.0f) {
+ return (v1 + (v2 - v1) * 6.0f * vH);
+ }
+ if ((2.0f * vH) < 1.0f) {
+ return (v2);
+ }
+ if ((3.0f * vH) < 2.0f) {
+ return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
+ }
+ return (v1);
+ }
+
+ public static Blender getBlenderFor(BlendComposite composite) {
+ switch (composite.getMode()) {
+ case NORMAL:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ System.arraycopy(src, 0, result, 0, 4);
+ return result;
+ }
+ };
+ case ADD:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 4; i++) {
+ result[i] = Math.min(255, src[i] + dst[i]);
+ }
+ return result;
+ }
+ };
+ case AVERAGE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = (src[i] + dst[i]) >> 1;
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case BLUE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ System.arraycopy(dst, 0, result, 0, 3);
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case COLOR:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ float[] srcHSL = new float[3];
+ RGBtoHSL(src[0], src[1], src[2], srcHSL);
+ float[] dstHSL = new float[3];
+ RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+ HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result);
+ result[3] = Math.min(255, src[3] + dst[3]);
+
+ return result;
+ }
+ };
+ case COLOR_BURN:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = src[i] == 0 ? 0 :
+ Math.max(0, 255 - (((255 - dst[i]) << 8) / src[i]));
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case COLOR_DODGE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = src[i] == 255 ? 255 :
+ Math.min((dst[i] << 8) / (255 - src[i]), 255);
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case DARKEN:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = Math.min(src[i], dst[i]);
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case DIFFERENCE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7);
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case EXCLUSION:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7);
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case FREEZE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = src[i] == 0 ? 0 :
+ Math.max(0, 255 - (255 - dst[i]) * (255 - dst[i]) / src[i]);
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case GLOW:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = dst[i] == 255 ? 255 :
+ Math.min(255, src[i] * src[i] / (255 - dst[i]));
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case GREEN:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ dst[0],
+ dst[1],
+ src[2],
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case HARD_LIGHT:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ src[0] < 128 ? dst[0] * src[0] >> 7 :
+ 255 - ((255 - src[0]) * (255 - dst[0]) >> 7),
+ src[1] < 128 ? dst[1] * src[1] >> 7 :
+ 255 - ((255 - src[1]) * (255 - dst[1]) >> 7),
+ src[2] < 128 ? dst[2] * src[2] >> 7 :
+ 255 - ((255 - src[2]) * (255 - dst[2]) >> 7),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case HEAT:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]),
+ dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]),
+ dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) / dst[2]),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case HUE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ float[] srcHSL = new float[3];
+ RGBtoHSL(src[0], src[1], src[2], srcHSL);
+ float[] dstHSL = new float[3];
+ RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+ HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result);
+ result[3] = Math.min(255, src[3] + dst[3]);
+
+ return result;
+ }
+ };
+ case INVERSE_COLOR_BURN:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ dst[0] == 0 ? 0 :
+ Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])),
+ dst[1] == 0 ? 0 :
+ Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])),
+ dst[2] == 0 ? 0 :
+ Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case INVERSE_COLOR_DODGE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ dst[0] == 255 ? 255 :
+ Math.min((src[0] << 8) / (255 - dst[0]), 255),
+ dst[1] == 255 ? 255 :
+ Math.min((src[1] << 8) / (255 - dst[1]), 255),
+ dst[2] == 255 ? 255 :
+ Math.min((src[2] << 8) / (255 - dst[2]), 255),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case LIGHTEN:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = Math.max(src[i], dst[i]);
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case LUMINOSITY:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ float[] srcHSL = new float[3];
+ RGBtoHSL(src[0], src[1], src[2], srcHSL);
+ float[] dstHSL = new float[3];
+ RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+ HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result);
+ result[3] = Math.min(255, src[3] + dst[3]);
+
+ return result;
+ }
+ };
+ case MULTIPLY:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = (src[i] * dst[i]) >> 8;
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case NEGATION:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ 255 - Math.abs(255 - dst[0] - src[0]),
+ 255 - Math.abs(255 - dst[1] - src[1]),
+ 255 - Math.abs(255 - dst[2] - src[2]),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case OVERLAY:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ for (int i = 0; i < 3; i++) {
+ result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
+ 255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
+ }
+ result[3] = Math.min(255, src[3] + dst[3]);
+ return result;
+ }
+ };
+ case RED:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ src[0],
+ dst[1],
+ dst[2],
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case REFLECT:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])),
+ src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])),
+ src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case SATURATION:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ float[] srcHSL = new float[3];
+ RGBtoHSL(src[0], src[1], src[2], srcHSL);
+ float[] dstHSL = new float[3];
+ RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+ HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result);
+ result[3] = Math.min(255, src[3] + dst[3]);
+
+ return result;
+ }
+ };
+ case SCREEN:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ 255 - ((255 - src[0]) * (255 - dst[0]) >> 8),
+ 255 - ((255 - src[1]) * (255 - dst[1]) >> 8),
+ 255 - ((255 - src[2]) * (255 - dst[2]) >> 8),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case SOFT_BURN:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ dst[0] + src[0] < 256 ?
+ (dst[0] == 255 ? 255 :
+ Math.min(255, (src[0] << 7) / (255 - dst[0]))) :
+ Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])),
+ dst[1] + src[1] < 256 ?
+ (dst[1] == 255 ? 255 :
+ Math.min(255, (src[1] << 7) / (255 - dst[1]))) :
+ Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])),
+ dst[2] + src[2] < 256 ?
+ (dst[2] == 255 ? 255 :
+ Math.min(255, (src[2] << 7) / (255 - dst[2]))) :
+ Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case SOFT_DODGE:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ dst[0] + src[0] < 256 ?
+ (src[0] == 255 ? 255 :
+ Math.min(255, (dst[0] << 7) / (255 - src[0]))) :
+ Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])),
+ dst[1] + src[1] < 256 ?
+ (src[1] == 255 ? 255 :
+ Math.min(255, (dst[1] << 7) / (255 - src[1]))) :
+ Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])),
+ dst[2] + src[2] < 256 ?
+ (src[2] == 255 ? 255 :
+ Math.min(255, (dst[2] << 7) / (255 - src[2]))) :
+ Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case SOFT_LIGHT:
+ break;
+ case STAMP:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)),
+ Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)),
+ Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ case SUBTRACT:
+ return new Blender() {
+ @Override
+ public int[] blend(int[] src, int[] dst, int[] result) {
+ return new int[] {
+ Math.max(0, src[0] + dst[0] - 256),
+ Math.max(0, src[1] + dst[1] - 256),
+ Math.max(0, src[2] + dst[2] - 256),
+ Math.min(255, src[3] + dst[3])
+ };
+ }
+ };
+ }
+ throw new IllegalArgumentException("Blender not implement for " +
+ composite.getMode().name());
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 7c8ef70..2ff0fc1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -20,6 +20,7 @@
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.GcSnapshot;
+import com.android.layoutlib.bridge.impl.PorterDuffUtility;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.graphics.Bitmap.Config;
@@ -534,7 +535,8 @@
// set the color
graphics.setColor(new Color(color, true /*alpha*/));
- Composite composite = PorterDuffXfermode_Delegate.getComposite(mode, 0xFF);
+ Composite composite = PorterDuffUtility.getComposite(
+ PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
if (composite != null) {
graphics.setComposite(composite);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index ee90595..4ac376c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -21,11 +21,10 @@
import android.graphics.PorterDuff.Mode;
-import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getComposite;
import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
/**
@@ -57,7 +56,7 @@
@Override
public boolean isSupported() {
- return getAlphaCompositeRule(mMode) != -1;
+ return true;
}
@Override
@@ -68,7 +67,7 @@
@Override
public void applyFilter(Graphics2D g, int width, int height) {
BufferedImage image = createFilterImage(width, height);
- g.setComposite(getComposite());
+ g.setComposite(getComposite(mMode, 0xFF));
g.drawImage(image, 0, 0, null);
}
@@ -101,49 +100,36 @@
return image;
}
- private AlphaComposite getComposite() {
- return AlphaComposite.getInstance(getAlphaCompositeRule(mMode));
- }
-
// For filtering the colors, the src image should contain the "color" only for pixel values
// which are not transparent in the target image. But, we are using a simple rectangular image
- // completely filled with color. Hence some AlphaComposite rules do not apply as intended.
- // However, in such cases, they can usually be mapped to some other mode, which produces an
+ // completely filled with color. Hence some Composite rules do not apply as intended. However,
+ // in such cases, they can usually be mapped to some other mode, which produces an
// equivalent result.
private Mode getCompatibleMode(Mode mode) {
Mode m = mode;
+ // Modes that are directly supported:
+ // CLEAR, DST, SRC_IN, DST_IN, DST_OUT, SRC_ATOP, DARKEN, LIGHTEN, MULTIPLY, SCREEN,
+ // ADD, OVERLAY
switch (mode) {
- // Modes that are directly supported.
- case CLEAR:
- case DST:
- case SRC_IN:
- case DST_IN:
- case DST_OUT:
- case SRC_ATOP:
- break;
- // Modes that can be mapped to one of the supported modes.
- case SRC:
- m = Mode.SRC_IN;
- break;
- case SRC_OVER:
- m = Mode.SRC_ATOP;
- break;
- case DST_OVER:
- m = Mode.DST;
- break;
- case SRC_OUT:
- m = Mode.CLEAR;
- break;
- case DST_ATOP:
- m = Mode.DST_IN;
- break;
- case XOR:
- m = Mode.DST_OUT;
- break;
- // This mode is not supported, but used by Action Bar Overflow Popup Menus. We map this
- // to the closest supported mode, to prevent showing excessive warnings to the user.
- case MULTIPLY:
- m = Mode.SRC_IN;
+ // Modes that can be mapped to one of the supported modes.
+ case SRC:
+ m = Mode.SRC_IN;
+ break;
+ case SRC_OVER:
+ m = Mode.SRC_ATOP;
+ break;
+ case DST_OVER:
+ m = Mode.DST;
+ break;
+ case SRC_OUT:
+ m = Mode.CLEAR;
+ break;
+ case DST_ATOP:
+ m = Mode.DST_IN;
+ break;
+ case XOR:
+ m = Mode.DST_OUT;
+ break;
}
return m;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index f6c36b6..8825f84 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -16,17 +16,14 @@
package android.graphics;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.PorterDuffUtility;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.graphics.PorterDuff.Mode;
-import java.awt.AlphaComposite;
import java.awt.Composite;
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
/**
@@ -58,7 +55,7 @@
@Override
public Composite getComposite(int alpha) {
- return getComposite(mMode, alpha);
+ return PorterDuffUtility.getComposite(mMode, alpha);
}
@Override
@@ -72,9 +69,6 @@
return null;
}
- public static Composite getComposite(int mode, int alpha) {
- return getComposite(getPorterDuffMode(mode), alpha);
- }
// ---- native methods ----
@@ -90,17 +84,4 @@
mMode = getPorterDuffMode(mode);
}
- private static Composite getComposite(Mode mode, int alpha255) {
- float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
- int rule = getAlphaCompositeRule(mode);
- if (rule >= 0) {
- return AlphaComposite.getInstance(rule, alpha1);
- }
-
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
- String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
- null, null /*data*/);
-
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
- }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
index bc53e93..9588035 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -19,11 +19,14 @@
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
+import android.graphics.BlendComposite;
+import android.graphics.BlendComposite.BlendingMode;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter_Delegate;
import android.graphics.PorterDuffXfermode_Delegate;
import java.awt.AlphaComposite;
+import java.awt.Composite;
/**
* Provides various utility methods for {@link PorterDuffColorFilter_Delegate} and {@link
@@ -51,46 +54,54 @@
}
/**
- * A utility method to convert the porterDuffMode to an int to be used as a rule for {@link
- * AlphaComposite}. If {@code AlphaComposite} doesn't support the mode, -1 is returned.
+ * A utility method to get the {@link Composite} that represents the filter for the given
+ * PorterDuff mode and the alpha. Defaults to {@link Mode#SRC_OVER} for invalid modes.
*/
- public static int getAlphaCompositeRule(Mode porterDuffMode) {
- switch (porterDuffMode) {
+ public static Composite getComposite(Mode mode, int alpha255) {
+ float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
+ switch (mode) {
case CLEAR:
- return AlphaComposite.CLEAR;
- case DARKEN:
- break;
- case DST:
- return AlphaComposite.DST;
- case DST_ATOP:
- return AlphaComposite.DST_ATOP;
- case DST_IN:
- return AlphaComposite.DST_IN;
- case DST_OUT:
- return AlphaComposite.DST_OUT;
- case DST_OVER:
- return AlphaComposite.DST_OVER;
- case LIGHTEN:
- break;
- case MULTIPLY:
- break;
- case SCREEN:
- break;
+ return AlphaComposite.getInstance(AlphaComposite.CLEAR, alpha1);
case SRC:
- return AlphaComposite.SRC;
- case SRC_ATOP:
- return AlphaComposite.SRC_ATOP;
- case SRC_IN:
- return AlphaComposite.SRC_IN;
- case SRC_OUT:
- return AlphaComposite.SRC_OUT;
+ return AlphaComposite.getInstance(AlphaComposite.SRC, alpha1);
+ case DST:
+ return AlphaComposite.getInstance(AlphaComposite.DST, alpha1);
case SRC_OVER:
- return AlphaComposite.SRC_OVER;
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
+ case DST_OVER:
+ return AlphaComposite.getInstance(AlphaComposite.DST_OVER, alpha1);
+ case SRC_IN:
+ return AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha1);
+ case DST_IN:
+ return AlphaComposite.getInstance(AlphaComposite.DST_IN, alpha1);
+ case SRC_OUT:
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, alpha1);
+ case DST_OUT:
+ return AlphaComposite.getInstance(AlphaComposite.DST_OUT, alpha1);
+ case SRC_ATOP:
+ return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha1);
+ case DST_ATOP:
+ return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, alpha1);
case XOR:
- return AlphaComposite.XOR;
- }
- // This is an unsupported mode.
- return -1;
+ return AlphaComposite.getInstance(AlphaComposite.XOR, alpha1);
+ case DARKEN:
+ return BlendComposite.getInstance(BlendingMode.DARKEN, alpha1);
+ case LIGHTEN:
+ return BlendComposite.getInstance(BlendingMode.LIGHTEN, alpha1);
+ case MULTIPLY:
+ return BlendComposite.getInstance(BlendingMode.MULTIPLY, alpha1);
+ case SCREEN:
+ return BlendComposite.getInstance(BlendingMode.SCREEN, alpha1);
+ case ADD:
+ return BlendComposite.getInstance(BlendingMode.ADD, alpha1);
+ case OVERLAY:
+ return BlendComposite.getInstance(BlendingMode.OVERLAY, alpha1);
+ default:
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+ String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
+ null, null /*data*/);
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
+ }
}
}