summaryrefslogtreecommitdiffstats
path: root/core/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/com')
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl2
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java20
-rw-r--r--core/java/com/android/internal/http/multipart/ByteArrayPartSource.java86
-rw-r--r--core/java/com/android/internal/http/multipart/FilePart.java259
-rw-r--r--core/java/com/android/internal/http/multipart/FilePartSource.java131
-rw-r--r--core/java/com/android/internal/http/multipart/MultipartEntity.java236
-rw-r--r--core/java/com/android/internal/http/multipart/Part.java445
-rw-r--r--core/java/com/android/internal/http/multipart/PartBase.java150
-rw-r--r--core/java/com/android/internal/http/multipart/PartSource.java72
-rw-r--r--core/java/com/android/internal/http/multipart/StringPart.java156
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodUtils.java32
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java268
-rw-r--r--core/java/com/android/internal/os/ProcessCpuTracker.java77
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
-rw-r--r--core/java/com/android/internal/policy/IPolicy.java39
-rw-r--r--core/java/com/android/internal/policy/PolicyManager.java71
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl39
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl37
-rw-r--r--core/java/com/android/internal/transition/EpicenterClipReveal.java115
-rw-r--r--core/java/com/android/internal/util/DumpUtils.java7
-rw-r--r--core/java/com/android/internal/util/UserIcons.java9
-rw-r--r--core/java/com/android/internal/view/ActionModeWrapper.java205
-rw-r--r--core/java/com/android/internal/view/StandaloneActionMode.java13
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuItemView.java6
-rw-r--r--core/java/com/android/internal/view/menu/ListMenuItemView.java4
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java6
-rw-r--r--core/java/com/android/internal/widget/AccessibleDateAnimator.java4
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java6
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java6
-rw-r--r--core/java/com/android/internal/widget/ButtonBarLayout.java91
-rw-r--r--core/java/com/android/internal/widget/ExploreByTouchHelper.java8
-rw-r--r--core/java/com/android/internal/widget/FaceUnlockView.java67
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl1
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java946
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java41
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java6
-rw-r--r--core/java/com/android/internal/widget/ScrollingTabContainerView.java47
-rw-r--r--core/java/com/android/internal/widget/SizeAdaptiveLayout.java442
-rw-r--r--core/java/com/android/internal/widget/WaveView.java663
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/Ease.java132
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/GlowPadView.java1383
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/PointCloud.java225
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java229
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/Tweener.java177
-rw-r--r--core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java380
-rw-r--r--core/java/com/android/server/backup/SystemBackupAgent.java4
46 files changed, 1487 insertions, 5858 deletions
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 5a10524..6d90420 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -26,7 +26,7 @@ import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
interface IVoiceInteractionManagerService {
- void startSession(IVoiceInteractionService service, in Bundle sessionArgs);
+ void startSession(IVoiceInteractionService service, in Bundle sessionArgs, int flags);
boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
IVoiceInteractor interactor);
int startVoiceActivity(IBinder token, in Intent intent, String resolvedType);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 649a59f..a410e45 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -505,15 +505,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// Header views don't count.
return;
}
- ResolveInfo resolveInfo = mAdapter.resolveInfoForPosition(position, true);
- if (mResolvingHome && hasManagedProfile()
- && !supportsManagedProfiles(resolveInfo)) {
- Toast.makeText(this, String.format(getResources().getString(
- com.android.internal.R.string.activity_resolver_work_profiles_support),
- resolveInfo.activityInfo.loadLabel(getPackageManager()).toString()),
- Toast.LENGTH_LONG).show();
- return;
- }
final int checkedPos = mListView.getCheckedItemPosition();
final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
@@ -579,7 +570,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
id == R.id.button_always,
mAlwaysUseOption);
- dismiss();
}
void startSelected(int which, boolean always, boolean filtered) {
@@ -587,6 +577,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return;
}
ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
+ if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
+ Toast.makeText(this, String.format(getResources().getString(
+ com.android.internal.R.string.activity_resolver_work_profiles_support),
+ ri.activityInfo.loadLabel(getPackageManager()).toString()),
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
Intent intent = mAdapter.intentForPosition(which, filtered);
onIntentSelected(ri, intent, always);
finish();
@@ -841,6 +839,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
}
+ // Clear the value of mOtherProfile from previous call.
+ mOtherProfile = null;
mList.clear();
if (mBaseResolveList != null) {
currentResolveList = mOrigResolveList = mBaseResolveList;
diff --git a/core/java/com/android/internal/http/multipart/ByteArrayPartSource.java b/core/java/com/android/internal/http/multipart/ByteArrayPartSource.java
deleted file mode 100644
index faaac7f..0000000
--- a/core/java/com/android/internal/http/multipart/ByteArrayPartSource.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/ByteArrayPartSource.java,v 1.7 2004/04/18 23:51:37 jsdever Exp $
- * $Revision: 480424 $
- * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-
-/**
- * A PartSource that reads from a byte array. This class should be used when
- * the data to post is already loaded into memory.
- *
- * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
- *
- * @since 2.0
- */
-public class ByteArrayPartSource implements PartSource {
-
- /** Name of the source file. */
- private String fileName;
-
- /** Byte array of the source file. */
- private byte[] bytes;
-
- /**
- * Constructor for ByteArrayPartSource.
- *
- * @param fileName the name of the file these bytes represent
- * @param bytes the content of this part
- */
- public ByteArrayPartSource(String fileName, byte[] bytes) {
-
- this.fileName = fileName;
- this.bytes = bytes;
-
- }
-
- /**
- * @see PartSource#getLength()
- */
- public long getLength() {
- return bytes.length;
- }
-
- /**
- * @see PartSource#getFileName()
- */
- public String getFileName() {
- return fileName;
- }
-
- /**
- * @see PartSource#createInputStream()
- */
- public InputStream createInputStream() {
- return new ByteArrayInputStream(bytes);
- }
-
-}
diff --git a/core/java/com/android/internal/http/multipart/FilePart.java b/core/java/com/android/internal/http/multipart/FilePart.java
deleted file mode 100644
index 45e4be6..0000000
--- a/core/java/com/android/internal/http/multipart/FilePart.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v 1.19 2004/04/18 23:51:37 jsdever Exp $
- * $Revision: 480424 $
- * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import org.apache.http.util.EncodingUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * This class implements a part of a Multipart post object that
- * consists of a file.
- *
- * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
- * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
- * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
- * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
- * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
- * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
- * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
- *
- * @since 2.0
- *
- * @deprecated Please use {@link java.net.URLConnection} and friends instead.
- * The Apache HTTP client is no longer maintained and may be removed in a future
- * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- * for further details.
- */
-@Deprecated
-public class FilePart extends PartBase {
-
- /** Default content encoding of file attachments. */
- public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
-
- /** Default charset of file attachments. */
- public static final String DEFAULT_CHARSET = "ISO-8859-1";
-
- /** Default transfer encoding of file attachments. */
- public static final String DEFAULT_TRANSFER_ENCODING = "binary";
-
- /** Log object for this class. */
- private static final Log LOG = LogFactory.getLog(FilePart.class);
-
- /** Attachment's file name */
- protected static final String FILE_NAME = "; filename=";
-
- /** Attachment's file name as a byte array */
- private static final byte[] FILE_NAME_BYTES =
- EncodingUtils.getAsciiBytes(FILE_NAME);
-
- /** Source of the file part. */
- private PartSource source;
-
- /**
- * FilePart Constructor.
- *
- * @param name the name for this part
- * @param partSource the source for this part
- * @param contentType the content type for this part, if <code>null</code> the
- * {@link #DEFAULT_CONTENT_TYPE default} is used
- * @param charset the charset encoding for this part, if <code>null</code> the
- * {@link #DEFAULT_CHARSET default} is used
- */
- public FilePart(String name, PartSource partSource, String contentType, String charset) {
-
- super(
- name,
- contentType == null ? DEFAULT_CONTENT_TYPE : contentType,
- charset == null ? "ISO-8859-1" : charset,
- DEFAULT_TRANSFER_ENCODING
- );
-
- if (partSource == null) {
- throw new IllegalArgumentException("Source may not be null");
- }
- this.source = partSource;
- }
-
- /**
- * FilePart Constructor.
- *
- * @param name the name for this part
- * @param partSource the source for this part
- */
- public FilePart(String name, PartSource partSource) {
- this(name, partSource, null, null);
- }
-
- /**
- * FilePart Constructor.
- *
- * @param name the name of the file part
- * @param file the file to post
- *
- * @throws FileNotFoundException if the <i>file</i> is not a normal
- * file or if it is not readable.
- */
- public FilePart(String name, File file)
- throws FileNotFoundException {
- this(name, new FilePartSource(file), null, null);
- }
-
- /**
- * FilePart Constructor.
- *
- * @param name the name of the file part
- * @param file the file to post
- * @param contentType the content type for this part, if <code>null</code> the
- * {@link #DEFAULT_CONTENT_TYPE default} is used
- * @param charset the charset encoding for this part, if <code>null</code> the
- * {@link #DEFAULT_CHARSET default} is used
- *
- * @throws FileNotFoundException if the <i>file</i> is not a normal
- * file or if it is not readable.
- */
- public FilePart(String name, File file, String contentType, String charset)
- throws FileNotFoundException {
- this(name, new FilePartSource(file), contentType, charset);
- }
-
- /**
- * FilePart Constructor.
- *
- * @param name the name of the file part
- * @param fileName the file name
- * @param file the file to post
- *
- * @throws FileNotFoundException if the <i>file</i> is not a normal
- * file or if it is not readable.
- */
- public FilePart(String name, String fileName, File file)
- throws FileNotFoundException {
- this(name, new FilePartSource(fileName, file), null, null);
- }
-
- /**
- * FilePart Constructor.
- *
- * @param name the name of the file part
- * @param fileName the file name
- * @param file the file to post
- * @param contentType the content type for this part, if <code>null</code> the
- * {@link #DEFAULT_CONTENT_TYPE default} is used
- * @param charset the charset encoding for this part, if <code>null</code> the
- * {@link #DEFAULT_CHARSET default} is used
- *
- * @throws FileNotFoundException if the <i>file</i> is not a normal
- * file or if it is not readable.
- */
- public FilePart(String name, String fileName, File file, String contentType, String charset)
- throws FileNotFoundException {
- this(name, new FilePartSource(fileName, file), contentType, charset);
- }
-
- /**
- * Write the disposition header to the output stream
- * @param out The output stream
- * @throws IOException If an IO problem occurs
- * @see Part#sendDispositionHeader(OutputStream)
- */
- @Override
- protected void sendDispositionHeader(OutputStream out)
- throws IOException {
- LOG.trace("enter sendDispositionHeader(OutputStream out)");
- super.sendDispositionHeader(out);
- String filename = this.source.getFileName();
- if (filename != null) {
- out.write(FILE_NAME_BYTES);
- out.write(QUOTE_BYTES);
- out.write(EncodingUtils.getAsciiBytes(filename));
- out.write(QUOTE_BYTES);
- }
- }
-
- /**
- * Write the data in "source" to the specified stream.
- * @param out The output stream.
- * @throws IOException if an IO problem occurs.
- * @see Part#sendData(OutputStream)
- */
- @Override
- protected void sendData(OutputStream out) throws IOException {
- LOG.trace("enter sendData(OutputStream out)");
- if (lengthOfData() == 0) {
-
- // this file contains no data, so there is nothing to send.
- // we don't want to create a zero length buffer as this will
- // cause an infinite loop when reading.
- LOG.debug("No data to send.");
- return;
- }
-
- byte[] tmp = new byte[4096];
- InputStream instream = source.createInputStream();
- try {
- int len;
- while ((len = instream.read(tmp)) >= 0) {
- out.write(tmp, 0, len);
- }
- } finally {
- // we're done with the stream, close it
- instream.close();
- }
- }
-
- /**
- * Returns the source of the file part.
- *
- * @return The source.
- */
- protected PartSource getSource() {
- LOG.trace("enter getSource()");
- return this.source;
- }
-
- /**
- * Return the length of the data.
- * @return The length.
- * @see Part#lengthOfData()
- */
- @Override
- protected long lengthOfData() {
- LOG.trace("enter lengthOfData()");
- return source.getLength();
- }
-
-}
diff --git a/core/java/com/android/internal/http/multipart/FilePartSource.java b/core/java/com/android/internal/http/multipart/FilePartSource.java
deleted file mode 100644
index eb5cc0f..0000000
--- a/core/java/com/android/internal/http/multipart/FilePartSource.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePartSource.java,v 1.10 2004/04/18 23:51:37 jsdever Exp $
- * $Revision: 480424 $
- * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * A PartSource that reads from a File.
- *
- * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
- * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
- * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
- *
- * @since 2.0
- */
-public class FilePartSource implements PartSource {
-
- /** File part file. */
- private File file = null;
-
- /** File part file name. */
- private String fileName = null;
-
- /**
- * Constructor for FilePartSource.
- *
- * @param file the FilePart source File.
- *
- * @throws FileNotFoundException if the file does not exist or
- * cannot be read
- */
- public FilePartSource(File file) throws FileNotFoundException {
- this.file = file;
- if (file != null) {
- if (!file.isFile()) {
- throw new FileNotFoundException("File is not a normal file.");
- }
- if (!file.canRead()) {
- throw new FileNotFoundException("File is not readable.");
- }
- this.fileName = file.getName();
- }
- }
-
- /**
- * Constructor for FilePartSource.
- *
- * @param fileName the file name of the FilePart
- * @param file the source File for the FilePart
- *
- * @throws FileNotFoundException if the file does not exist or
- * cannot be read
- */
- public FilePartSource(String fileName, File file)
- throws FileNotFoundException {
- this(file);
- if (fileName != null) {
- this.fileName = fileName;
- }
- }
-
- /**
- * Return the length of the file
- * @return the length of the file.
- * @see PartSource#getLength()
- */
- public long getLength() {
- if (this.file != null) {
- return this.file.length();
- } else {
- return 0;
- }
- }
-
- /**
- * Return the current filename
- * @return the filename.
- * @see PartSource#getFileName()
- */
- public String getFileName() {
- return (fileName == null) ? "noname" : fileName;
- }
-
- /**
- * Return a new {@link FileInputStream} for the current filename.
- * @return the new input stream.
- * @throws IOException If an IO problem occurs.
- * @see PartSource#createInputStream()
- */
- public InputStream createInputStream() throws IOException {
- if (this.file != null) {
- return new FileInputStream(this.file);
- } else {
- return new ByteArrayInputStream(new byte[] {});
- }
- }
-
-}
diff --git a/core/java/com/android/internal/http/multipart/MultipartEntity.java b/core/java/com/android/internal/http/multipart/MultipartEntity.java
deleted file mode 100644
index 5319251..0000000
--- a/core/java/com/android/internal/http/multipart/MultipartEntity.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/MultipartRequestEntity.java,v 1.1 2004/10/06 03:39:59 mbecke Exp $
- * $Revision: 502647 $
- * $Date: 2007-02-02 17:22:54 +0100 (Fri, 02 Feb 2007) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Random;
-
-import org.apache.http.Header;
-import org.apache.http.entity.AbstractHttpEntity;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.HTTP;
-import org.apache.http.util.EncodingUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Implements a request entity suitable for an HTTP multipart POST method.
- * <p>
- * The HTTP multipart POST method is defined in section 3.3 of
- * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>:
- * <blockquote>
- * The media-type multipart/form-data follows the rules of all multipart
- * MIME data streams as outlined in RFC 1521. The multipart/form-data contains
- * a series of parts. Each part is expected to contain a content-disposition
- * header where the value is "form-data" and a name attribute specifies
- * the field name within the form, e.g., 'content-disposition: form-data;
- * name="xxxxx"', where xxxxx is the field name corresponding to that field.
- * Field names originally in non-ASCII character sets may be encoded using
- * the method outlined in RFC 1522.
- * </blockquote>
- * </p>
- * <p>This entity is designed to be used in conjunction with the
- * {@link org.apache.http.HttpRequest} to provide
- * multipart posts. Example usage:</p>
- * <pre>
- * File f = new File("/path/fileToUpload.txt");
- * HttpRequest request = new HttpRequest("http://host/some_path");
- * Part[] parts = {
- * new StringPart("param_name", "value"),
- * new FilePart(f.getName(), f)
- * };
- * filePost.setEntity(
- * new MultipartRequestEntity(parts, filePost.getParams())
- * );
- * HttpClient client = new HttpClient();
- * int status = client.executeMethod(filePost);
- * </pre>
- *
- * @since 3.0
- *
- * @deprecated Please use {@link java.net.URLConnection} and friends instead.
- * The Apache HTTP client is no longer maintained and may be removed in a future
- * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- * for further details.
- */
-@Deprecated
-public class MultipartEntity extends AbstractHttpEntity {
-
- private static final Log log = LogFactory.getLog(MultipartEntity.class);
-
- /** The Content-Type for multipart/form-data. */
- private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data";
-
- /**
- * Sets the value to use as the multipart boundary.
- * <p>
- * This parameter expects a value if type {@link String}.
- * </p>
- */
- public static final String MULTIPART_BOUNDARY = "http.method.multipart.boundary";
-
- /**
- * The pool of ASCII chars to be used for generating a multipart boundary.
- */
- private static byte[] MULTIPART_CHARS = EncodingUtils.getAsciiBytes(
- "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
-
- /**
- * Generates a random multipart boundary string.
- */
- private static byte[] generateMultipartBoundary() {
- Random rand = new Random();
- byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40
- for (int i = 0; i < bytes.length; i++) {
- bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)];
- }
- return bytes;
- }
-
- /** The MIME parts as set by the constructor */
- protected Part[] parts;
-
- private byte[] multipartBoundary;
-
- private HttpParams params;
-
- private boolean contentConsumed = false;
-
- /**
- * Creates a new multipart entity containing the given parts.
- * @param parts The parts to include.
- * @param params The params of the HttpMethod using this entity.
- */
- public MultipartEntity(Part[] parts, HttpParams params) {
- if (parts == null) {
- throw new IllegalArgumentException("parts cannot be null");
- }
- if (params == null) {
- throw new IllegalArgumentException("params cannot be null");
- }
- this.parts = parts;
- this.params = params;
- }
-
- public MultipartEntity(Part[] parts) {
- setContentType(MULTIPART_FORM_CONTENT_TYPE);
- if (parts == null) {
- throw new IllegalArgumentException("parts cannot be null");
- }
- this.parts = parts;
- this.params = null;
- }
-
- /**
- * Returns the MIME boundary string that is used to demarcate boundaries of
- * this part. The first call to this method will implicitly create a new
- * boundary string. To create a boundary string first the
- * HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise
- * a random one is generated.
- *
- * @return The boundary string of this entity in ASCII encoding.
- */
- protected byte[] getMultipartBoundary() {
- if (multipartBoundary == null) {
- String temp = null;
- if (params != null) {
- temp = (String) params.getParameter(MULTIPART_BOUNDARY);
- }
- if (temp != null) {
- multipartBoundary = EncodingUtils.getAsciiBytes(temp);
- } else {
- multipartBoundary = generateMultipartBoundary();
- }
- }
- return multipartBoundary;
- }
-
- /**
- * Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise.
- */
- public boolean isRepeatable() {
- for (int i = 0; i < parts.length; i++) {
- if (!parts[i].isRepeatable()) {
- return false;
- }
- }
- return true;
- }
-
- /* (non-Javadoc)
- */
- public void writeTo(OutputStream out) throws IOException {
- Part.sendParts(out, parts, getMultipartBoundary());
- }
- /* (non-Javadoc)
- * @see org.apache.commons.http.AbstractHttpEntity.#getContentType()
- */
- @Override
- public Header getContentType() {
- StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE);
- buffer.append("; boundary=");
- buffer.append(EncodingUtils.getAsciiString(getMultipartBoundary()));
- return new BasicHeader(HTTP.CONTENT_TYPE, buffer.toString());
-
- }
-
- /* (non-Javadoc)
- */
- public long getContentLength() {
- try {
- return Part.getLengthOfParts(parts, getMultipartBoundary());
- } catch (Exception e) {
- log.error("An exception occurred while getting the length of the parts", e);
- return 0;
- }
- }
-
- public InputStream getContent() throws IOException, IllegalStateException {
- if(!isRepeatable() && this.contentConsumed ) {
- throw new IllegalStateException("Content has been consumed");
- }
- this.contentConsumed = true;
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- Part.sendParts(baos, this.parts, this.multipartBoundary);
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- return bais;
- }
-
- public boolean isStreaming() {
- return false;
- }
-}
diff --git a/core/java/com/android/internal/http/multipart/Part.java b/core/java/com/android/internal/http/multipart/Part.java
deleted file mode 100644
index 1d66dc6..0000000
--- a/core/java/com/android/internal/http/multipart/Part.java
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.16 2005/01/14 21:16:40 olegk Exp $
- * $Revision: 480424 $
- * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apache.http.util.EncodingUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Abstract class for one Part of a multipart post object.
- *
- * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
- * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
- * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
- * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
- * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
- *
- * @since 2.0
- *
- * @deprecated Please use {@link java.net.URLConnection} and friends instead.
- * The Apache HTTP client is no longer maintained and may be removed in a future
- * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- * for further details.
- */
-@Deprecated
-public abstract class Part {
-
- /** Log object for this class. */
- private static final Log LOG = LogFactory.getLog(Part.class);
-
- /**
- * The boundary
- * @deprecated use {@link org.apache.http.client.methods.multipart#MULTIPART_BOUNDARY}
- */
- protected static final String BOUNDARY = "----------------314159265358979323846";
-
- /**
- * The boundary as a byte array.
- * @deprecated
- */
- protected static final byte[] BOUNDARY_BYTES = EncodingUtils.getAsciiBytes(BOUNDARY);
-
- /**
- * The default boundary to be used if {@link #setPartBoundary(byte[])} has not
- * been called.
- */
- private static final byte[] DEFAULT_BOUNDARY_BYTES = BOUNDARY_BYTES;
-
- /** Carriage return/linefeed */
- protected static final String CRLF = "\r\n";
-
- /** Carriage return/linefeed as a byte array */
- protected static final byte[] CRLF_BYTES = EncodingUtils.getAsciiBytes(CRLF);
-
- /** Content dispostion characters */
- protected static final String QUOTE = "\"";
-
- /** Content dispostion as a byte array */
- protected static final byte[] QUOTE_BYTES =
- EncodingUtils.getAsciiBytes(QUOTE);
-
- /** Extra characters */
- protected static final String EXTRA = "--";
-
- /** Extra characters as a byte array */
- protected static final byte[] EXTRA_BYTES =
- EncodingUtils.getAsciiBytes(EXTRA);
-
- /** Content dispostion characters */
- protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name=";
-
- /** Content dispostion as a byte array */
- protected static final byte[] CONTENT_DISPOSITION_BYTES =
- EncodingUtils.getAsciiBytes(CONTENT_DISPOSITION);
-
- /** Content type header */
- protected static final String CONTENT_TYPE = "Content-Type: ";
-
- /** Content type header as a byte array */
- protected static final byte[] CONTENT_TYPE_BYTES =
- EncodingUtils.getAsciiBytes(CONTENT_TYPE);
-
- /** Content charset */
- protected static final String CHARSET = "; charset=";
-
- /** Content charset as a byte array */
- protected static final byte[] CHARSET_BYTES =
- EncodingUtils.getAsciiBytes(CHARSET);
-
- /** Content type header */
- protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: ";
-
- /** Content type header as a byte array */
- protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES =
- EncodingUtils.getAsciiBytes(CONTENT_TRANSFER_ENCODING);
-
- /**
- * Return the boundary string.
- * @return the boundary string
- * @deprecated uses a constant string. Rather use {@link #getPartBoundary}
- */
- public static String getBoundary() {
- return BOUNDARY;
- }
-
- /**
- * The ASCII bytes to use as the multipart boundary.
- */
- private byte[] boundaryBytes;
-
- /**
- * Return the name of this part.
- * @return The name.
- */
- public abstract String getName();
-
- /**
- * Returns the content type of this part.
- * @return the content type, or <code>null</code> to exclude the content type header
- */
- public abstract String getContentType();
-
- /**
- * Return the character encoding of this part.
- * @return the character encoding, or <code>null</code> to exclude the character
- * encoding header
- */
- public abstract String getCharSet();
-
- /**
- * Return the transfer encoding of this part.
- * @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header
- */
- public abstract String getTransferEncoding();
-
- /**
- * Gets the part boundary to be used.
- * @return the part boundary as an array of bytes.
- *
- * @since 3.0
- */
- protected byte[] getPartBoundary() {
- if (boundaryBytes == null) {
- // custom boundary bytes have not been set, use the default.
- return DEFAULT_BOUNDARY_BYTES;
- } else {
- return boundaryBytes;
- }
- }
-
- /**
- * Sets the part boundary. Only meant to be used by
- * {@link Part#sendParts(OutputStream, Part[], byte[])}
- * and {@link Part#getLengthOfParts(Part[], byte[])}
- * @param boundaryBytes An array of ASCII bytes.
- * @since 3.0
- */
- void setPartBoundary(byte[] boundaryBytes) {
- this.boundaryBytes = boundaryBytes;
- }
-
- /**
- * Tests if this part can be sent more than once.
- * @return <code>true</code> if {@link #sendData(OutputStream)} can be successfully called
- * more than once.
- * @since 3.0
- */
- public boolean isRepeatable() {
- return true;
- }
-
- /**
- * Write the start to the specified output stream
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- protected void sendStart(OutputStream out) throws IOException {
- LOG.trace("enter sendStart(OutputStream out)");
- out.write(EXTRA_BYTES);
- out.write(getPartBoundary());
- out.write(CRLF_BYTES);
- }
-
- /**
- * Write the content disposition header to the specified output stream
- *
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- protected void sendDispositionHeader(OutputStream out) throws IOException {
- LOG.trace("enter sendDispositionHeader(OutputStream out)");
- out.write(CONTENT_DISPOSITION_BYTES);
- out.write(QUOTE_BYTES);
- out.write(EncodingUtils.getAsciiBytes(getName()));
- out.write(QUOTE_BYTES);
- }
-
- /**
- * Write the content type header to the specified output stream
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- protected void sendContentTypeHeader(OutputStream out) throws IOException {
- LOG.trace("enter sendContentTypeHeader(OutputStream out)");
- String contentType = getContentType();
- if (contentType != null) {
- out.write(CRLF_BYTES);
- out.write(CONTENT_TYPE_BYTES);
- out.write(EncodingUtils.getAsciiBytes(contentType));
- String charSet = getCharSet();
- if (charSet != null) {
- out.write(CHARSET_BYTES);
- out.write(EncodingUtils.getAsciiBytes(charSet));
- }
- }
- }
-
- /**
- * Write the content transfer encoding header to the specified
- * output stream
- *
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- protected void sendTransferEncodingHeader(OutputStream out) throws IOException {
- LOG.trace("enter sendTransferEncodingHeader(OutputStream out)");
- String transferEncoding = getTransferEncoding();
- if (transferEncoding != null) {
- out.write(CRLF_BYTES);
- out.write(CONTENT_TRANSFER_ENCODING_BYTES);
- out.write(EncodingUtils.getAsciiBytes(transferEncoding));
- }
- }
-
- /**
- * Write the end of the header to the output stream
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- protected void sendEndOfHeader(OutputStream out) throws IOException {
- LOG.trace("enter sendEndOfHeader(OutputStream out)");
- out.write(CRLF_BYTES);
- out.write(CRLF_BYTES);
- }
-
- /**
- * Write the data to the specified output stream
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- protected abstract void sendData(OutputStream out) throws IOException;
-
- /**
- * Return the length of the main content
- *
- * @return long The length.
- * @throws IOException If an IO problem occurs
- */
- protected abstract long lengthOfData() throws IOException;
-
- /**
- * Write the end data to the output stream.
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- protected void sendEnd(OutputStream out) throws IOException {
- LOG.trace("enter sendEnd(OutputStream out)");
- out.write(CRLF_BYTES);
- }
-
- /**
- * Write all the data to the output stream.
- * If you override this method make sure to override
- * #length() as well
- *
- * @param out The output stream
- * @throws IOException If an IO problem occurs.
- */
- public void send(OutputStream out) throws IOException {
- LOG.trace("enter send(OutputStream out)");
- sendStart(out);
- sendDispositionHeader(out);
- sendContentTypeHeader(out);
- sendTransferEncodingHeader(out);
- sendEndOfHeader(out);
- sendData(out);
- sendEnd(out);
- }
-
-
- /**
- * Return the full length of all the data.
- * If you override this method make sure to override
- * #send(OutputStream) as well
- *
- * @return long The length.
- * @throws IOException If an IO problem occurs
- */
- public long length() throws IOException {
- LOG.trace("enter length()");
- if (lengthOfData() < 0) {
- return -1;
- }
- ByteArrayOutputStream overhead = new ByteArrayOutputStream();
- sendStart(overhead);
- sendDispositionHeader(overhead);
- sendContentTypeHeader(overhead);
- sendTransferEncodingHeader(overhead);
- sendEndOfHeader(overhead);
- sendEnd(overhead);
- return overhead.size() + lengthOfData();
- }
-
- /**
- * Return a string representation of this object.
- * @return A string representation of this object.
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return this.getName();
- }
-
- /**
- * Write all parts and the last boundary to the specified output stream.
- *
- * @param out The stream to write to.
- * @param parts The parts to write.
- *
- * @throws IOException If an I/O error occurs while writing the parts.
- */
- public static void sendParts(OutputStream out, final Part[] parts)
- throws IOException {
- sendParts(out, parts, DEFAULT_BOUNDARY_BYTES);
- }
-
- /**
- * Write all parts and the last boundary to the specified output stream.
- *
- * @param out The stream to write to.
- * @param parts The parts to write.
- * @param partBoundary The ASCII bytes to use as the part boundary.
- *
- * @throws IOException If an I/O error occurs while writing the parts.
- *
- * @since 3.0
- */
- public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary)
- throws IOException {
-
- if (parts == null) {
- throw new IllegalArgumentException("Parts may not be null");
- }
- if (partBoundary == null || partBoundary.length == 0) {
- throw new IllegalArgumentException("partBoundary may not be empty");
- }
- for (int i = 0; i < parts.length; i++) {
- // set the part boundary before the part is sent
- parts[i].setPartBoundary(partBoundary);
- parts[i].send(out);
- }
- out.write(EXTRA_BYTES);
- out.write(partBoundary);
- out.write(EXTRA_BYTES);
- out.write(CRLF_BYTES);
- }
-
- /**
- * Return the total sum of all parts and that of the last boundary
- *
- * @param parts The parts.
- * @return The total length
- *
- * @throws IOException If an I/O error occurs while writing the parts.
- */
- public static long getLengthOfParts(Part[] parts)
- throws IOException {
- return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES);
- }
-
- /**
- * Gets the length of the multipart message including the given parts.
- *
- * @param parts The parts.
- * @param partBoundary The ASCII bytes to use as the part boundary.
- * @return The total length
- *
- * @throws IOException If an I/O error occurs while writing the parts.
- *
- * @since 3.0
- */
- public static long getLengthOfParts(Part[] parts, byte[] partBoundary) throws IOException {
- LOG.trace("getLengthOfParts(Parts[])");
- if (parts == null) {
- throw new IllegalArgumentException("Parts may not be null");
- }
- long total = 0;
- for (int i = 0; i < parts.length; i++) {
- // set the part boundary before we calculate the part's length
- parts[i].setPartBoundary(partBoundary);
- long l = parts[i].length();
- if (l < 0) {
- return -1;
- }
- total += l;
- }
- total += EXTRA_BYTES.length;
- total += partBoundary.length;
- total += EXTRA_BYTES.length;
- total += CRLF_BYTES.length;
- return total;
- }
-}
diff --git a/core/java/com/android/internal/http/multipart/PartBase.java b/core/java/com/android/internal/http/multipart/PartBase.java
deleted file mode 100644
index 876d15d..0000000
--- a/core/java/com/android/internal/http/multipart/PartBase.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartBase.java,v 1.5 2004/04/18 23:51:37 jsdever Exp $
- * $Revision: 480424 $
- * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-
-/**
- * Provides setters and getters for the basic Part properties.
- *
- * @author Michael Becke
- */
-public abstract class PartBase extends Part {
-
- /** Name of the file part. */
- private String name;
-
- /** Content type of the file part. */
- private String contentType;
-
- /** Content encoding of the file part. */
- private String charSet;
-
- /** The transfer encoding. */
- private String transferEncoding;
-
- /**
- * Constructor.
- *
- * @param name The name of the part
- * @param contentType The content type, or <code>null</code>
- * @param charSet The character encoding, or <code>null</code>
- * @param transferEncoding The transfer encoding, or <code>null</code>
- */
- public PartBase(String name, String contentType, String charSet, String transferEncoding) {
-
- if (name == null) {
- throw new IllegalArgumentException("Name must not be null");
- }
- this.name = name;
- this.contentType = contentType;
- this.charSet = charSet;
- this.transferEncoding = transferEncoding;
- }
-
- /**
- * Returns the name.
- * @return The name.
- * @see Part#getName()
- */
- @Override
- public String getName() {
- return this.name;
- }
-
- /**
- * Returns the content type of this part.
- * @return String The name.
- */
- @Override
- public String getContentType() {
- return this.contentType;
- }
-
- /**
- * Return the character encoding of this part.
- * @return String The name.
- */
- @Override
- public String getCharSet() {
- return this.charSet;
- }
-
- /**
- * Returns the transfer encoding of this part.
- * @return String The name.
- */
- @Override
- public String getTransferEncoding() {
- return transferEncoding;
- }
-
- /**
- * Sets the character encoding.
- *
- * @param charSet the character encoding, or <code>null</code> to exclude the character
- * encoding header
- */
- public void setCharSet(String charSet) {
- this.charSet = charSet;
- }
-
- /**
- * Sets the content type.
- *
- * @param contentType the content type, or <code>null</code> to exclude the content type header
- */
- public void setContentType(String contentType) {
- this.contentType = contentType;
- }
-
- /**
- * Sets the part name.
- *
- * @param name
- */
- public void setName(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Name must not be null");
- }
- this.name = name;
- }
-
- /**
- * Sets the transfer encoding.
- *
- * @param transferEncoding the transfer encoding, or <code>null</code> to exclude the
- * transfer encoding header
- */
- public void setTransferEncoding(String transferEncoding) {
- this.transferEncoding = transferEncoding;
- }
-
-}
diff --git a/core/java/com/android/internal/http/multipart/PartSource.java b/core/java/com/android/internal/http/multipart/PartSource.java
deleted file mode 100644
index 3740696..0000000
--- a/core/java/com/android/internal/http/multipart/PartSource.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartSource.java,v 1.6 2004/04/18 23:51:37 jsdever Exp $
- * $Revision: 480424 $
- * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * An interface for providing access to data when posting MultiPart messages.
- *
- * @see FilePart
- *
- * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
- *
- * @since 2.0
- */
-public interface PartSource {
-
- /**
- * Gets the number of bytes contained in this source.
- *
- * @return a value >= 0
- */
- long getLength();
-
- /**
- * Gets the name of the file this source represents.
- *
- * @return the fileName used for posting a MultiPart file part
- */
- String getFileName();
-
- /**
- * Gets a new InputStream for reading this source. This method can be
- * called more than once and should therefore return a new stream every
- * time.
- *
- * @return a new InputStream
- *
- * @throws IOException if an error occurs when creating the InputStream
- */
- InputStream createInputStream() throws IOException;
-
-}
diff --git a/core/java/com/android/internal/http/multipart/StringPart.java b/core/java/com/android/internal/http/multipart/StringPart.java
deleted file mode 100644
index 73d0f90..0000000
--- a/core/java/com/android/internal/http/multipart/StringPart.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java,v 1.11 2004/04/18 23:51:37 jsdever Exp $
- * $Revision: 480424 $
- * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
- *
- * ====================================================================
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package com.android.internal.http.multipart;
-
-import java.io.OutputStream;
-import java.io.IOException;
-
-import org.apache.http.util.EncodingUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Simple string parameter for a multipart post
- *
- * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
- * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
- * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
- * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
- *
- * @since 2.0
- *
- * @deprecated Please use {@link java.net.URLConnection} and friends instead.
- * The Apache HTTP client is no longer maintained and may be removed in a future
- * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- * for further details.
- */
-@Deprecated
-public class StringPart extends PartBase {
-
- /** Log object for this class. */
- private static final Log LOG = LogFactory.getLog(StringPart.class);
-
- /** Default content encoding of string parameters. */
- public static final String DEFAULT_CONTENT_TYPE = "text/plain";
-
- /** Default charset of string parameters*/
- public static final String DEFAULT_CHARSET = "US-ASCII";
-
- /** Default transfer encoding of string parameters*/
- public static final String DEFAULT_TRANSFER_ENCODING = "8bit";
-
- /** Contents of this StringPart. */
- private byte[] content;
-
- /** The String value of this part. */
- private String value;
-
- /**
- * Constructor.
- *
- * @param name The name of the part
- * @param value the string to post
- * @param charset the charset to be used to encode the string, if <code>null</code>
- * the {@link #DEFAULT_CHARSET default} is used
- */
- public StringPart(String name, String value, String charset) {
-
- super(
- name,
- DEFAULT_CONTENT_TYPE,
- charset == null ? DEFAULT_CHARSET : charset,
- DEFAULT_TRANSFER_ENCODING
- );
- if (value == null) {
- throw new IllegalArgumentException("Value may not be null");
- }
- if (value.indexOf(0) != -1) {
- // See RFC 2048, 2.8. "8bit Data"
- throw new IllegalArgumentException("NULs may not be present in string parts");
- }
- this.value = value;
- }
-
- /**
- * Constructor.
- *
- * @param name The name of the part
- * @param value the string to post
- */
- public StringPart(String name, String value) {
- this(name, value, null);
- }
-
- /**
- * Gets the content in bytes. Bytes are lazily created to allow the charset to be changed
- * after the part is created.
- *
- * @return the content in bytes
- */
- private byte[] getContent() {
- if (content == null) {
- content = EncodingUtils.getBytes(value, getCharSet());
- }
- return content;
- }
-
- /**
- * Writes the data to the given OutputStream.
- * @param out the OutputStream to write to
- * @throws IOException if there is a write error
- */
- @Override
- protected void sendData(OutputStream out) throws IOException {
- LOG.trace("enter sendData(OutputStream)");
- out.write(getContent());
- }
-
- /**
- * Return the length of the data.
- * @return The length of the data.
- * @see Part#lengthOfData()
- */
- @Override
- protected long lengthOfData() {
- LOG.trace("enter lengthOfData()");
- return getContent().length;
- }
-
- /* (non-Javadoc)
- * @see org.apache.commons.httpclient.methods.multipart.BasePart#setCharSet(java.lang.String)
- */
- @Override
- public void setCharSet(String charSet) {
- super.setCharSet(charSet);
- this.content = null;
- }
-
-}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 183527c..9aa36d3 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -35,6 +35,8 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.TextServicesManager;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -409,6 +411,24 @@ public class InputMethodUtils {
return false;
}
+ public static Locale constructLocaleFromString(String localeStr) {
+ if (TextUtils.isEmpty(localeStr)) {
+ return null;
+ }
+ // TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}.
+ String[] localeParams = localeStr.split("_", 3);
+ // The length of localeStr is guaranteed to always return a 1 <= value <= 3
+ // because localeStr is not empty.
+ if (localeParams.length == 1) {
+ return new Locale(localeParams[0]);
+ } else if (localeParams.length == 2) {
+ return new Locale(localeParams[0], localeParams[1]);
+ } else if (localeParams.length == 3) {
+ return new Locale(localeParams[0], localeParams[1], localeParams[2]);
+ }
+ return null;
+ }
+
public static boolean containsSubtypeOf(final InputMethodInfo imi,
@Nullable final Locale locale, final boolean checkCountry, final String mode) {
if (locale == null) {
@@ -418,15 +438,16 @@ public class InputMethodUtils {
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = imi.getSubtypeAt(i);
if (checkCountry) {
- // TODO: Use {@link Locale#toLanguageTag()} and
- // {@link Locale#forLanguageTag(languageTag)} instead.
- if (!TextUtils.equals(subtype.getLocale(), locale.toString())) {
+ final Locale subtypeLocale = constructLocaleFromString(subtype.getLocale());
+ if (subtypeLocale == null ||
+ !TextUtils.equals(subtypeLocale.getLanguage(), locale.getLanguage()) ||
+ !TextUtils.equals(subtypeLocale.getCountry(), locale.getCountry())) {
continue;
}
} else {
final Locale subtypeLocale = new Locale(getLanguageFromLocaleString(
subtype.getLocale()));
- if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) {
+ if (!TextUtils.equals(subtypeLocale.getLanguage(), locale.getLanguage())) {
continue;
}
}
@@ -518,7 +539,8 @@ public class InputMethodUtils {
return NOT_A_SUBTYPE_ID;
}
- private static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
+ @VisibleForTesting
+ public static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
Resources res, InputMethodInfo imi) {
final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi);
final String systemLocale = res.getConfiguration().locale.toString();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 20bb95e..d0c7f8c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -94,7 +94,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 116 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 118 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -208,7 +208,7 @@ public final class BatteryStatsImpl extends BatteryStats {
final HistoryItem mHistoryLastLastWritten = new HistoryItem();
final HistoryItem mHistoryReadTmp = new HistoryItem();
final HistoryItem mHistoryAddTmp = new HistoryItem();
- final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<HistoryTag, Integer>();
+ final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap();
String[] mReadHistoryStrings;
int[] mReadHistoryUids;
int mReadHistoryChars;
@@ -227,6 +227,38 @@ public final class BatteryStatsImpl extends BatteryStats {
HistoryItem mHistoryLastEnd;
HistoryItem mHistoryCache;
+ // Used by computeHistoryStepDetails
+ HistoryStepDetails mLastHistoryStepDetails = null;
+ byte mLastHistoryStepLevel = 0;
+ final HistoryStepDetails mCurHistoryStepDetails = new HistoryStepDetails();
+ final HistoryStepDetails mReadHistoryStepDetails = new HistoryStepDetails();
+ final HistoryStepDetails mTmpHistoryStepDetails = new HistoryStepDetails();
+ /**
+ * Total time (in 1/100 sec) spent executing in user code.
+ */
+ long mLastStepCpuUserTime;
+ long mCurStepCpuUserTime;
+ /**
+ * Total time (in 1/100 sec) spent executing in kernel code.
+ */
+ long mLastStepCpuSystemTime;
+ long mCurStepCpuSystemTime;
+ /**
+ * Times from /proc/stat
+ */
+ long mLastStepStatUserTime;
+ long mLastStepStatSystemTime;
+ long mLastStepStatIOWaitTime;
+ long mLastStepStatIrqTime;
+ long mLastStepStatSoftIrqTime;
+ long mLastStepStatIdleTime;
+ long mCurStepStatUserTime;
+ long mCurStepStatSystemTime;
+ long mCurStepStatIOWaitTime;
+ long mCurStepStatIrqTime;
+ long mCurStepStatSoftIrqTime;
+ long mCurStepStatIdleTime;
+
private HistoryItem mHistoryIterator;
private boolean mReadOverflow;
private boolean mIteratingHistory;
@@ -1938,6 +1970,10 @@ public final class BatteryStatsImpl extends BatteryStats {
static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
static final int STATE_BATTERY_PLUG_SHIFT = 24;
+ // We use the low bit of the battery state int to indicate that we have full details
+ // from a battery level change.
+ static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
+
public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
dest.writeInt(DELTA_TIME_ABS);
@@ -1958,7 +1994,11 @@ public final class BatteryStatsImpl extends BatteryStats {
deltaTimeToken = (int)deltaTime;
}
int firstToken = deltaTimeToken | (cur.states&DELTA_STATE_MASK);
- final int batteryLevelInt = buildBatteryLevelInt(cur);
+ final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
+ ? BATTERY_DELTA_LEVEL_FLAG : 0;
+ final boolean computeStepDetails = includeStepDetails != 0
+ || mLastHistoryStepDetails == null;
+ final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
if (batteryLevelIntChanged) {
firstToken |= DELTA_BATTERY_LEVEL_FLAG;
@@ -2040,12 +2080,26 @@ public final class BatteryStatsImpl extends BatteryStats {
+ cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
+ cur.eventTag.string);
}
+ if (computeStepDetails) {
+ computeHistoryStepDetails(mCurHistoryStepDetails, mLastHistoryStepDetails);
+ if (includeStepDetails != 0) {
+ mCurHistoryStepDetails.writeToParcel(dest);
+ }
+ cur.stepDetails = mCurHistoryStepDetails;
+ mLastHistoryStepDetails = mCurHistoryStepDetails;
+ } else {
+ cur.stepDetails = null;
+ }
+ if (mLastHistoryStepLevel < cur.batteryLevel) {
+ mLastHistoryStepDetails = null;
+ }
+ mLastHistoryStepLevel = cur.batteryLevel;
}
private int buildBatteryLevelInt(HistoryItem h) {
return ((((int)h.batteryLevel)<<25)&0xfe000000)
- | ((((int)h.batteryTemperature)<<14)&0x01ffc000)
- | (((int)h.batteryVoltage)&0x00003fff);
+ | ((((int)h.batteryTemperature)<<14)&0x01ff8000)
+ | ((((int)h.batteryVoltage)<<1)&0x00007fff);
}
private int buildStateInt(HistoryItem h) {
@@ -2063,6 +2117,98 @@ public final class BatteryStatsImpl extends BatteryStats {
| (h.states&(~DELTA_STATE_MASK));
}
+ private void computeHistoryStepDetails(final HistoryStepDetails out,
+ final HistoryStepDetails last) {
+ final HistoryStepDetails tmp = last != null ? mTmpHistoryStepDetails : out;
+
+ // Perform a CPU update right after we do this collection, so we have started
+ // collecting good data for the next step.
+ requestImmediateCpuUpdate();
+
+ if (last == null) {
+ // We are not generating a delta, so all we need to do is reset the stats
+ // we will later be doing a delta from.
+ final int NU = mUidStats.size();
+ for (int i=0; i<NU; i++) {
+ final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ uid.mLastStepUserTime = uid.mCurStepUserTime;
+ uid.mLastStepSystemTime = uid.mCurStepSystemTime;
+ }
+ mLastStepCpuUserTime = mCurStepCpuUserTime;
+ mLastStepCpuSystemTime = mCurStepCpuSystemTime;
+ mLastStepStatUserTime = mCurStepStatUserTime;
+ mLastStepStatSystemTime = mCurStepStatSystemTime;
+ mLastStepStatIOWaitTime = mCurStepStatIOWaitTime;
+ mLastStepStatIrqTime = mCurStepStatIrqTime;
+ mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime;
+ mLastStepStatIdleTime = mCurStepStatIdleTime;
+ tmp.clear();
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTime + " sys="
+ + mLastStepStatSystemTime + " io=" + mLastStepStatIOWaitTime
+ + " irq=" + mLastStepStatIrqTime + " sirq="
+ + mLastStepStatSoftIrqTime + " idle=" + mLastStepStatIdleTime);
+ Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTime + " sys="
+ + mCurStepStatSystemTime + " io=" + mCurStepStatIOWaitTime
+ + " irq=" + mCurStepStatIrqTime + " sirq="
+ + mCurStepStatSoftIrqTime + " idle=" + mCurStepStatIdleTime);
+ }
+ out.userTime = (int)(mCurStepCpuUserTime - mLastStepCpuUserTime);
+ out.systemTime = (int)(mCurStepCpuSystemTime - mLastStepCpuSystemTime);
+ out.statUserTime = (int)(mCurStepStatUserTime - mLastStepStatUserTime);
+ out.statSystemTime = (int)(mCurStepStatSystemTime - mLastStepStatSystemTime);
+ out.statIOWaitTime = (int)(mCurStepStatIOWaitTime - mLastStepStatIOWaitTime);
+ out.statIrqTime = (int)(mCurStepStatIrqTime - mLastStepStatIrqTime);
+ out.statSoftIrqTime = (int)(mCurStepStatSoftIrqTime - mLastStepStatSoftIrqTime);
+ out.statIdlTime = (int)(mCurStepStatIdleTime - mLastStepStatIdleTime);
+ out.appCpuUid1 = out.appCpuUid2 = out.appCpuUid3 = -1;
+ out.appCpuUTime1 = out.appCpuUTime2 = out.appCpuUTime3 = 0;
+ out.appCpuSTime1 = out.appCpuSTime2 = out.appCpuSTime3 = 0;
+ final int NU = mUidStats.size();
+ for (int i=0; i<NU; i++) {
+ final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ final int totalUTime = (int)(uid.mCurStepUserTime - uid.mLastStepUserTime);
+ final int totalSTime = (int)(uid.mCurStepSystemTime - uid.mLastStepSystemTime);
+ final int totalTime = totalUTime + totalSTime;
+ uid.mLastStepUserTime = uid.mCurStepUserTime;
+ uid.mLastStepSystemTime = uid.mCurStepSystemTime;
+ if (totalTime <= (out.appCpuUTime3+out.appCpuSTime3)) {
+ continue;
+ }
+ if (totalTime <= (out.appCpuUTime2+out.appCpuSTime2)) {
+ out.appCpuUid3 = uid.mUid;
+ out.appCpuUTime3 = totalUTime;
+ out.appCpuSTime3 = totalSTime;
+ } else {
+ out.appCpuUid3 = out.appCpuUid2;
+ out.appCpuUTime3 = out.appCpuUTime2;
+ out.appCpuSTime3 = out.appCpuSTime2;
+ if (totalTime <= (out.appCpuUTime1+out.appCpuSTime1)) {
+ out.appCpuUid2 = uid.mUid;
+ out.appCpuUTime2 = totalUTime;
+ out.appCpuSTime2 = totalSTime;
+ } else {
+ out.appCpuUid2 = out.appCpuUid1;
+ out.appCpuUTime2 = out.appCpuUTime1;
+ out.appCpuSTime2 = out.appCpuSTime1;
+ out.appCpuUid1 = uid.mUid;
+ out.appCpuUTime1 = totalUTime;
+ out.appCpuSTime1 = totalSTime;
+ }
+ }
+ }
+ mLastStepCpuUserTime = mCurStepCpuUserTime;
+ mLastStepCpuSystemTime = mCurStepCpuSystemTime;
+ mLastStepStatUserTime = mCurStepStatUserTime;
+ mLastStepStatSystemTime = mCurStepStatSystemTime;
+ mLastStepStatIOWaitTime = mCurStepStatIOWaitTime;
+ mLastStepStatIrqTime = mCurStepStatIrqTime;
+ mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime;
+ mLastStepStatIdleTime = mCurStepStatIdleTime;
+ }
+
public void readHistoryDelta(Parcel src, HistoryItem cur) {
int firstToken = src.readInt();
int deltaTimeToken = firstToken&DELTA_TIME_MASK;
@@ -2091,8 +2237,9 @@ public final class BatteryStatsImpl extends BatteryStats {
cur.numReadInts += 2;
}
+ final int batteryLevelInt;
if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
- int batteryLevelInt = src.readInt();
+ batteryLevelInt = src.readInt();
cur.batteryLevel = (byte)((batteryLevelInt>>25)&0x7f);
cur.batteryTemperature = (short)((batteryLevelInt<<7)>>21);
cur.batteryVoltage = (char)(batteryLevelInt&0x3fff);
@@ -2102,6 +2249,8 @@ public final class BatteryStatsImpl extends BatteryStats {
+ " batteryLevel=" + cur.batteryLevel
+ " batteryTemp=" + cur.batteryTemperature
+ " batteryVolt=" + (int)cur.batteryVoltage);
+ } else {
+ batteryLevelInt = 0;
}
if ((firstToken&DELTA_STATE_FLAG) != 0) {
@@ -2180,6 +2329,13 @@ public final class BatteryStatsImpl extends BatteryStats {
} else {
cur.eventCode = HistoryItem.EVENT_NONE;
}
+
+ if ((batteryLevelInt&BATTERY_DELTA_LEVEL_FLAG) != 0) {
+ cur.stepDetails = mReadHistoryStepDetails;
+ cur.stepDetails.readFromParcel(src);
+ } else {
+ cur.stepDetails = null;
+ }
}
@Override
@@ -2207,6 +2363,7 @@ public final class BatteryStatsImpl extends BatteryStats {
&& (diffStates2&lastDiffStates2) == 0
&& (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
&& (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
+ && mHistoryLastWritten.stepDetails == null
&& (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
|| cur.eventCode == HistoryItem.EVENT_NONE)
&& mHistoryLastWritten.batteryLevel == cur.batteryLevel
@@ -2632,6 +2789,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ private void requestImmediateCpuUpdate() {
+ mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
+ mHandler.sendEmptyMessage(MSG_UPDATE_WAKELOCKS);
+ }
+
public void setRecordAllHistoryLocked(boolean enabled) {
mRecordAllHistory = enabled;
if (!enabled) {
@@ -2823,6 +2985,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public int startAddingCpuLocked() {
mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
+ if (!mOnBatteryInternal) {
+ return -1;
+ }
+
final int N = mPartialTimers.size();
if (N == 0) {
mLastPartialTimers.clear();
@@ -2853,7 +3019,23 @@ public final class BatteryStatsImpl extends BatteryStats {
return 0;
}
- public void finishAddingCpuLocked(int perc, int utime, int stime, long[] cpuSpeedTimes) {
+ public void finishAddingCpuLocked(int perc, int remainUTime, int remainSTtime,
+ int totalUTime, int totalSTime, int statUserTime, int statSystemTime,
+ int statIOWaitTime, int statIrqTime, int statSoftIrqTime, int statIdleTime,
+ long[] cpuSpeedTimes) {
+ if (DEBUG) Slog.d(TAG, "Adding cpu: tuser=" + totalUTime + " tsys=" + totalSTime
+ + " user=" + statUserTime + " sys=" + statSystemTime
+ + " io=" + statIOWaitTime + " irq=" + statIrqTime
+ + " sirq=" + statSoftIrqTime + " idle=" + statIdleTime);
+ mCurStepCpuUserTime += totalUTime;
+ mCurStepCpuSystemTime += totalSTime;
+ mCurStepStatUserTime += statUserTime;
+ mCurStepStatSystemTime += statSystemTime;
+ mCurStepStatIOWaitTime += statIOWaitTime;
+ mCurStepStatIrqTime += statIrqTime;
+ mCurStepStatSoftIrqTime += statSoftIrqTime;
+ mCurStepStatIdleTime += statIdleTime;
+
final int N = mPartialTimers.size();
if (perc != 0) {
int num = 0;
@@ -2874,26 +3056,24 @@ public final class BatteryStatsImpl extends BatteryStats {
if (st.mInList) {
Uid uid = st.mUid;
if (uid != null && uid.mUid != Process.SYSTEM_UID) {
- int myUTime = utime/num;
- int mySTime = stime/num;
- utime -= myUTime;
- stime -= mySTime;
+ int myUTime = remainUTime/num;
+ int mySTime = remainSTtime/num;
+ remainUTime -= myUTime;
+ remainSTtime -= mySTime;
num--;
Uid.Proc proc = uid.getProcessStatsLocked("*wakelock*");
- proc.addCpuTimeLocked(myUTime, mySTime);
- proc.addSpeedStepTimes(cpuSpeedTimes);
+ proc.addCpuTimeLocked(myUTime, mySTime, cpuSpeedTimes);
}
}
}
}
// Just in case, collect any lost CPU time.
- if (utime != 0 || stime != 0) {
+ if (remainUTime != 0 || remainSTtime != 0) {
Uid uid = getUidStatsLocked(Process.SYSTEM_UID);
if (uid != null) {
Uid.Proc proc = uid.getProcessStatsLocked("*lost*");
- proc.addCpuTimeLocked(utime, stime);
- proc.addSpeedStepTimes(cpuSpeedTimes);
+ proc.addCpuTimeLocked(remainUTime, remainSTtime, cpuSpeedTimes);
}
}
}
@@ -4214,6 +4394,14 @@ public final class BatteryStatsImpl extends BatteryStats {
LongSamplingCounter mMobileRadioActiveCount;
/**
+ * The CPU times we had at the last history details update.
+ */
+ long mLastStepUserTime;
+ long mLastStepSystemTime;
+ long mCurStepUserTime;
+ long mCurStepSystemTime;
+
+ /**
* The statistics we have collected for this uid's wake locks.
*/
final OverflowArrayMap<Wakelock> mWakelockStats = new OverflowArrayMap<Wakelock>() {
@@ -4876,6 +5064,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mPackageStats.clear();
}
+ mLastStepUserTime = mLastStepSystemTime = 0;
+ mCurStepUserTime = mCurStepSystemTime = 0;
+
if (!active) {
if (mWifiRunningTimer != null) {
mWifiRunningTimer.detach();
@@ -5678,9 +5869,22 @@ public final class BatteryStatsImpl extends BatteryStats {
return BatteryStatsImpl.this;
}
- public void addCpuTimeLocked(int utime, int stime) {
+ public void addCpuTimeLocked(int utime, int stime, long[] speedStepBins) {
mUserTime += utime;
+ mCurStepUserTime += utime;
mSystemTime += stime;
+ mCurStepSystemTime += stime;
+
+ for (int i = 0; i < mSpeedBins.length && i < speedStepBins.length; i++) {
+ long amt = speedStepBins[i];
+ if (amt != 0) {
+ SamplingCounter c = mSpeedBins[i];
+ if (c == null) {
+ mSpeedBins[i] = c = new SamplingCounter(mOnBatteryTimeBase);
+ }
+ c.addCountAtomic(speedStepBins[i]);
+ }
+ }
}
public void addForegroundTimeLocked(long ttime) {
@@ -5770,20 +5974,6 @@ public final class BatteryStatsImpl extends BatteryStats {
return val;
}
- /* Called by ActivityManagerService when CPU times are updated. */
- public void addSpeedStepTimes(long[] values) {
- for (int i = 0; i < mSpeedBins.length && i < values.length; i++) {
- long amt = values[i];
- if (amt != 0) {
- SamplingCounter c = mSpeedBins[i];
- if (c == null) {
- mSpeedBins[i] = c = new SamplingCounter(mOnBatteryTimeBase);
- }
- c.addCountAtomic(values[i]);
- }
- }
- }
-
@Override
public long getTimeAtCpuSpeedStep(int speedStep, int which) {
if (speedStep < mSpeedBins.length) {
@@ -6756,6 +6946,18 @@ public final class BatteryStatsImpl extends BatteryStats {
mWakeupReasonStats.clear();
}
+ mLastHistoryStepDetails = null;
+ mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
+ mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
+ mLastStepCpuUserTime = mCurStepCpuUserTime = 0;
+ mLastStepCpuSystemTime = mCurStepCpuSystemTime = 0;
+ mLastStepStatUserTime = mCurStepStatUserTime = 0;
+ mLastStepStatSystemTime = mCurStepStatSystemTime = 0;
+ mLastStepStatIOWaitTime = mCurStepStatIOWaitTime = 0;
+ mLastStepStatIrqTime = mCurStepStatIrqTime = 0;
+ mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime = 0;
+ mLastStepStatIdleTime = mCurStepStatIdleTime = 0;
+
initDischarge();
clearHistoryLocked();
@@ -6872,7 +7074,7 @@ public final class BatteryStatsImpl extends BatteryStats {
reset = true;
mNumDischargeStepDurations = 0;
}
- mOnBattery = mOnBatteryInternal = onBattery;
+ mOnBattery = mOnBatteryInternal = true;
mLastDischargeStepLevel = level;
mMinDischargeStepLevel = level;
mLastDischargeStepTime = -1;
@@ -6900,7 +7102,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOff = 0;
updateTimeBasesLocked(true, !screenOn, uptime, realtime);
} else {
- mOnBattery = mOnBatteryInternal = onBattery;
+ mOnBattery = mOnBatteryInternal = false;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index b5338df..501e0ec 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -152,6 +152,7 @@ public class ProcessCpuTracker {
private int mRelIrqTime;
private int mRelSoftIrqTime;
private int mRelIdleTime;
+ private boolean mRelStatsAreGood;
private int[] mCurPids;
private int[] mCurThreadPids;
@@ -285,10 +286,9 @@ public class ProcessCpuTracker {
public void update() {
if (DEBUG) Slog.v(TAG, "Update: " + this);
- mLastSampleTime = mCurrentSampleTime;
- mCurrentSampleTime = SystemClock.uptimeMillis();
- mLastSampleRealTime = mCurrentSampleRealTime;
- mCurrentSampleRealTime = SystemClock.elapsedRealtime();
+
+ final long nowUptime = SystemClock.uptimeMillis();
+ final long nowRealtime = SystemClock.elapsedRealtime();
final long[] sysCpu = mSystemCpuData;
if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
@@ -304,30 +304,53 @@ public class ProcessCpuTracker {
final long irqtime = sysCpu[5];
final long softirqtime = sysCpu[6];
- mRelUserTime = (int)(usertime - mBaseUserTime);
- mRelSystemTime = (int)(systemtime - mBaseSystemTime);
- mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
- mRelIrqTime = (int)(irqtime - mBaseIrqTime);
- mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
- mRelIdleTime = (int)(idletime - mBaseIdleTime);
-
- if (DEBUG) {
- Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
- + " S:" + sysCpu[2] + " I:" + sysCpu[3]
- + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
- + " O:" + sysCpu[6]);
- Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
- + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
- }
+ // This code is trying to avoid issues with idle time going backwards,
+ // but currently it gets into situations where it triggers most of the time. :(
+ if (true || (usertime >= mBaseUserTime && systemtime >= mBaseSystemTime
+ && iowaittime >= mBaseIoWaitTime && irqtime >= mBaseIrqTime
+ && softirqtime >= mBaseSoftIrqTime && idletime >= mBaseIdleTime)) {
+ mRelUserTime = (int)(usertime - mBaseUserTime);
+ mRelSystemTime = (int)(systemtime - mBaseSystemTime);
+ mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
+ mRelIrqTime = (int)(irqtime - mBaseIrqTime);
+ mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
+ mRelIdleTime = (int)(idletime - mBaseIdleTime);
+ mRelStatsAreGood = true;
+
+ if (DEBUG) {
+ Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
+ + " S:" + sysCpu[2] + " I:" + sysCpu[3]
+ + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
+ + " O:" + sysCpu[6]);
+ Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
+ + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
+ }
+
+ mBaseUserTime = usertime;
+ mBaseSystemTime = systemtime;
+ mBaseIoWaitTime = iowaittime;
+ mBaseIrqTime = irqtime;
+ mBaseSoftIrqTime = softirqtime;
+ mBaseIdleTime = idletime;
- mBaseUserTime = usertime;
- mBaseSystemTime = systemtime;
- mBaseIoWaitTime = iowaittime;
- mBaseIrqTime = irqtime;
- mBaseSoftIrqTime = softirqtime;
- mBaseIdleTime = idletime;
+ } else {
+ mRelUserTime = 0;
+ mRelSystemTime = 0;
+ mRelIoWaitTime = 0;
+ mRelIrqTime = 0;
+ mRelSoftIrqTime = 0;
+ mRelIdleTime = 0;
+ mRelStatsAreGood = false;
+ Slog.w(TAG, "/proc/stats has gone backwards; skipping CPU update");
+ return;
+ }
}
+ mLastSampleTime = mCurrentSampleTime;
+ mCurrentSampleTime = nowUptime;
+ mLastSampleRealTime = mCurrentSampleRealTime;
+ mCurrentSampleRealTime = nowRealtime;
+
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
try {
mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats);
@@ -647,6 +670,10 @@ public class ProcessCpuTracker {
return mRelIdleTime;
}
+ final public boolean hasGoodLastStats() {
+ return mRelStatsAreGood;
+ }
+
final public float getTotalCpuPercent() {
int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime;
if (denom <= 0) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 98638ed..400ea37 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -355,7 +355,7 @@ public class ZygoteInit {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
- if (mResources.getColorStateList(id) == null) {
+ if (mResources.getColorStateList(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded color resource #0x"
+ Integer.toHexString(id)
diff --git a/core/java/com/android/internal/policy/IPolicy.java b/core/java/com/android/internal/policy/IPolicy.java
deleted file mode 100644
index d08b3b4..0000000
--- a/core/java/com/android/internal/policy/IPolicy.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2008 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.internal.policy;
-
-import android.content.Context;
-import android.view.FallbackEventHandler;
-import android.view.LayoutInflater;
-import android.view.Window;
-import android.view.WindowManagerPolicy;
-
-/**
- * {@hide}
- */
-
-/* The implementation of this interface must be called Policy and contained
- * within the com.android.internal.policy.impl package */
-public interface IPolicy {
- public Window makeNewWindow(Context context);
-
- public LayoutInflater makeNewLayoutInflater(Context context);
-
- public WindowManagerPolicy makeNewWindowManager();
-
- public FallbackEventHandler makeNewFallbackEventHandler(Context context);
-}
diff --git a/core/java/com/android/internal/policy/PolicyManager.java b/core/java/com/android/internal/policy/PolicyManager.java
deleted file mode 100644
index 462b3a9..0000000
--- a/core/java/com/android/internal/policy/PolicyManager.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2008 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.internal.policy;
-
-import android.content.Context;
-import android.view.FallbackEventHandler;
-import android.view.LayoutInflater;
-import android.view.Window;
-import android.view.WindowManagerPolicy;
-
-/**
- * {@hide}
- */
-
-public final class PolicyManager {
- private static final String POLICY_IMPL_CLASS_NAME =
- "com.android.internal.policy.impl.Policy";
-
- private static final IPolicy sPolicy;
-
- static {
- // Pull in the actual implementation of the policy at run-time
- try {
- Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
- sPolicy = (IPolicy)policyClass.newInstance();
- } catch (ClassNotFoundException ex) {
- throw new RuntimeException(
- POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
- } catch (InstantiationException ex) {
- throw new RuntimeException(
- POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(
- POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
- }
- }
-
- // Cannot instantiate this class
- private PolicyManager() {}
-
- // The static methods to spawn new policy-specific objects
- public static Window makeNewWindow(Context context) {
- return sPolicy.makeNewWindow(context);
- }
-
- public static LayoutInflater makeNewLayoutInflater(Context context) {
- return sPolicy.makeNewLayoutInflater(context);
- }
-
- public static WindowManagerPolicy makeNewWindowManager() {
- return sPolicy.makeNewWindowManager();
- }
-
- public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
- return sPolicy.makeNewFallbackEventHandler(context);
- }
-}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a3c0db4..2b0d244 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -1,19 +1,19 @@
/**
* Copyright (c) 2007, 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
+ * 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
+ * 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
+ * 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.internal.statusbar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -43,5 +43,26 @@ oneway interface IStatusBar
void preloadRecentApps();
void cancelPreloadRecentApps();
void showScreenPinningRequest();
+
+ /**
+ * Notifies the status bar that an app transition is pending to delay applying some flags with
+ * visual impact until {@link #appTransitionReady} is called.
+ */
+ void appTransitionPending();
+
+ /**
+ * Notifies the status bar that a pending app transition has been cancelled.
+ */
+ void appTransitionCancelled();
+
+ /**
+ * Notifies the status bar that an app transition is now being executed.
+ *
+ * @param statusBarAnimationsStartTime the desired start time for all visual animations in the
+ * status bar caused by this app transition in uptime millis
+ * @param statusBarAnimationsDuration the duration for all visual animations in the status
+ * bar caused by this app transition in millis
+ */
+ void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 40c009f..6cb839e 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -1,16 +1,16 @@
/**
* Copyright (c) 2007, 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
+ * 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
+ * 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
+ * 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.
*/
@@ -61,4 +61,25 @@ interface IStatusBarService
void toggleRecentApps();
void preloadRecentApps();
void cancelPreloadRecentApps();
+
+ /**
+ * Notifies the status bar that an app transition is pending to delay applying some flags with
+ * visual impact until {@link #appTransitionReady} is called.
+ */
+ void appTransitionPending();
+
+ /**
+ * Notifies the status bar that a pending app transition has been cancelled.
+ */
+ void appTransitionCancelled();
+
+ /**
+ * Notifies the status bar that an app transition is now being executed.
+ *
+ * @param statusBarAnimationsStartTime the desired start time for all visual animations in the
+ * status bar caused by this app transition in uptime millis
+ * @param statusBarAnimationsDuration the duration for all visual animations in the status
+ * bar caused by this app transition in millis
+ */
+ void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
}
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
new file mode 100644
index 0000000..d8a7f16
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterClipReveal.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
+ * after the scene change and animates between those and the epicenter bounds
+ * during a visibility transition.
+ */
+public class EpicenterClipReveal extends Visibility {
+ private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+ private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+
+ public EpicenterClipReveal() {}
+
+ public EpicenterClipReveal(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ private void captureValues(TransitionValues values) {
+ final View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ final Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+
+ if (clip == null) {
+ final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ }
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (endValues == null) {
+ return null;
+ }
+
+ final Rect start = getEpicenter();
+ final Rect end = getBestRect(endValues);
+
+ // Prepare the view.
+ view.setClipBounds(start);
+
+ return createRectAnimator(view, start, end);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null) {
+ return null;
+ }
+
+ final Rect start = getBestRect(startValues);
+ final Rect end = getEpicenter();
+
+ // Prepare the view.
+ view.setClipBounds(start);
+
+ return createRectAnimator(view, start, end);
+ }
+
+ private Rect getBestRect(TransitionValues values) {
+ final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
+ if (clipRect == null) {
+ return (Rect) values.values.get(PROPNAME_BOUNDS);
+ }
+ return clipRect;
+ }
+
+ private Animator createRectAnimator(View view, Rect start, Rect end) {
+ final RectEvaluator evaluator = new RectEvaluator(new Rect());
+ return ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end);
+ }
+}
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 65b56ec..64e1d10 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -35,13 +35,14 @@ public final class DumpUtils {
* trying to acquire, we use a short timeout to avoid deadlocks. The process
* is inelegant but this function is only used for debugging purposes.
*/
- public static void dumpAsync(Handler handler, final Dump dump, PrintWriter pw, long timeout) {
+ public static void dumpAsync(Handler handler, final Dump dump, PrintWriter pw,
+ final String prefix, long timeout) {
final StringWriter sw = new StringWriter();
if (handler.runWithScissors(new Runnable() {
@Override
public void run() {
PrintWriter lpw = new FastPrintWriter(sw);
- dump.dump(lpw);
+ dump.dump(lpw, prefix);
lpw.close();
}
}, timeout)) {
@@ -52,6 +53,6 @@ public final class DumpUtils {
}
public interface Dump {
- void dump(PrintWriter pw);
+ void dump(PrintWriter pw, String prefix);
}
}
diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java
index e1e9d5e..c69d14f 100644
--- a/core/java/com/android/internal/util/UserIcons.java
+++ b/core/java/com/android/internal/util/UserIcons.java
@@ -48,9 +48,12 @@ public class UserIcons {
if (icon == null) {
return null;
}
- Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
- Bitmap.Config.ARGB_8888);
- icon.draw(new Canvas(bitmap));
+ final int width = icon.getIntrinsicWidth();
+ final int height = icon.getIntrinsicHeight();
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ icon.setBounds(0, 0, width, height);
+ icon.draw(canvas);
return bitmap;
}
diff --git a/core/java/com/android/internal/view/ActionModeWrapper.java b/core/java/com/android/internal/view/ActionModeWrapper.java
new file mode 100644
index 0000000..72066b9
--- /dev/null
+++ b/core/java/com/android/internal/view/ActionModeWrapper.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import android.content.Context;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.internal.view.menu.MenuBuilder;
+
+/**
+ * ActionMode implementation that wraps several actions modes and creates them on the fly depending
+ * on the ActionMode type chosen by the client.
+ */
+public class ActionModeWrapper extends ActionMode {
+
+ /**
+ * Interface to defer the ActionMode creation until the type is chosen.
+ */
+ public interface ActionModeProvider {
+ /**
+ * Create the desired ActionMode, that will immediately be used as the current active mode
+ * in the decorator.
+ *
+ * @param callback The {@link ActionMode.Callback} to be used.
+ * @param menuBuilder The {@link MenuBuilder} that should be used by the created
+ * {@link ActionMode}. This will already have been populated.
+ * @return A new {@link ActionMode} ready to be used that uses menuBuilder as its menu.
+ */
+ ActionMode createActionMode(ActionMode.Callback callback, MenuBuilder menuBuilder);
+ }
+
+ private ActionMode mActionMode;
+ private final Context mContext;
+ private MenuBuilder mMenu;
+ private final ActionMode.Callback mCallback;
+ private boolean mTypeLocked = false;
+
+ private CharSequence mTitle;
+ private CharSequence mSubtitle;
+ private View mCustomView;
+
+ private final ActionModeProvider mActionModeProvider;
+
+ public ActionModeWrapper(
+ Context context, ActionMode.Callback callback, ActionModeProvider actionModeProvider) {
+ mContext = context;
+ mMenu = new MenuBuilder(context).setDefaultShowAsAction(
+ MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ mCallback = callback;
+ mActionModeProvider = actionModeProvider;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ if (mActionMode != null) {
+ mActionMode.setTitle(title);
+ } else {
+ mTitle = title;
+ }
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ if (mActionMode != null) {
+ mActionMode.setTitle(resId);
+ } else {
+ mTitle = resId != 0 ? mContext.getString(resId) : null;
+ }
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ if (mActionMode != null) {
+ mActionMode.setSubtitle(subtitle);
+ } else {
+ mSubtitle = subtitle;
+ }
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ if (mActionMode != null) {
+ mActionMode.setSubtitle(resId);
+ } else {
+ mSubtitle = resId != 0 ? mContext.getString(resId) : null;
+ }
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ if (mActionMode != null) {
+ mActionMode.setCustomView(view);
+ } else {
+ mCustomView = view;
+ }
+ }
+
+ /**
+ * Set the current type as final and create the necessary ActionMode. After this call, any
+ * changes to the ActionMode type will be ignored.
+ */
+ public void lockType() {
+ mTypeLocked = true;
+ switch (getType()) {
+ case ActionMode.TYPE_PRIMARY:
+ default:
+ mActionMode = mActionModeProvider.createActionMode(mCallback, mMenu);
+ break;
+ case ActionMode.TYPE_FLOATING:
+ // Not implemented yet.
+ break;
+ }
+
+ if (mActionMode == null) {
+ return;
+ }
+
+ mActionMode.setTitle(mTitle);
+ mActionMode.setSubtitle(mSubtitle);
+ if (mCustomView != null) {
+ mActionMode.setCustomView(mCustomView);
+ }
+
+ mTitle = null;
+ mSubtitle = null;
+ mCustomView = null;
+ }
+
+ @Override
+ public void setType(int type) {
+ if (!mTypeLocked) {
+ super.setType(type);
+ } else {
+ throw new IllegalStateException(
+ "You can't change the ActionMode's type after onCreateActionMode.");
+ }
+ }
+
+ @Override
+ public void invalidate() {
+ if (mActionMode != null) {
+ mActionMode.invalidate();
+ }
+ }
+
+ @Override
+ public void finish() {
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
+ }
+
+ @Override
+ public Menu getMenu() {
+ return mMenu;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ if (mActionMode != null) {
+ return mActionMode.getTitle();
+ }
+ return mTitle;
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ if (mActionMode != null) {
+ return mActionMode.getSubtitle();
+ }
+ return mSubtitle;
+ }
+
+ @Override
+ public View getCustomView() {
+ if (mActionMode != null) {
+ return mActionMode.getCustomView();
+ }
+ return mCustomView;
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return new MenuInflater(mContext);
+ }
+
+}
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index d5d3602..2812b77 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -20,6 +20,7 @@ import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.SubMenuBuilder;
import com.android.internal.widget.ActionBarContextView;
+import android.annotation.Nullable;
import android.content.Context;
import android.view.ActionMode;
import android.view.Menu;
@@ -41,13 +42,15 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call
private MenuBuilder mMenu;
public StandaloneActionMode(Context context, ActionBarContextView view,
- ActionMode.Callback callback, boolean isFocusable) {
+ ActionMode.Callback callback, boolean isFocusable, @Nullable MenuBuilder menuBuilder) {
mContext = context;
mContextView = view;
mCallback = callback;
- mMenu = new MenuBuilder(view.getContext()).setDefaultShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ mMenu = (menuBuilder != null)
+ ? menuBuilder
+ : new MenuBuilder(view.getContext()).setDefaultShowAsAction(
+ MenuItem.SHOW_AS_ACTION_IF_ROOM);
mMenu.setCallback(this);
mFocusable = isFocusable;
}
@@ -64,12 +67,12 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call
@Override
public void setTitle(int resId) {
- setTitle(mContext.getString(resId));
+ setTitle(resId != 0 ? mContext.getString(resId) : null);
}
@Override
public void setSubtitle(int resId) {
- setSubtitle(mContext.getString(resId));
+ setSubtitle(resId != 0 ? mContext.getString(resId) : null);
}
@Override
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index 7eec392..f75b139 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -217,14 +217,14 @@ public class ActionMenuItemView extends TextView
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
onPopulateAccessibilityEvent(event);
return true;
}
@Override
- public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
- super.onPopulateAccessibilityEvent(event);
+ public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEventInternal(event);
final CharSequence cdesc = getContentDescription();
if (!TextUtils.isEmpty(cdesc)) {
event.getText().add(cdesc);
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 692bdac..29ac3f3 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -276,8 +276,8 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
}
@Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
+ public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfoInternal(info);
if (mItemData != null && mItemData.hasSubMenu()) {
info.setCanOpenPopup(true);
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 99bb1ac..2b20b38 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -118,6 +118,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
mDropDownGravity = gravity;
}
+ public int getGravity() {
+ return mDropDownGravity;
+ }
+
public void show() {
if (!tryShow()) {
throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
@@ -135,7 +139,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
mPopup.setAdapter(mAdapter);
mPopup.setModal(true);
- View anchor = mAnchorView;
+ final View anchor = mAnchorView;
if (anchor != null) {
final boolean addGlobalListener = mTreeObserver == null;
mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
index e91a55c..f97a5d1 100644
--- a/core/java/com/android/internal/widget/AccessibleDateAnimator.java
+++ b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
@@ -40,7 +40,7 @@ public class AccessibleDateAnimator extends ViewAnimator {
* Announce the currently-selected date when launched.
*/
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
// Clear the event's current text so that only the current date will be spoken.
event.getText().clear();
@@ -51,6 +51,6 @@ public class AccessibleDateAnimator extends ViewAnimator {
event.getText().add(dateString);
return true;
}
- return super.dispatchPopulateAccessibilityEvent(event);
+ return super.dispatchPopulateAccessibilityEventInternal(event);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 7c671e8..ae5999a 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -158,7 +158,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
removeView(mCustomView);
}
mCustomView = view;
- if (mTitleLayout != null) {
+ if (view != null && mTitleLayout != null) {
removeView(mTitleLayout);
mTitleLayout = null;
}
@@ -529,7 +529,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
}
@Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
// Action mode started
event.setSource(this);
@@ -537,7 +537,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
event.setPackageName(getContext().getPackageName());
event.setContentDescription(mTitle);
} else {
- super.onInitializeAccessibilityEvent(event);
+ super.onInitializeAccessibilityEventInternal(event);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 654d08b..88436f8 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -1463,14 +1463,14 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar {
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
onPopulateAccessibilityEvent(event);
return true;
}
@Override
- public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
- super.onPopulateAccessibilityEvent(event);
+ public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEventInternal(event);
final CharSequence cdesc = getContentDescription();
if (!TextUtils.isEmpty(cdesc)) {
event.getText().add(cdesc);
diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java
new file mode 100644
index 0000000..64e6c69
--- /dev/null
+++ b/core/java/com/android/internal/widget/ButtonBarLayout.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+/**
+ * An extension of LinearLayout that automatically switches to vertical
+ * orientation when it can't fit its child views horizontally.
+ */
+public class ButtonBarLayout extends LinearLayout {
+ /** Spacer used in horizontal orientation. */
+ private final View mSpacer;
+
+ /** Whether the current configuration allows stacking. */
+ private final boolean mAllowStacked;
+
+ /** Whether the layout is currently stacked. */
+ private boolean mStacked;
+
+ public ButtonBarLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mAllowStacked = context.getResources().getBoolean(R.bool.allow_stacked_button_bar);
+ mSpacer = findViewById(R.id.spacer);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ // Maybe we can fit the content now?
+ if (w > oldw && mStacked) {
+ setStacked(false);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (mAllowStacked && getOrientation() == LinearLayout.HORIZONTAL) {
+ final int measuredWidth = getMeasuredWidthAndState();
+ final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
+ if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
+ setStacked(true);
+
+ // Measure again in the new orientation.
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+ }
+
+ private void setStacked(boolean stacked) {
+ setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
+ setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM);
+
+ if (mSpacer != null) {
+ mSpacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
+ }
+
+ // Reverse the child order. This is specific to the Material button
+ // bar's layout XML and will probably not generalize.
+ final int childCount = getChildCount();
+ for (int i = childCount - 2; i >= 0; i--) {
+ bringChildToFront(getChildAt(i));
+ }
+
+ mStacked = stacked;
+ }
+}
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index 0e046cb..bdf17fc 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -567,7 +567,15 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
}
// TODO: Check virtual view visibility.
if (!isAccessibilityFocused(virtualViewId)) {
+ // Clear focus from the previously focused view, if applicable.
+ if (mFocusedVirtualViewId != INVALID_ID) {
+ sendEventForVirtualView(mFocusedVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ }
+
+ // Set focus on the new view.
mFocusedVirtualViewId = virtualViewId;
+
// TODO: Only invalidate virtual view bounds.
mView.invalidate();
sendEventForVirtualView(virtualViewId,
diff --git a/core/java/com/android/internal/widget/FaceUnlockView.java b/core/java/com/android/internal/widget/FaceUnlockView.java
deleted file mode 100644
index 121e601..0000000
--- a/core/java/com/android/internal/widget/FaceUnlockView.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.RelativeLayout;
-
-public class FaceUnlockView extends RelativeLayout {
- private static final String TAG = "FaceUnlockView";
-
- public FaceUnlockView(Context context) {
- this(context, null);
- }
-
- public FaceUnlockView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- private int resolveMeasured(int measureSpec, int desired)
- {
- int result = 0;
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (MeasureSpec.getMode(measureSpec)) {
- case MeasureSpec.UNSPECIFIED:
- result = desired;
- break;
- case MeasureSpec.AT_MOST:
- result = Math.max(specSize, desired);
- break;
- case MeasureSpec.EXACTLY:
- default:
- result = specSize;
- }
- return result;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int minimumWidth = getSuggestedMinimumWidth();
- final int minimumHeight = getSuggestedMinimumHeight();
- int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
- int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-
- final int chosenSize = Math.min(viewWidth, viewHeight);
- final int newWidthMeasureSpec =
- MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST);
- final int newHeightMeasureSpec =
- MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST);
-
- super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
- }
-}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 9501f92..0cb1f38 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -31,5 +31,4 @@ interface ILockSettings {
boolean checkVoldPassword(int userId);
boolean havePattern(int userId);
boolean havePassword(int userId);
- void removeUser(int userId);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 0afc651..90821dc 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -18,14 +18,11 @@ package com.android.internal.widget;
import android.Manifest;
import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
-import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.AsyncTask;
@@ -39,19 +36,12 @@ import android.os.UserManager;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.provider.Settings;
-import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
-import android.view.IWindowManager;
-import android.view.View;
-import android.widget.Button;
-import com.android.internal.R;
import com.google.android.collect.Lists;
-import java.io.ByteArrayOutputStream;
-import java.nio.charset.StandardCharsets;
-import libcore.util.HexEncoding;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -59,6 +49,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import libcore.util.HexEncoding;
+
/**
* Utilities for the lock pattern and its settings.
*/
@@ -103,52 +95,36 @@ public class LockPatternUtils {
public static final int MIN_LOCK_PATTERN_SIZE = 4;
/**
+ * The minimum size of a valid password.
+ */
+ public static final int MIN_LOCK_PASSWORD_SIZE = 4;
+
+ /**
* The minimum number of dots the user must include in a wrong pattern
* attempt for it to be counted against the counts that affect
* {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
*/
public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
- /**
- * Tells the keyguard to show the user switcher when the keyguard is created.
- */
- public static final String KEYGUARD_SHOW_USER_SWITCHER = "showuserswitcher";
-
- /**
- * Tells the keyguard to show the security challenge when the keyguard is created.
- */
- public static final String KEYGUARD_SHOW_SECURITY_CHALLENGE = "showsecuritychallenge";
-
- /**
- * Tells the keyguard to show the widget with the specified id when the keyguard is created.
- */
- public static final String KEYGUARD_SHOW_APPWIDGET = "showappwidget";
-
- /**
- * The bit in LOCK_BIOMETRIC_WEAK_FLAGS to be used to indicate whether liveliness should
- * be used
- */
- public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1;
-
- /**
- * Pseudo-appwidget id we use to represent the default clock status widget
- */
- public static final int ID_DEFAULT_STATUS_WIDGET = -2;
-
+ @Deprecated
public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
- public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
+ @Deprecated
+ public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
public final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
public final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
+ @Deprecated
public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
= "lockscreen.biometric_weak_fallback";
+ @Deprecated
public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
= "lockscreen.biometricweakeverchosen";
public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
= "lockscreen.power_button_instantly_locks";
+ @Deprecated
public final static String LOCKSCREEN_WIDGETS_ENABLED = "lockscreen.widgets_enabled";
public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
@@ -227,7 +203,11 @@ public class LockPatternUtils {
}
public int getRequestedPasswordHistoryLength() {
- return getDevicePolicyManager().getPasswordHistoryLength(null, getCurrentOrCallingUserId());
+ return getRequestedPasswordHistoryLength(getCurrentOrCallingUserId());
+ }
+
+ private int getRequestedPasswordHistoryLength(int userId) {
+ return getDevicePolicyManager().getPasswordHistoryLength(null, userId);
}
public int getRequestedPasswordMinimumLetters() {
@@ -289,14 +269,6 @@ public class LockPatternUtils {
}
}
- public void removeUser(int userId) {
- try {
- getLockSettings().removeUser(userId);
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't remove lock settings for user " + userId);
- }
- }
-
private int getCurrentOrCallingUserId() {
if (mMultiUserMode) {
// TODO: This is a little inefficient. See if all users of this are able to
@@ -359,9 +331,10 @@ public class LockPatternUtils {
* @return Whether the password matches any in the history.
*/
public boolean checkPasswordHistory(String password) {
+ int userId = getCurrentOrCallingUserId();
String passwordHashString = new String(
- passwordToHash(password, getCurrentOrCallingUserId()), StandardCharsets.UTF_8);
- String passwordHistory = getString(PASSWORD_HISTORY_KEY);
+ passwordToHash(password, userId), StandardCharsets.UTF_8);
+ String passwordHistory = getString(PASSWORD_HISTORY_KEY, userId);
if (passwordHistory == null) {
return false;
}
@@ -383,15 +356,7 @@ public class LockPatternUtils {
* Check to see if the user has stored a lock pattern.
* @return Whether a saved pattern exists.
*/
- public boolean savedPatternExists() {
- return savedPatternExists(getCurrentOrCallingUserId());
- }
-
- /**
- * Check to see if the user has stored a lock pattern.
- * @return Whether a saved pattern exists.
- */
- public boolean savedPatternExists(int userId) {
+ private boolean savedPatternExists(int userId) {
try {
return getLockSettings().havePattern(userId);
} catch (RemoteException re) {
@@ -403,15 +368,7 @@ public class LockPatternUtils {
* Check to see if the user has stored a lock pattern.
* @return Whether a saved pattern exists.
*/
- public boolean savedPasswordExists() {
- return savedPasswordExists(getCurrentOrCallingUserId());
- }
-
- /**
- * Check to see if the user has stored a lock pattern.
- * @return Whether a saved pattern exists.
- */
- public boolean savedPasswordExists(int userId) {
+ private boolean savedPasswordExists(int userId) {
try {
return getLockSettings().havePassword(userId);
} catch (RemoteException re) {
@@ -426,86 +383,62 @@ public class LockPatternUtils {
* @return True if the user has ever chosen a pattern.
*/
public boolean isPatternEverChosen() {
- return getBoolean(PATTERN_EVER_CHOSEN_KEY, false);
+ return getBoolean(PATTERN_EVER_CHOSEN_KEY, false, getCurrentOrCallingUserId());
}
/**
- * Return true if the user has ever chosen biometric weak. This is true even if biometric
- * weak is not current set.
- *
- * @return True if the user has ever chosen biometric weak.
+ * Used by device policy manager to validate the current password
+ * information it has.
*/
- public boolean isBiometricWeakEverChosen() {
- return getBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, false);
+ public int getActivePasswordQuality() {
+ return getActivePasswordQuality(getCurrentOrCallingUserId());
}
/**
* Used by device policy manager to validate the current password
* information it has.
*/
- public int getActivePasswordQuality() {
- int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- // Note we don't want to use getKeyguardStoredPasswordQuality() because we want this to
- // return biometric_weak if that is being used instead of the backup
- int quality =
- (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
- switch (quality) {
- case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
- if (isLockPatternEnabled()) {
- activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
- }
- break;
- case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK:
- if (isBiometricWeakInstalled()) {
- activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
- }
- break;
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
- if (isLockPasswordEnabled()) {
- activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
- }
- break;
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
- if (isLockPasswordEnabled()) {
- activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
- }
- break;
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
- if (isLockPasswordEnabled()) {
- activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
- }
- break;
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
- if (isLockPasswordEnabled()) {
- activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
- }
- break;
- case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
- if (isLockPasswordEnabled()) {
- activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
- }
- break;
+ public int getActivePasswordQuality(int userId) {
+ int quality = getKeyguardStoredPasswordQuality(userId);
+
+ if (isLockPasswordEnabled(quality, userId)) {
+ // Quality is a password and a password exists. Return the quality.
+ return quality;
}
- return activePasswordQuality;
+ if (isLockPatternEnabled(quality, userId)) {
+ // Quality is a pattern and a pattern exists. Return the quality.
+ return quality;
+ }
+
+ return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
- public void clearLock(boolean isFallback) {
- clearLock(isFallback, getCurrentOrCallingUserId());
+ public void clearLock() {
+ clearLock(getCurrentOrCallingUserId());
}
/**
* Clear any lock pattern or password.
*/
- public void clearLock(boolean isFallback, int userHandle) {
- if(!isFallback) deleteGallery(userHandle);
- saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, isFallback,
- userHandle);
- setLockPatternEnabled(false, userHandle);
- saveLockPattern(null, isFallback, userHandle);
+ public void clearLock(int userHandle) {
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
- setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- userHandle);
+
+ try {
+ getLockSettings().setLockPassword(null, userHandle);
+ getLockSettings().setLockPattern(null, userHandle);
+ } catch (RemoteException e) {
+ // well, we tried...
+ }
+
+ if (userHandle == UserHandle.USER_OWNER) {
+ // Set the encryption password to default.
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+ }
+
+ getDevicePolicyManager().setActivePasswordState(
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
+
onAfterChangingPassword(userHandle);
}
@@ -516,7 +449,7 @@ public class LockPatternUtils {
* @param disable Disables lock screen when true
*/
public void setLockScreenDisabled(boolean disable) {
- setLong(DISABLE_LOCKSCREEN_KEY, disable ? 1 : 0);
+ setBoolean(DISABLE_LOCKSCREEN_KEY, disable, getCurrentOrCallingUserId());
}
/**
@@ -526,7 +459,7 @@ public class LockPatternUtils {
* @return true if lock screen is can be disabled
*/
public boolean isLockScreenDisabled() {
- if (!isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0) {
+ if (!isSecure() && getBoolean(DISABLE_LOCKSCREEN_KEY, false, getCurrentOrCallingUserId())) {
// Check if the number of switchable users forces the lockscreen.
final List<UserInfo> users = UserManager.get(mContext).getUsers(true);
final int userCount = users.size();
@@ -542,96 +475,57 @@ public class LockPatternUtils {
}
/**
- * Calls back SetupFaceLock to delete the temporary gallery file
- */
- public void deleteTempGallery() {
- Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY");
- intent.putExtra("deleteTempGallery", true);
- mContext.sendBroadcast(intent);
- }
-
- /**
- * Calls back SetupFaceLock to delete the gallery file when the lock type is changed
- */
- void deleteGallery(int userId) {
- if(usingBiometricWeak(userId)) {
- Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY");
- intent.putExtra("deleteGallery", true);
- mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
- }
- }
-
- /**
* Save a lock pattern.
* @param pattern The new pattern to save.
*/
public void saveLockPattern(List<LockPatternView.Cell> pattern) {
- this.saveLockPattern(pattern, false);
- }
-
- /**
- * Save a lock pattern.
- * @param pattern The new pattern to save.
- */
- public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
- this.saveLockPattern(pattern, isFallback, getCurrentOrCallingUserId());
+ this.saveLockPattern(pattern, getCurrentOrCallingUserId());
}
/**
* Save a lock pattern.
* @param pattern The new pattern to save.
- * @param isFallback Specifies if this is a fallback to biometric weak
* @param userId the user whose pattern is to be saved.
*/
- public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback,
- int userId) {
+ public void saveLockPattern(List<LockPatternView.Cell> pattern, int userId) {
try {
+ if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
+ throw new IllegalArgumentException("pattern must not be null and at least "
+ + MIN_LOCK_PATTERN_SIZE + " dots long.");
+ }
+
getLockSettings().setLockPattern(patternToString(pattern), userId);
DevicePolicyManager dpm = getDevicePolicyManager();
- if (pattern != null) {
- // Update the device encryption password.
- if (userId == UserHandle.USER_OWNER
- && LockPatternUtils.isDeviceEncryptionEnabled()) {
- final boolean required = isCredentialRequiredToDecrypt(true);
- if (!required) {
- clearEncryptionPassword();
- } else {
- String stringPattern = patternToString(pattern);
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
- }
- }
- setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
- if (!isFallback) {
- deleteGallery(userId);
- setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
- dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
- pattern.size(), 0, 0, 0, 0, 0, 0, userId);
+ // Update the device encryption password.
+ if (userId == UserHandle.USER_OWNER
+ && LockPatternUtils.isDeviceEncryptionEnabled()) {
+ final boolean required = isCredentialRequiredToDecrypt(true);
+ if (!required) {
+ clearEncryptionPassword();
} else {
- setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, userId);
- setLong(PASSWORD_TYPE_ALTERNATE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
- finishBiometricWeak(userId);
- dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
- 0, 0, 0, 0, 0, 0, 0, userId);
+ String stringPattern = patternToString(pattern);
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
}
- } else {
- dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
- 0, 0, 0, 0, 0, userId);
}
+
+ setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
+
+ setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
+ dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
+ pattern.size(), 0, 0, 0, 0, 0, 0, userId);
onAfterChangingPassword(userId);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't save lock pattern " + re);
}
}
- private void updateCryptoUserInfo() {
- int userId = getCurrentOrCallingUserId();
+ private void updateCryptoUserInfo(int userId) {
if (userId != UserHandle.USER_OWNER) {
return;
}
- final String ownerInfo = isOwnerInfoEnabled() ? getOwnerInfo(userId) : "";
+ final String ownerInfo = isOwnerInfoEnabled(userId) ? getOwnerInfo(userId) : "";
IBinder service = ServiceManager.getService("mount");
if (service == null) {
@@ -650,20 +544,25 @@ public class LockPatternUtils {
public void setOwnerInfo(String info, int userId) {
setString(LOCK_SCREEN_OWNER_INFO, info, userId);
- updateCryptoUserInfo();
+ updateCryptoUserInfo(userId);
}
public void setOwnerInfoEnabled(boolean enabled) {
- setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled);
- updateCryptoUserInfo();
+ int userId = getCurrentOrCallingUserId();
+ setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled, userId);
+ updateCryptoUserInfo(userId);
}
public String getOwnerInfo(int userId) {
- return getString(LOCK_SCREEN_OWNER_INFO);
+ return getString(LOCK_SCREEN_OWNER_INFO, userId);
}
public boolean isOwnerInfoEnabled() {
- return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false);
+ return isOwnerInfoEnabled(getCurrentOrCallingUserId());
+ }
+
+ private boolean isOwnerInfoEnabled(int userId) {
+ return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false, userId);
}
/**
@@ -789,19 +688,7 @@ public class LockPatternUtils {
* @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
*/
public void saveLockPassword(String password, int quality) {
- this.saveLockPassword(password, quality, false, getCurrentOrCallingUserId());
- }
-
- /**
- * Save a lock password. Does not ensure that the password is as good
- * as the requested mode, but will adjust the mode to be as good as the
- * pattern.
- * @param password The password to save
- * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
- * @param isFallback Specifies if this is a fallback to biometric weak
- */
- public void saveLockPassword(String password, int quality, boolean isFallback) {
- saveLockPassword(password, quality, isFallback, getCurrentOrCallingUserId());
+ saveLockPassword(password, quality, getCurrentOrCallingUserId());
}
/**
@@ -810,108 +697,88 @@ public class LockPatternUtils {
* pattern.
* @param password The password to save
* @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
- * @param isFallback Specifies if this is a fallback to biometric weak
* @param userHandle The userId of the user to change the password for
*/
- public void saveLockPassword(String password, int quality, boolean isFallback, int userHandle) {
+ public void saveLockPassword(String password, int quality, int userHandle) {
try {
DevicePolicyManager dpm = getDevicePolicyManager();
- if (!TextUtils.isEmpty(password)) {
- getLockSettings().setLockPassword(password, userHandle);
- int computedQuality = computePasswordQuality(password);
-
- // Update the device encryption password.
- if (userHandle == UserHandle.USER_OWNER
- && LockPatternUtils.isDeviceEncryptionEnabled()) {
- if (!isCredentialRequiredToDecrypt(true)) {
- clearEncryptionPassword();
- } else {
- boolean numeric = computedQuality
- == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
- boolean numericComplex = computedQuality
- == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
- int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
- : StorageManager.CRYPT_TYPE_PASSWORD;
- updateEncryptionPassword(type, password);
- }
+ if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
+ throw new IllegalArgumentException("password must not be null and at least "
+ + "of length " + MIN_LOCK_PASSWORD_SIZE);
+ }
+
+ getLockSettings().setLockPassword(password, userHandle);
+ int computedQuality = computePasswordQuality(password);
+
+ // Update the device encryption password.
+ if (userHandle == UserHandle.USER_OWNER
+ && LockPatternUtils.isDeviceEncryptionEnabled()) {
+ if (!isCredentialRequiredToDecrypt(true)) {
+ clearEncryptionPassword();
+ } else {
+ boolean numeric = computedQuality
+ == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+ boolean numericComplex = computedQuality
+ == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+ int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
+ : StorageManager.CRYPT_TYPE_PASSWORD;
+ updateEncryptionPassword(type, password);
}
+ }
- if (!isFallback) {
- deleteGallery(userHandle);
- setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
- if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
- int letters = 0;
- int uppercase = 0;
- int lowercase = 0;
- int numbers = 0;
- int symbols = 0;
- int nonletter = 0;
- for (int i = 0; i < password.length(); i++) {
- char c = password.charAt(i);
- if (c >= 'A' && c <= 'Z') {
- letters++;
- uppercase++;
- } else if (c >= 'a' && c <= 'z') {
- letters++;
- lowercase++;
- } else if (c >= '0' && c <= '9') {
- numbers++;
- nonletter++;
- } else {
- symbols++;
- nonletter++;
- }
- }
- dpm.setActivePasswordState(Math.max(quality, computedQuality),
- password.length(), letters, uppercase, lowercase,
- numbers, symbols, nonletter, userHandle);
+ setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
+ if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ int letters = 0;
+ int uppercase = 0;
+ int lowercase = 0;
+ int numbers = 0;
+ int symbols = 0;
+ int nonletter = 0;
+ for (int i = 0; i < password.length(); i++) {
+ char c = password.charAt(i);
+ if (c >= 'A' && c <= 'Z') {
+ letters++;
+ uppercase++;
+ } else if (c >= 'a' && c <= 'z') {
+ letters++;
+ lowercase++;
+ } else if (c >= '0' && c <= '9') {
+ numbers++;
+ nonletter++;
} else {
- // The password is not anything.
- dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- 0, 0, 0, 0, 0, 0, 0, userHandle);
+ symbols++;
+ nonletter++;
}
- } else {
- // Case where it's a fallback for biometric weak
- setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
- userHandle);
- setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality),
- userHandle);
- finishBiometricWeak(userHandle);
- dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
- 0, 0, 0, 0, 0, 0, 0, userHandle);
}
- // Add the password to the password history. We assume all
- // password hashes have the same length for simplicity of implementation.
- String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
- if (passwordHistory == null) {
- passwordHistory = "";
- }
- int passwordHistoryLength = getRequestedPasswordHistoryLength();
- if (passwordHistoryLength == 0) {
- passwordHistory = "";
- } else {
- byte[] hash = passwordToHash(password, userHandle);
- passwordHistory = new String(hash, StandardCharsets.UTF_8) + "," + passwordHistory;
- // Cut it to contain passwordHistoryLength hashes
- // and passwordHistoryLength -1 commas.
- passwordHistory = passwordHistory.substring(0, Math.min(hash.length
- * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
- .length()));
- }
- setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
+ dpm.setActivePasswordState(Math.max(quality, computedQuality),
+ password.length(), letters, uppercase, lowercase,
+ numbers, symbols, nonletter, userHandle);
} else {
- // Empty password
- getLockSettings().setLockPassword(null, userHandle);
- if (userHandle == UserHandle.USER_OWNER) {
- // Set the encryption password to default.
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
- }
-
+ // The password is not anything.
dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
- userHandle);
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+ 0, 0, 0, 0, 0, 0, 0, userHandle);
}
+
+ // Add the password to the password history. We assume all
+ // password hashes have the same length for simplicity of implementation.
+ String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
+ if (passwordHistory == null) {
+ passwordHistory = "";
+ }
+ int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle);
+ if (passwordHistoryLength == 0) {
+ passwordHistory = "";
+ } else {
+ byte[] hash = passwordToHash(password, userHandle);
+ passwordHistory = new String(hash, StandardCharsets.UTF_8) + "," + passwordHistory;
+ // Cut it to contain passwordHistoryLength hashes
+ // and passwordHistoryLength -1 commas.
+ passwordHistory = passwordHistory.substring(0, Math.min(hash.length
+ * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
+ .length()));
+ }
+ setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
onAfterChangingPassword(userHandle);
} catch (RemoteException re) {
// Cant do much
@@ -971,31 +838,8 @@ public class LockPatternUtils {
* @return stored password quality
*/
public int getKeyguardStoredPasswordQuality(int userHandle) {
- int quality = (int) getLong(PASSWORD_TYPE_KEY,
+ return (int) getLong(PASSWORD_TYPE_KEY,
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
- // If the user has chosen to use weak biometric sensor, then return the backup locking
- // method and treat biometric as a special case.
- if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
- quality = (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
- }
- return quality;
- }
-
- /**
- * @return true if the lockscreen method is set to biometric weak
- */
- public boolean usingBiometricWeak() {
- return usingBiometricWeak(getCurrentOrCallingUserId());
- }
-
- /**
- * @return true if the lockscreen method is set to biometric weak
- */
- public boolean usingBiometricWeak(int userId) {
- int quality = (int) getLong(
- PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
- return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
}
/**
@@ -1004,6 +848,10 @@ public class LockPatternUtils {
* @return The pattern.
*/
public static List<LockPatternView.Cell> stringToPattern(String string) {
+ if (string == null) {
+ return null;
+ }
+
List<LockPatternView.Cell> result = Lists.newArrayList();
final byte[] bytes = string.getBytes();
@@ -1106,126 +954,73 @@ public class LockPatternUtils {
}
/**
- * @return Whether the lock password is enabled, or if it is set as a backup for biometric weak
+ * @return Whether the lock screen is secured.
*/
- public boolean isLockPasswordEnabled() {
- long mode = getLong(PASSWORD_TYPE_KEY, 0);
- long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0);
- final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
- || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
- || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
- || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
- final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
- || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
- || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
- || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-
- return savedPasswordExists() && (passwordEnabled ||
- (usingBiometricWeak() && backupEnabled));
+ public boolean isSecure() {
+ return isSecure(getCurrentOrCallingUserId());
}
/**
- * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
+ * @param userId the user for which to report the value
+ * @return Whether the lock screen is secured.
*/
- public boolean isLockPatternEnabled() {
- return isLockPatternEnabled(getCurrentOrCallingUserId());
+ public boolean isSecure(int userId) {
+ int mode = getKeyguardStoredPasswordQuality(userId);
+ return isLockPatternEnabled(mode, userId) || isLockPasswordEnabled(mode, userId);
}
/**
- * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
+ * @return Whether the lock password is enabled
*/
- public boolean isLockPatternEnabled(int userId) {
- final boolean backupEnabled =
- getLong(PASSWORD_TYPE_ALTERNATE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId)
- == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
-
- return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false, userId)
- && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- userId) == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
- || (usingBiometricWeak(userId) && backupEnabled));
+ public boolean isLockPasswordEnabled() {
+ return isLockPasswordEnabled(getCurrentOrCallingUserId());
}
- /**
- * @return Whether biometric weak lock is installed and that the front facing camera exists
- */
- public boolean isBiometricWeakInstalled() {
- // Check that it's installed
- PackageManager pm = mContext.getPackageManager();
- try {
- pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES);
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
-
- // Check that the camera is enabled
- if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
- return false;
- }
- if (getDevicePolicyManager().getCameraDisabled(null, getCurrentOrCallingUserId())) {
- return false;
- }
-
- // TODO: If we decide not to proceed with Face Unlock as a trustlet, this must be changed
- // back to returning true. If we become certain that Face Unlock will be a trustlet, this
- // entire function and a lot of other code can be removed.
- if (DEBUG) Log.d(TAG, "Forcing isBiometricWeakInstalled() to return false to disable it");
- return false;
+ public boolean isLockPasswordEnabled(int userId) {
+ return isLockPasswordEnabled(getKeyguardStoredPasswordQuality(userId), userId);
}
- /**
- * Set whether biometric weak liveliness is enabled.
- */
- public void setBiometricWeakLivelinessEnabled(boolean enabled) {
- long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
- long newFlag;
- if (enabled) {
- newFlag = currentFlag | FLAG_BIOMETRIC_WEAK_LIVELINESS;
- } else {
- newFlag = currentFlag & ~FLAG_BIOMETRIC_WEAK_LIVELINESS;
- }
- setLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, newFlag);
+ private boolean isLockPasswordEnabled(int mode, int userId) {
+ final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+ return passwordEnabled && savedPasswordExists(userId);
}
/**
- * @return Whether the biometric weak liveliness is enabled.
+ * @return Whether the lock pattern is enabled
*/
- public boolean isBiometricWeakLivelinessEnabled() {
- long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
- return ((currentFlag & FLAG_BIOMETRIC_WEAK_LIVELINESS) != 0);
+ public boolean isLockPatternEnabled() {
+ return isLockPatternEnabled(getCurrentOrCallingUserId());
}
- /**
- * Set whether the lock pattern is enabled.
- */
- public void setLockPatternEnabled(boolean enabled) {
- setLockPatternEnabled(enabled, getCurrentOrCallingUserId());
+ public boolean isLockPatternEnabled(int userId) {
+ return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId);
}
- /**
- * Set whether the lock pattern is enabled.
- */
- public void setLockPatternEnabled(boolean enabled, int userHandle) {
- setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled, userHandle);
+ private boolean isLockPatternEnabled(int mode, int userId) {
+ return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+ && savedPatternExists(userId);
}
/**
* @return Whether the visible pattern is enabled.
*/
public boolean isVisiblePatternEnabled() {
- return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false);
+ return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false, getCurrentOrCallingUserId());
}
/**
* Set whether the visible pattern is enabled.
*/
public void setVisiblePatternEnabled(boolean enabled) {
- setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled);
+ int userId = getCurrentOrCallingUserId();
+
+ setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled, userId);
// Update for crypto if owner
- int userId = getCurrentOrCallingUserId();
if (userId != UserHandle.USER_OWNER) {
return;
}
@@ -1259,7 +1054,7 @@ public class LockPatternUtils {
*/
public long setLockoutAttemptDeadline() {
final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
- setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline);
+ setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, getCurrentOrCallingUserId());
return deadline;
}
@@ -1269,7 +1064,7 @@ public class LockPatternUtils {
* enter a pattern.
*/
public long getLockoutAttemptDeadline() {
- final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L);
+ final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, getCurrentOrCallingUserId());
final long now = SystemClock.elapsedRealtime();
if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
return 0L;
@@ -1277,51 +1072,6 @@ public class LockPatternUtils {
return deadline;
}
- /**
- * @return Whether the user is permanently locked out until they verify their
- * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
- * attempts.
- */
- public boolean isPermanentlyLocked() {
- return getBoolean(LOCKOUT_PERMANENT_KEY, false);
- }
-
- /**
- * Set the state of whether the device is permanently locked, meaning the user
- * must authenticate via other means.
- *
- * @param locked Whether the user is permanently locked out until they verify their
- * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
- * attempts.
- */
- public void setPermanentlyLocked(boolean locked) {
- setBoolean(LOCKOUT_PERMANENT_KEY, locked);
- }
-
- public boolean isEmergencyCallCapable() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_voice_capable);
- }
-
- public boolean isPukUnlockScreenEnable() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enable_puk_unlock_screen);
- }
-
- public boolean isEmergencyCallEnabledWhileSimLocked() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
- }
-
- /**
- * @return A formatted string of the next alarm (for showing on the lock screen),
- * or null if there is no next alarm.
- */
- public AlarmManager.AlarmClockInfo getNextAlarm() {
- AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- return alarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
- }
-
private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
try {
return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId);
@@ -1330,10 +1080,6 @@ public class LockPatternUtils {
}
}
- private boolean getBoolean(String secureSettingKey, boolean defaultValue) {
- return getBoolean(secureSettingKey, defaultValue, getCurrentOrCallingUserId());
- }
-
private void setBoolean(String secureSettingKey, boolean enabled, int userId) {
try {
getLockSettings().setBoolean(secureSettingKey, enabled, userId);
@@ -1343,144 +1089,6 @@ public class LockPatternUtils {
}
}
- private void setBoolean(String secureSettingKey, boolean enabled) {
- setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId());
- }
-
- public int[] getAppWidgets() {
- return getAppWidgets(UserHandle.USER_CURRENT);
- }
-
- private int[] getAppWidgets(int userId) {
- String appWidgetIdString = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS, userId);
- String delims = ",";
- if (appWidgetIdString != null && appWidgetIdString.length() > 0) {
- String[] appWidgetStringIds = appWidgetIdString.split(delims);
- int[] appWidgetIds = new int[appWidgetStringIds.length];
- for (int i = 0; i < appWidgetStringIds.length; i++) {
- String appWidget = appWidgetStringIds[i];
- try {
- appWidgetIds[i] = Integer.decode(appWidget);
- } catch (NumberFormatException e) {
- Log.d(TAG, "Error when parsing widget id " + appWidget);
- return null;
- }
- }
- return appWidgetIds;
- }
- return new int[0];
- }
-
- private static String combineStrings(int[] list, String separator) {
- int listLength = list.length;
-
- switch (listLength) {
- case 0: {
- return "";
- }
- case 1: {
- return Integer.toString(list[0]);
- }
- }
-
- int strLength = 0;
- int separatorLength = separator.length();
-
- String[] stringList = new String[list.length];
- for (int i = 0; i < listLength; i++) {
- stringList[i] = Integer.toString(list[i]);
- strLength += stringList[i].length();
- if (i < listLength - 1) {
- strLength += separatorLength;
- }
- }
-
- StringBuilder sb = new StringBuilder(strLength);
-
- for (int i = 0; i < listLength; i++) {
- sb.append(list[i]);
- if (i < listLength - 1) {
- sb.append(separator);
- }
- }
-
- return sb.toString();
- }
-
- // appwidget used when appwidgets are disabled (we make an exception for
- // default clock widget)
- public void writeFallbackAppWidgetId(int appWidgetId) {
- Settings.Secure.putIntForUser(mContentResolver,
- Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
- appWidgetId,
- UserHandle.USER_CURRENT);
- }
-
- // appwidget used when appwidgets are disabled (we make an exception for
- // default clock widget)
- public int getFallbackAppWidgetId() {
- return Settings.Secure.getIntForUser(
- mContentResolver,
- Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
- AppWidgetManager.INVALID_APPWIDGET_ID,
- UserHandle.USER_CURRENT);
- }
-
- private void writeAppWidgets(int[] appWidgetIds) {
- Settings.Secure.putStringForUser(mContentResolver,
- Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
- combineStrings(appWidgetIds, ","),
- UserHandle.USER_CURRENT);
- }
-
- // TODO: log an error if this returns false
- public boolean addAppWidget(int widgetId, int index) {
- int[] widgets = getAppWidgets();
- if (widgets == null) {
- return false;
- }
- if (index < 0 || index > widgets.length) {
- return false;
- }
- int[] newWidgets = new int[widgets.length + 1];
- for (int i = 0, j = 0; i < newWidgets.length; i++) {
- if (index == i) {
- newWidgets[i] = widgetId;
- i++;
- }
- if (i < newWidgets.length) {
- newWidgets[i] = widgets[j];
- j++;
- }
- }
- writeAppWidgets(newWidgets);
- return true;
- }
-
- public boolean removeAppWidget(int widgetId) {
- int[] widgets = getAppWidgets();
-
- if (widgets.length == 0) {
- return false;
- }
-
- int[] newWidgets = new int[widgets.length - 1];
- for (int i = 0, j = 0; i < widgets.length; i++) {
- if (widgets[i] == widgetId) {
- // continue...
- } else if (j >= newWidgets.length) {
- // we couldn't find the widget
- return false;
- } else {
- newWidgets[j] = widgets[i];
- j++;
- }
- }
- writeAppWidgets(newWidgets);
- return true;
- }
-
private long getLong(String secureSettingKey, long defaultValue, int userHandle) {
try {
return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle);
@@ -1489,19 +1097,6 @@ public class LockPatternUtils {
}
}
- private long getLong(String secureSettingKey, long defaultValue) {
- try {
- return getLockSettings().getLong(secureSettingKey, defaultValue,
- getCurrentOrCallingUserId());
- } catch (RemoteException re) {
- return defaultValue;
- }
- }
-
- private void setLong(String secureSettingKey, long value) {
- setLong(secureSettingKey, value, getCurrentOrCallingUserId());
- }
-
private void setLong(String secureSettingKey, long value, int userHandle) {
try {
getLockSettings().setLong(secureSettingKey, value, userHandle);
@@ -1511,10 +1106,6 @@ public class LockPatternUtils {
}
}
- private String getString(String secureSettingKey) {
- return getString(secureSettingKey, getCurrentOrCallingUserId());
- }
-
private String getString(String secureSettingKey, int userHandle) {
try {
return getLockSettings().getString(secureSettingKey, null, userHandle);
@@ -1532,134 +1123,13 @@ public class LockPatternUtils {
}
}
- public boolean isSecure() {
- return isSecure(getCurrentOrCallingUserId());
- }
-
- public boolean isSecure(int userId) {
- long mode = getKeyguardStoredPasswordQuality(userId);
- final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
- final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
- || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
- || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
- || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
- final boolean secure =
- isPattern && isLockPatternEnabled(userId) && savedPatternExists(userId)
- || isPassword && savedPasswordExists(userId);
- return secure;
- }
-
- /**
- * Sets the emergency button visibility based on isEmergencyCallCapable().
- *
- * If the emergency button is visible, sets the text on the emergency button
- * to indicate what action will be taken.
- *
- * If there's currently a call in progress, the button will take them to the call
- * @param button The button to update
- * @param shown Indicates whether the given screen wants the emergency button to show at all
- * @param showIcon Indicates whether to show a phone icon for the button.
- */
- public void updateEmergencyCallButtonState(Button button, boolean shown, boolean showIcon) {
- if (isEmergencyCallCapable() && shown) {
- button.setVisibility(View.VISIBLE);
- } else {
- button.setVisibility(View.GONE);
- return;
- }
-
- int textId;
- if (isInCall()) {
- // show "return to call" text and show phone icon
- textId = R.string.lockscreen_return_to_call;
- int phoneCallIcon = showIcon ? R.drawable.stat_sys_phone_call : 0;
- button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
- } else {
- textId = R.string.lockscreen_emergency_call;
- int emergencyIcon = showIcon ? R.drawable.ic_emergency : 0;
- button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
- }
- button.setText(textId);
- }
-
- /**
- * Resumes a call in progress. Typically launched from the EmergencyCall button
- * on various lockscreens.
- */
- public void resumeCall() {
- getTelecommManager().showInCallScreen(false);
- }
-
- /**
- * @return {@code true} if there is a call currently in progress, {@code false} otherwise.
- */
- public boolean isInCall() {
- return getTelecommManager().isInCall();
- }
-
- private TelecomManager getTelecommManager() {
- return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- }
-
- private void finishBiometricWeak(int userId) {
- setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true, userId);
-
- // Launch intent to show final screen, this also
- // moves the temporary gallery to the actual gallery
- Intent intent = new Intent();
- intent.setClassName("com.android.facelock",
- "com.android.facelock.SetupEndScreen");
- mContext.startActivityAsUser(intent, new UserHandle(userId));
- }
-
public void setPowerButtonInstantlyLocks(boolean enabled) {
- setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled);
+ setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled, getCurrentOrCallingUserId());
}
public boolean getPowerButtonInstantlyLocks() {
- return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true);
- }
-
- public static boolean isSafeModeEnabled() {
- try {
- return IWindowManager.Stub.asInterface(
- ServiceManager.getService("window")).isSafeModeEnabled();
- } catch (RemoteException e) {
- // Shouldn't happen!
- }
- return false;
- }
-
- /**
- * Determine whether the user has selected any non-system widgets in keyguard
- *
- * @return true if widgets have been selected
- */
- public boolean hasWidgetsEnabledInKeyguard(int userid) {
- int widgets[] = getAppWidgets(userid);
- for (int i = 0; i < widgets.length; i++) {
- if (widgets[i] > 0) {
- return true;
- }
- }
- return false;
- }
-
- public boolean getWidgetsEnabled() {
- return getWidgetsEnabled(getCurrentOrCallingUserId());
- }
-
- public boolean getWidgetsEnabled(int userId) {
- return getBoolean(LOCKSCREEN_WIDGETS_ENABLED, false, userId);
- }
-
- public void setWidgetsEnabled(boolean enabled) {
- setWidgetsEnabled(enabled, getCurrentOrCallingUserId());
- }
-
- public void setWidgetsEnabled(boolean enabled, int userId) {
- setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId);
+ return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true,
+ getCurrentOrCallingUserId());
}
public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) {
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 9fa6882..52bbabf 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -65,8 +65,8 @@ public class LockPatternView extends View {
private boolean mDrawingProfilingStarted = false;
- private Paint mPaint = new Paint();
- private Paint mPathPaint = new Paint();
+ private final Paint mPaint = new Paint();
+ private final Paint mPathPaint = new Paint();
/**
* How many milliseconds we spend animating each circle of a lock pattern
@@ -82,7 +82,7 @@ public class LockPatternView extends View {
private static final float DRAG_THRESHHOLD = 0.0f;
private OnPatternListener mOnPatternListener;
- private ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
+ private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
/**
* Lookup table for the circles of the pattern we are currently drawing.
@@ -90,7 +90,7 @@ public class LockPatternView extends View {
* in which case we use this to hold the cells we are drawing for the in
* progress animation.
*/
- private boolean[][] mPatternDrawLookup = new boolean[3][3];
+ private final boolean[][] mPatternDrawLookup = new boolean[3][3];
/**
* the in progress point:
@@ -122,24 +122,27 @@ public class LockPatternView extends View {
private int mErrorColor;
private int mSuccessColor;
- private Interpolator mFastOutSlowInInterpolator;
- private Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mLinearOutSlowInInterpolator;
/**
* Represents a cell in the 3 X 3 matrix of the unlock pattern view.
*/
- public static class Cell {
- int row;
- int column;
+ public static final class Cell {
+ final int row;
+ final int column;
// keep # objects limited to 9
- static Cell[][] sCells = new Cell[3][3];
- static {
+ private static final Cell[][] sCells = createCells();
+
+ private static Cell[][] createCells() {
+ Cell[][] res = new Cell[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
- sCells[i][j] = new Cell(i, j);
+ res[i][j] = new Cell(i, j);
}
}
+ return res;
}
/**
@@ -160,11 +163,7 @@ public class LockPatternView extends View {
return column;
}
- /**
- * @param row The row of the cell.
- * @param column The column of the cell.
- */
- public static synchronized Cell of(int row, int column) {
+ public static Cell of(int row, int column) {
checkRange(row, column);
return sCells[row][column];
}
@@ -178,6 +177,7 @@ public class LockPatternView extends View {
}
}
+ @Override
public String toString() {
return "(row=" + row + ",clmn=" + column + ")";
}
@@ -722,7 +722,7 @@ public class LockPatternView extends View {
handleActionDown(event);
return true;
case MotionEvent.ACTION_UP:
- handleActionUp(event);
+ handleActionUp();
return true;
case MotionEvent.ACTION_MOVE:
handleActionMove(event);
@@ -812,7 +812,7 @@ public class LockPatternView extends View {
announceForAccessibility(mContext.getString(resId));
}
- private void handleActionUp(MotionEvent event) {
+ private void handleActionUp() {
// report pattern detected
if (!mPattern.isEmpty()) {
mPatternInProgress = false;
@@ -1119,12 +1119,15 @@ public class LockPatternView extends View {
dest.writeValue(mTactileFeedbackEnabled);
}
+ @SuppressWarnings({ "unused", "hiding" }) // Found using reflection
public static final Parcelable.Creator<SavedState> CREATOR =
new Creator<SavedState>() {
+ @Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
+ @Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 4e48454..01e835b 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -593,15 +593,13 @@ public class ResolverDrawerLayout extends ViewGroup {
}
@Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- event.setClassName(ResolverDrawerLayout.class.getName());
+ public CharSequence getAccessibilityClassName() {
+ return ResolverDrawerLayout.class.getName();
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
- info.setClassName(ResolverDrawerLayout.class.getName());
if (isEnabled()) {
if (mCollapseOffset != 0) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index d6bd1d6..825fcad 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -150,7 +150,9 @@ public class ScrollingTabContainerView extends HorizontalScrollView
addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT));
if (mTabSpinner.getAdapter() == null) {
- mTabSpinner.setAdapter(new TabAdapter());
+ final TabAdapter adapter = new TabAdapter(mContext);
+ adapter.setDropDownViewContext(mTabSpinner.getPopupContext());
+ mTabSpinner.setAdapter(adapter);
}
if (mTabSelector != null) {
removeCallbacks(mTabSelector);
@@ -276,8 +278,8 @@ public class ScrollingTabContainerView extends HorizontalScrollView
}
}
- private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
- final TabView tabView = new TabView(getContext(), tab, forAdapter);
+ private TabView createTabView(Context context, ActionBar.Tab tab, boolean forAdapter) {
+ final TabView tabView = new TabView(context, tab, forAdapter);
if (forAdapter) {
tabView.setBackgroundDrawable(null);
tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT,
@@ -294,7 +296,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView
}
public void addTab(ActionBar.Tab tab, boolean setSelected) {
- TabView tabView = createTabView(tab, false);
+ TabView tabView = createTabView(mContext, tab, false);
mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
LayoutParams.MATCH_PARENT, 1));
if (mTabSpinner != null) {
@@ -309,7 +311,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView
}
public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
- final TabView tabView = createTabView(tab, false);
+ final TabView tabView = createTabView(mContext, tab, false);
mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams(
0, LayoutParams.MATCH_PARENT, 1));
if (mTabSpinner != null) {
@@ -391,17 +393,9 @@ public class ScrollingTabContainerView extends HorizontalScrollView
}
@Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
+ public CharSequence getAccessibilityClassName() {
// This view masquerades as an action bar tab.
- event.setClassName(ActionBar.Tab.class.getName());
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- // This view masquerades as an action bar tab.
- info.setClassName(ActionBar.Tab.class.getName());
+ return ActionBar.Tab.class.getName();
}
@Override
@@ -514,6 +508,16 @@ public class ScrollingTabContainerView extends HorizontalScrollView
}
private class TabAdapter extends BaseAdapter {
+ private Context mDropDownContext;
+
+ public TabAdapter(Context context) {
+ setDropDownViewContext(context);
+ }
+
+ public void setDropDownViewContext(Context context) {
+ mDropDownContext = context;
+ }
+
@Override
public int getCount() {
return mTabLayout.getChildCount();
@@ -532,7 +536,18 @@ public class ScrollingTabContainerView extends HorizontalScrollView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
- convertView = createTabView((ActionBar.Tab) getItem(position), true);
+ convertView = createTabView(mContext, (ActionBar.Tab) getItem(position), true);
+ } else {
+ ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
+ }
+ return convertView;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = createTabView(mDropDownContext,
+ (ActionBar.Tab) getItem(position), true);
} else {
((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
}
diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
deleted file mode 100644
index 5f3c5f9..0000000
--- a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget;
-
-import java.lang.Math;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.StateSet;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.widget.RemoteViews.RemoteView;
-
-/**
- * A layout that switches between its children based on the requested layout height.
- * Each child specifies its minimum and maximum valid height. Results are undefined
- * if children specify overlapping ranges. A child may specify the maximum height
- * as 'unbounded' to indicate that it is willing to be displayed arbitrarily tall.
- *
- * <p>
- * See {@link SizeAdaptiveLayout.LayoutParams} for a full description of the
- * layout parameters used by SizeAdaptiveLayout.
- */
-@RemoteView
-public class SizeAdaptiveLayout extends ViewGroup {
-
- private static final String TAG = "SizeAdaptiveLayout";
- private static final boolean DEBUG = false;
- private static final boolean REPORT_BAD_BOUNDS = true;
- private static final long CROSSFADE_TIME = 250;
-
- // TypedArray indices
- private static final int MIN_VALID_HEIGHT =
- R.styleable.SizeAdaptiveLayout_Layout_layout_minHeight;
- private static final int MAX_VALID_HEIGHT =
- R.styleable.SizeAdaptiveLayout_Layout_layout_maxHeight;
-
- // view state
- private View mActiveChild;
- private View mLastActive;
-
- // animation state
- private AnimatorSet mTransitionAnimation;
- private AnimatorListener mAnimatorListener;
- private ObjectAnimator mFadePanel;
- private ObjectAnimator mFadeView;
- private int mCanceledAnimationCount;
- private View mEnteringView;
- private View mLeavingView;
- // View used to hide larger views under smaller ones to create a uniform crossfade
- private View mModestyPanel;
- private int mModestyPanelTop;
-
- public SizeAdaptiveLayout(Context context) {
- this(context, null);
- }
-
- public SizeAdaptiveLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SizeAdaptiveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public SizeAdaptiveLayout(
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- initialize();
- }
-
- private void initialize() {
- mModestyPanel = new View(getContext());
- // If the SizeAdaptiveLayout has a solid background, use it as a transition hint.
- Drawable background = getBackground();
- if (background instanceof StateListDrawable) {
- StateListDrawable sld = (StateListDrawable) background;
- sld.setState(StateSet.WILD_CARD);
- background = sld.getCurrent();
- }
- if (background instanceof ColorDrawable) {
- mModestyPanel.setBackgroundDrawable(background);
- }
- SizeAdaptiveLayout.LayoutParams layout =
- new SizeAdaptiveLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- mModestyPanel.setLayoutParams(layout);
- addView(mModestyPanel);
- mFadePanel = ObjectAnimator.ofFloat(mModestyPanel, "alpha", 0f);
- mFadeView = ObjectAnimator.ofFloat(null, "alpha", 0f);
- mAnimatorListener = new BringToFrontOnEnd();
- mTransitionAnimation = new AnimatorSet();
- mTransitionAnimation.play(mFadeView).with(mFadePanel);
- mTransitionAnimation.setDuration(CROSSFADE_TIME);
- mTransitionAnimation.addListener(mAnimatorListener);
- }
-
- /**
- * Visible for testing
- * @hide
- */
- public Animator getTransitionAnimation() {
- return mTransitionAnimation;
- }
-
- /**
- * Visible for testing
- * @hide
- */
- public View getModestyPanel() {
- return mModestyPanel;
- }
-
- @Override
- public void onAttachedToWindow() {
- mLastActive = null;
- // make sure all views start off invisible.
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (DEBUG) Log.d(TAG, this + " measure spec: " +
- MeasureSpec.toString(heightMeasureSpec));
- View model = selectActiveChild(heightMeasureSpec);
- if (model == null) {
- setMeasuredDimension(0, 0);
- return;
- }
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) model.getLayoutParams();
- if (DEBUG) Log.d(TAG, "active min: " + lp.minHeight + " max: " + lp.maxHeight);
- measureChild(model, widthMeasureSpec, heightMeasureSpec);
- int childHeight = model.getMeasuredHeight();
- int childWidth = model.getMeasuredHeight();
- int childState = combineMeasuredStates(0, model.getMeasuredState());
- if (DEBUG) Log.d(TAG, "measured child at: " + childHeight);
- int resolvedWidth = resolveSizeAndState(childWidth, widthMeasureSpec, childState);
- int resolvedHeight = resolveSizeAndState(childHeight, heightMeasureSpec, childState);
- if (DEBUG) Log.d(TAG, "resolved to: " + resolvedHeight);
- int boundedHeight = clampSizeToBounds(resolvedHeight, model);
- if (DEBUG) Log.d(TAG, "bounded to: " + boundedHeight);
- setMeasuredDimension(resolvedWidth, boundedHeight);
- }
-
- private int clampSizeToBounds(int measuredHeight, View child) {
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
- int heightIn = View.MEASURED_SIZE_MASK & measuredHeight;
- int height = Math.max(heightIn, lp.minHeight);
- if (lp.maxHeight != SizeAdaptiveLayout.LayoutParams.UNBOUNDED) {
- height = Math.min(height, lp.maxHeight);
- }
-
- if (REPORT_BAD_BOUNDS && heightIn != height) {
- Log.d(TAG, this + "child view " + child + " " +
- "measured out of bounds at " + heightIn +"px " +
- "clamped to " + height + "px");
- }
-
- return height;
- }
-
- //TODO extend to width and height
- private View selectActiveChild(int heightMeasureSpec) {
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
- View unboundedView = null;
- View tallestView = null;
- int tallestViewSize = 0;
- View smallestView = null;
- int smallestViewSize = Integer.MAX_VALUE;
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child != mModestyPanel) {
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
- if (DEBUG) Log.d(TAG, "looking at " + i +
- " with min: " + lp.minHeight +
- " max: " + lp.maxHeight);
- if (lp.maxHeight == SizeAdaptiveLayout.LayoutParams.UNBOUNDED &&
- unboundedView == null) {
- unboundedView = child;
- }
- if (lp.maxHeight > tallestViewSize) {
- tallestViewSize = lp.maxHeight;
- tallestView = child;
- }
- if (lp.minHeight < smallestViewSize) {
- smallestViewSize = lp.minHeight;
- smallestView = child;
- }
- if (heightMode != MeasureSpec.UNSPECIFIED &&
- heightSize >= lp.minHeight && heightSize <= lp.maxHeight) {
- if (DEBUG) Log.d(TAG, " found exact match, finishing early");
- return child;
- }
- }
- }
- if (unboundedView != null) {
- tallestView = unboundedView;
- }
- if (heightMode == MeasureSpec.UNSPECIFIED || heightSize > tallestViewSize) {
- return tallestView;
- } else {
- return smallestView;
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (DEBUG) Log.d(TAG, this + " onlayout height: " + (bottom - top));
- mLastActive = mActiveChild;
- int measureSpec = View.MeasureSpec.makeMeasureSpec(bottom - top,
- View.MeasureSpec.EXACTLY);
- mActiveChild = selectActiveChild(measureSpec);
- if (mActiveChild == null) return;
-
- mActiveChild.setVisibility(View.VISIBLE);
-
- if (mLastActive != mActiveChild && mLastActive != null) {
- if (DEBUG) Log.d(TAG, this + " changed children from: " + mLastActive +
- " to: " + mActiveChild);
-
- mEnteringView = mActiveChild;
- mLeavingView = mLastActive;
-
- mEnteringView.setAlpha(1f);
-
- mModestyPanel.setAlpha(1f);
- mModestyPanel.bringToFront();
- mModestyPanelTop = mLeavingView.getHeight();
- mModestyPanel.setVisibility(View.VISIBLE);
- // TODO: mModestyPanel background should be compatible with mLeavingView
-
- mLeavingView.bringToFront();
-
- if (mTransitionAnimation.isRunning()) {
- mTransitionAnimation.cancel();
- }
- mFadeView.setTarget(mLeavingView);
- mFadeView.setFloatValues(0f);
- mFadePanel.setFloatValues(0f);
- mTransitionAnimation.setupStartValues();
- mTransitionAnimation.start();
- }
- final int childWidth = mActiveChild.getMeasuredWidth();
- final int childHeight = mActiveChild.getMeasuredHeight();
- // TODO investigate setting LAYER_TYPE_HARDWARE on mLastActive
- mActiveChild.layout(0, 0, childWidth, childHeight);
-
- if (DEBUG) Log.d(TAG, "got modesty offset of " + mModestyPanelTop);
- mModestyPanel.layout(0, mModestyPanelTop, childWidth, mModestyPanelTop + childHeight);
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- if (DEBUG) Log.d(TAG, "generate layout from attrs");
- return new SizeAdaptiveLayout.LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- if (DEBUG) Log.d(TAG, "generate default layout from viewgroup");
- return new SizeAdaptiveLayout.LayoutParams(p);
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- if (DEBUG) Log.d(TAG, "generate default layout from null");
- return new SizeAdaptiveLayout.LayoutParams();
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof SizeAdaptiveLayout.LayoutParams;
- }
-
- /**
- * Per-child layout information associated with ViewSizeAdaptiveLayout.
- *
- * TODO extend to width and height
- *
- * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_minHeight
- * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_maxHeight
- */
- public static class LayoutParams extends ViewGroup.LayoutParams {
-
- /**
- * Indicates the minimum valid height for the child.
- */
- @ViewDebug.ExportedProperty(category = "layout")
- public int minHeight;
-
- /**
- * Indicates the maximum valid height for the child.
- */
- @ViewDebug.ExportedProperty(category = "layout")
- public int maxHeight;
-
- /**
- * Constant value for maxHeight that indicates there is not maximum height.
- */
- public static final int UNBOUNDED = -1;
-
- /**
- * {@inheritDoc}
- */
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- if (DEBUG) {
- Log.d(TAG, "construct layout from attrs");
- for (int i = 0; i < attrs.getAttributeCount(); i++) {
- Log.d(TAG, " " + attrs.getAttributeName(i) + " = " +
- attrs.getAttributeValue(i));
- }
- }
- TypedArray a =
- c.obtainStyledAttributes(attrs,
- R.styleable.SizeAdaptiveLayout_Layout);
-
- minHeight = a.getDimensionPixelSize(MIN_VALID_HEIGHT, 0);
- if (DEBUG) Log.d(TAG, "got minHeight of: " + minHeight);
-
- try {
- maxHeight = a.getLayoutDimension(MAX_VALID_HEIGHT, UNBOUNDED);
- if (DEBUG) Log.d(TAG, "got maxHeight of: " + maxHeight);
- } catch (Exception e) {
- if (DEBUG) Log.d(TAG, "caught exception looking for maxValidHeight " + e);
- }
-
- a.recycle();
- }
-
- /**
- * Creates a new set of layout parameters with the specified width, height
- * and valid height bounds.
- *
- * @param width the width, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
- * @param height the height, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
- * @param minHeight the minimum height of this child
- * @param maxHeight the maximum height of this child
- * or {@link #UNBOUNDED} if the child can grow forever
- */
- public LayoutParams(int width, int height, int minHeight, int maxHeight) {
- super(width, height);
- this.minHeight = minHeight;
- this.maxHeight = maxHeight;
- }
-
- /**
- * {@inheritDoc}
- */
- public LayoutParams(int width, int height) {
- this(width, height, UNBOUNDED, UNBOUNDED);
- }
-
- /**
- * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
- */
- public LayoutParams() {
- this(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- }
-
- /**
- * {@inheritDoc}
- */
- public LayoutParams(ViewGroup.LayoutParams p) {
- super(p);
- minHeight = UNBOUNDED;
- maxHeight = UNBOUNDED;
- }
-
- public String debug(String output) {
- return output + "SizeAdaptiveLayout.LayoutParams={" +
- ", max=" + maxHeight +
- ", max=" + minHeight + "}";
- }
- }
-
- class BringToFrontOnEnd implements AnimatorListener {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCanceledAnimationCount == 0) {
- mLeavingView.setVisibility(View.GONE);
- mModestyPanel.setVisibility(View.GONE);
- mEnteringView.bringToFront();
- mEnteringView = null;
- mLeavingView = null;
- } else {
- mCanceledAnimationCount--;
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCanceledAnimationCount++;
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- if (DEBUG) Log.d(TAG, "fade animation repeated: should never happen.");
- assert(false);
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- }
- }
-}
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
deleted file mode 100644
index 9e7a649..0000000
--- a/core/java/com/android/internal/widget/WaveView.java
+++ /dev/null
@@ -1,663 +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.internal.widget;
-
-import java.util.ArrayList;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.media.AudioAttributes;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.R;
-
-/**
- * A special widget containing a center and outer ring. Moving the center ring to the outer ring
- * causes an event that can be caught by implementing OnTriggerListener.
- */
-public class WaveView extends View implements ValueAnimator.AnimatorUpdateListener {
- private static final String TAG = "WaveView";
- private static final boolean DBG = false;
- private static final int WAVE_COUNT = 20; // default wave count
- private static final long VIBRATE_SHORT = 20; // msec
- private static final long VIBRATE_LONG = 20; // msec
-
- // Lock state machine states
- private static final int STATE_RESET_LOCK = 0;
- private static final int STATE_READY = 1;
- private static final int STATE_START_ATTEMPT = 2;
- private static final int STATE_ATTEMPTING = 3;
- private static final int STATE_UNLOCK_ATTEMPT = 4;
- private static final int STATE_UNLOCK_SUCCESS = 5;
-
- // Animation properties.
- private static final long DURATION = 300; // duration of transitional animations
- private static final long FINAL_DURATION = 200; // duration of final animations when unlocking
- private static final long RING_DELAY = 1300; // when to start fading animated rings
- private static final long FINAL_DELAY = 200; // delay for unlock success animation
- private static final long SHORT_DELAY = 100; // for starting one animation after another.
- private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay
- private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset
- private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion
- private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking
- private static final long WAVE_DELAY = WAVE_DURATION / WAVE_COUNT; // initial propagation delay
-
- /**
- * The scale by which to multiply the unlock handle width to compute the radius
- * in which it can be grabbed when accessibility is disabled.
- */
- private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED = 0.5f;
-
- /**
- * The scale by which to multiply the unlock handle width to compute the radius
- * in which it can be grabbed when accessibility is enabled (more generous).
- */
- private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.0f;
-
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
- private Vibrator mVibrator;
- private OnTriggerListener mOnTriggerListener;
- private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3);
- private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT);
- private boolean mFingerDown = false;
- private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it
- private int mSnapRadius = 136; // minimum threshold for drag unlock
- private int mWaveCount = WAVE_COUNT; // number of waves
- private long mWaveTimerDelay = WAVE_DELAY;
- private int mCurrentWave = 0;
- private float mLockCenterX; // center of widget as dictated by widget size
- private float mLockCenterY;
- private float mMouseX; // current mouse position as of last touch event
- private float mMouseY;
- private DrawableHolder mUnlockRing;
- private DrawableHolder mUnlockDefault;
- private DrawableHolder mUnlockHalo;
- private int mLockState = STATE_RESET_LOCK;
- private int mGrabbedState = OnTriggerListener.NO_HANDLE;
- private boolean mWavesRunning;
- private boolean mFinishWaves;
-
- public WaveView(Context context) {
- this(context, null);
- }
-
- public WaveView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
- // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL);
- // a.recycle();
-
- initDrawables();
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mLockCenterX = 0.5f * w;
- mLockCenterY = 0.5f * h;
- super.onSizeChanged(w, h, oldw, oldh);
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // View should be large enough to contain the unlock ring + halo
- return mUnlockRing.getWidth() + mUnlockHalo.getWidth();
- }
-
- @Override
- protected int getSuggestedMinimumHeight() {
- // View should be large enough to contain the unlock ring + halo
- return mUnlockRing.getHeight() + mUnlockHalo.getHeight();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
- int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- int width;
- int height;
-
- if (widthSpecMode == MeasureSpec.AT_MOST) {
- width = Math.min(widthSpecSize, getSuggestedMinimumWidth());
- } else if (widthSpecMode == MeasureSpec.EXACTLY) {
- width = widthSpecSize;
- } else {
- width = getSuggestedMinimumWidth();
- }
-
- if (heightSpecMode == MeasureSpec.AT_MOST) {
- height = Math.min(heightSpecSize, getSuggestedMinimumWidth());
- } else if (heightSpecMode == MeasureSpec.EXACTLY) {
- height = heightSpecSize;
- } else {
- height = getSuggestedMinimumHeight();
- }
-
- setMeasuredDimension(width, height);
- }
-
- private void initDrawables() {
- mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring));
- mUnlockRing.setX(mLockCenterX);
- mUnlockRing.setY(mLockCenterY);
- mUnlockRing.setScaleX(0.1f);
- mUnlockRing.setScaleY(0.1f);
- mUnlockRing.setAlpha(0.0f);
- mDrawables.add(mUnlockRing);
-
- mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default));
- mUnlockDefault.setX(mLockCenterX);
- mUnlockDefault.setY(mLockCenterY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
- mDrawables.add(mUnlockDefault);
-
- mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo));
- mUnlockHalo.setX(mLockCenterX);
- mUnlockHalo.setY(mLockCenterY);
- mUnlockHalo.setScaleX(0.1f);
- mUnlockHalo.setScaleY(0.1f);
- mUnlockHalo.setAlpha(0.0f);
- mDrawables.add(mUnlockHalo);
-
- BitmapDrawable wave = createDrawable(R.drawable.unlock_wave);
- for (int i = 0; i < mWaveCount; i++) {
- DrawableHolder holder = new DrawableHolder(wave);
- mLightWaves.add(holder);
- holder.setAlpha(0.0f);
- }
- }
-
- private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) {
- double distX = mouseX - mLockCenterX;
- double distY = mouseY - mLockCenterY;
- int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
- double touchA = Math.atan2(distX, distY);
- float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA));
- float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA));
-
- switch (mLockState) {
- case STATE_RESET_LOCK:
- if (DBG) Log.v(TAG, "State RESET_LOCK");
- mWaveTimerDelay = WAVE_DELAY;
- for (int i = 0; i < mLightWaves.size(); i++) {
- DrawableHolder holder = mLightWaves.get(i);
- holder.addAnimTo(300, 0, "alpha", 0.0f, false);
- }
- for (int i = 0; i < mLightWaves.size(); i++) {
- mLightWaves.get(i).startAnimations(this);
- }
-
- mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true);
- mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true);
- mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true);
-
- mUnlockDefault.removeAnimationFor("x");
- mUnlockDefault.removeAnimationFor("y");
- mUnlockDefault.removeAnimationFor("scaleX");
- mUnlockDefault.removeAnimationFor("scaleY");
- mUnlockDefault.removeAnimationFor("alpha");
- mUnlockDefault.setX(mLockCenterX);
- mUnlockDefault.setY(mLockCenterY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
-
- mUnlockHalo.removeAnimationFor("x");
- mUnlockHalo.removeAnimationFor("y");
- mUnlockHalo.removeAnimationFor("scaleX");
- mUnlockHalo.removeAnimationFor("scaleY");
- mUnlockHalo.removeAnimationFor("alpha");
- mUnlockHalo.setX(mLockCenterX);
- mUnlockHalo.setY(mLockCenterY);
- mUnlockHalo.setScaleX(0.1f);
- mUnlockHalo.setScaleY(0.1f);
- mUnlockHalo.setAlpha(0.0f);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
-
- removeCallbacks(mLockTimerActions);
-
- mLockState = STATE_READY;
- break;
-
- case STATE_READY:
- if (DBG) Log.v(TAG, "State READY");
- mWaveTimerDelay = WAVE_DELAY;
- break;
-
- case STATE_START_ATTEMPT:
- if (DBG) Log.v(TAG, "State START_ATTEMPT");
- mUnlockDefault.removeAnimationFor("x");
- mUnlockDefault.removeAnimationFor("y");
- mUnlockDefault.removeAnimationFor("scaleX");
- mUnlockDefault.removeAnimationFor("scaleY");
- mUnlockDefault.removeAnimationFor("alpha");
- mUnlockDefault.setX(mLockCenterX + 182);
- mUnlockDefault.setY(mLockCenterY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
-
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false);
-
- mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true);
-
- mLockState = STATE_ATTEMPTING;
- break;
-
- case STATE_ATTEMPTING:
- if (DBG) Log.v(TAG, "State ATTEMPTING (fingerDown = " + fingerDown + ")");
- if (dragDistance > mSnapRadius) {
- mFinishWaves = true; // don't start any more waves.
- if (fingerDown) {
- mUnlockHalo.addAnimTo(0, 0, "x", ringX, true);
- mUnlockHalo.addAnimTo(0, 0, "y", ringY, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
- } else {
- if (DBG) Log.v(TAG, "up detected, moving to STATE_UNLOCK_ATTEMPT");
- mLockState = STATE_UNLOCK_ATTEMPT;
- }
- } else {
- // If waves have stopped, we need to kick them off again...
- if (!mWavesRunning) {
- mWavesRunning = true;
- mFinishWaves = false;
- // mWaveTimerDelay = WAVE_DELAY;
- postDelayed(mAddWaveAction, mWaveTimerDelay);
- }
- mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true);
- mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
- }
- break;
-
- case STATE_UNLOCK_ATTEMPT:
- if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT");
- if (dragDistance > mSnapRadius) {
- for (int n = 0; n < mLightWaves.size(); n++) {
- DrawableHolder wave = mLightWaves.get(n);
- long delay = 1000L*(6 + n - mCurrentWave)/10L;
- wave.addAnimTo(FINAL_DURATION, delay, "x", ringX, true);
- wave.addAnimTo(FINAL_DURATION, delay, "y", ringY, true);
- wave.addAnimTo(FINAL_DURATION, delay, "scaleX", 0.1f, true);
- wave.addAnimTo(FINAL_DURATION, delay, "scaleY", 0.1f, true);
- wave.addAnimTo(FINAL_DURATION, delay, "alpha", 0.0f, true);
- }
- for (int i = 0; i < mLightWaves.size(); i++) {
- mLightWaves.get(i).startAnimations(this);
- }
-
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleX", 0.1f, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleY", 0.1f, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "alpha", 0.0f, false);
-
- mUnlockRing.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
- mUnlockDefault.removeAnimationFor("x");
- mUnlockDefault.removeAnimationFor("y");
- mUnlockDefault.removeAnimationFor("scaleX");
- mUnlockDefault.removeAnimationFor("scaleY");
- mUnlockDefault.removeAnimationFor("alpha");
- mUnlockDefault.setX(ringX);
- mUnlockDefault.setY(ringY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
-
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "x", ringX, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "y", ringY, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleX", 1.0f, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleY", 1.0f, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "alpha", 1.0f, true);
-
- mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
- mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
- mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
- mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
- mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
-
- mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
- mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
- mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
- removeCallbacks(mLockTimerActions);
-
- postDelayed(mLockTimerActions, RESET_TIMEOUT);
-
- dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE);
- mLockState = STATE_UNLOCK_SUCCESS;
- } else {
- mLockState = STATE_RESET_LOCK;
- }
- break;
-
- case STATE_UNLOCK_SUCCESS:
- if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS");
- removeCallbacks(mAddWaveAction);
- break;
-
- default:
- if (DBG) Log.v(TAG, "Unknown state " + mLockState);
- break;
- }
- mUnlockDefault.startAnimations(this);
- mUnlockHalo.startAnimations(this);
- mUnlockRing.startAnimations(this);
- }
-
- BitmapDrawable createDrawable(int resId) {
- Resources res = getResources();
- Bitmap bitmap = BitmapFactory.decodeResource(res, resId);
- return new BitmapDrawable(res, bitmap);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
- for (int i = 0; i < mDrawables.size(); ++i) {
- mDrawables.get(i).draw(canvas);
- }
- for (int i = 0; i < mLightWaves.size(); ++i) {
- mLightWaves.get(i).draw(canvas);
- }
- }
-
- private final Runnable mLockTimerActions = new Runnable() {
- public void run() {
- if (DBG) Log.v(TAG, "LockTimerActions");
- // reset lock after inactivity
- if (mLockState == STATE_ATTEMPTING) {
- if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK");
- mLockState = STATE_RESET_LOCK;
- }
- // for prototype, reset after successful unlock
- if (mLockState == STATE_UNLOCK_SUCCESS) {
- if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK after success");
- mLockState = STATE_RESET_LOCK;
- }
- invalidate();
- }
- };
-
- private final Runnable mAddWaveAction = new Runnable() {
- public void run() {
- double distX = mMouseX - mLockCenterX;
- double distY = mMouseY - mLockCenterY;
- int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
- if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius
- && mWaveTimerDelay >= WAVE_DELAY) {
- mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT);
-
- DrawableHolder wave = mLightWaves.get(mCurrentWave);
- wave.setAlpha(0.0f);
- wave.setScaleX(0.2f);
- wave.setScaleY(0.2f);
- wave.setX(mMouseX);
- wave.setY(mMouseY);
-
- wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true);
- wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true);
- wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true);
- wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true);
- wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true);
-
- wave.addAnimTo(1000, RING_DELAY, "alpha", 0.0f, false);
- wave.startAnimations(WaveView.this);
-
- mCurrentWave = (mCurrentWave+1) % mWaveCount;
- if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay);
- } else {
- mWaveTimerDelay += DELAY_INCREMENT2;
- }
- if (mFinishWaves) {
- // sentinel used to restart the waves after they've stopped
- mWavesRunning = false;
- } else {
- postDelayed(mAddWaveAction, mWaveTimerDelay);
- }
- }
- };
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER:
- event.setAction(MotionEvent.ACTION_DOWN);
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- event.setAction(MotionEvent.ACTION_MOVE);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- event.setAction(MotionEvent.ACTION_UP);
- break;
- }
- onTouchEvent(event);
- event.setAction(action);
- }
- return super.onHoverEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getAction();
- mMouseX = event.getX();
- mMouseY = event.getY();
- boolean handled = false;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- removeCallbacks(mLockTimerActions);
- mFingerDown = true;
- tryTransitionToStartAttemptState(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- tryTransitionToStartAttemptState(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_UP:
- if (DBG) Log.v(TAG, "ACTION_UP");
- mFingerDown = false;
- postDelayed(mLockTimerActions, RESET_TIMEOUT);
- setGrabbedState(OnTriggerListener.NO_HANDLE);
- // Normally the state machine is driven by user interaction causing redraws.
- // However, when there's no more user interaction and no running animations,
- // the state machine stops advancing because onDraw() never gets called.
- // The following ensures we advance to the next state in this case,
- // either STATE_UNLOCK_ATTEMPT or STATE_RESET_LOCK.
- waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
- handled = true;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- mFingerDown = false;
- handled = true;
- break;
- }
- invalidate();
- return handled ? true : super.onTouchEvent(event);
- }
-
- /**
- * Tries to transition to start attempt state.
- *
- * @param event A motion event.
- */
- private void tryTransitionToStartAttemptState(MotionEvent event) {
- final float dx = event.getX() - mUnlockHalo.getX();
- final float dy = event.getY() - mUnlockHalo.getY();
- float dist = (float) Math.hypot(dx, dy);
- if (dist <= getScaledGrabHandleRadius()) {
- setGrabbedState(OnTriggerListener.CENTER_HANDLE);
- if (mLockState == STATE_READY) {
- mLockState = STATE_START_ATTEMPT;
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- announceUnlockHandle();
- }
- }
- }
- }
-
- /**
- * @return The radius in which the handle is grabbed scaled based on
- * whether accessibility is enabled.
- */
- private float getScaledGrabHandleRadius() {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mUnlockHalo.getWidth();
- } else {
- return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED * mUnlockHalo.getWidth();
- }
- }
-
- /**
- * Announces the unlock handle if accessibility is enabled.
- */
- private void announceUnlockHandle() {
- setContentDescription(mContext.getString(R.string.description_target_unlock_tablet));
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- setContentDescription(null);
- }
-
- /**
- * Triggers haptic feedback.
- */
- private synchronized void vibrate(long duration) {
- final boolean hapticEnabled = Settings.System.getIntForUser(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
- if (hapticEnabled) {
- if (mVibrator == null) {
- mVibrator = (android.os.Vibrator) getContext()
- .getSystemService(Context.VIBRATOR_SERVICE);
- }
- mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES);
- }
- }
-
- /**
- * Registers a callback to be invoked when the user triggers an event.
- *
- * @param listener the OnDialTriggerListener to attach to this view
- */
- public void setOnTriggerListener(OnTriggerListener listener) {
- mOnTriggerListener = listener;
- }
-
- /**
- * Dispatches a trigger event to listener. Ignored if a listener is not set.
- * @param whichHandle the handle that triggered the event.
- */
- private void dispatchTriggerEvent(int whichHandle) {
- vibrate(VIBRATE_LONG);
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onTrigger(this, whichHandle);
- }
- }
-
- /**
- * Sets the current grabbed state, and dispatches a grabbed state change
- * event to our listener.
- */
- private void setGrabbedState(int newState) {
- if (newState != mGrabbedState) {
- mGrabbedState = newState;
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
- }
- }
- }
-
- public interface OnTriggerListener {
- /**
- * Sent when the user releases the handle.
- */
- public static final int NO_HANDLE = 0;
-
- /**
- * Sent when the user grabs the center handle
- */
- public static final int CENTER_HANDLE = 10;
-
- /**
- * Called when the user drags the center ring beyond a threshold.
- */
- void onTrigger(View v, int whichHandle);
-
- /**
- * Called when the "grabbed state" changes (i.e. when the user either grabs or releases
- * one of the handles.)
- *
- * @param v the view that was triggered
- * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE},
- */
- void onGrabbedStateChange(View v, int grabbedState);
- }
-
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
-
- public void reset() {
- if (DBG) Log.v(TAG, "reset() : resets state to STATE_RESET_LOCK");
- mLockState = STATE_RESET_LOCK;
- invalidate();
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/Ease.java b/core/java/com/android/internal/widget/multiwaveview/Ease.java
deleted file mode 100644
index 7f90c44..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/Ease.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2011 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.internal.widget.multiwaveview;
-
-import android.animation.TimeInterpolator;
-
-class Ease {
- private static final float DOMAIN = 1.0f;
- private static final float DURATION = 1.0f;
- private static final float START = 0.0f;
-
- static class Linear {
- public static final TimeInterpolator easeNone = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return input;
- }
- };
- }
-
- static class Cubic {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*((input=input/DURATION-1)*input*input + 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1.0f) ?
- (DOMAIN/2*input*input*input + START)
- : (DOMAIN/2*((input-=2)*input*input + 2) + START);
- }
- };
- }
-
- static class Quad {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation (float input) {
- return DOMAIN*(input/=DURATION)*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN *(input/=DURATION)*(input-2) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input + START)
- : (-DOMAIN/2 * ((--input)*(input-2) - 1) + START);
- }
- };
- }
-
- static class Quart {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN * ((input=input/DURATION-1)*input*input*input - 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input*input*input + START)
- : (-DOMAIN/2 * ((input-=2)*input*input*input - 2) + START);
- }
- };
- }
-
- static class Quint {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*((input=input/DURATION-1)*input*input*input*input + 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input*input*input*input + START)
- : (DOMAIN/2*((input-=2)*input*input*input*input + 2) + START);
- }
- };
- }
-
- static class Sine {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN * (float) Math.cos(input/DURATION * (Math.PI/2)) + DOMAIN + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN * (float) Math.sin(input/DURATION * (Math.PI/2)) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN/2 * ((float)Math.cos(Math.PI*input/DURATION) - 1.0f) + START;
- }
- };
- }
-
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
deleted file mode 100644
index 11ac19e..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ /dev/null
@@ -1,1383 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget.multiwaveview;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-
-/**
- * A re-usable widget containing a center, outer ring and wave animation.
- */
-public class GlowPadView extends View {
- private static final String TAG = "GlowPadView";
- private static final boolean DEBUG = false;
-
- // Wave state machine
- private static final int STATE_IDLE = 0;
- private static final int STATE_START = 1;
- private static final int STATE_FIRST_TOUCH = 2;
- private static final int STATE_TRACKING = 3;
- private static final int STATE_SNAP = 4;
- private static final int STATE_FINISH = 5;
-
- // Animation properties.
- private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
-
- public interface OnTriggerListener {
- int NO_HANDLE = 0;
- int CENTER_HANDLE = 1;
- public void onGrabbed(View v, int handle);
- public void onReleased(View v, int handle);
- public void onTrigger(View v, int target);
- public void onGrabbedStateChange(View v, int handle);
- public void onFinishFinalAnimation();
- }
-
- // Tuneable parameters for animation
- private static final int WAVE_ANIMATION_DURATION = 1000;
- private static final int RETURN_TO_HOME_DELAY = 1200;
- private static final int RETURN_TO_HOME_DURATION = 200;
- private static final int HIDE_ANIMATION_DELAY = 200;
- private static final int HIDE_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DELAY = 50;
- private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
- private static final int REVEAL_GLOW_DELAY = 0;
- private static final int REVEAL_GLOW_DURATION = 0;
-
- private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
- private static final float TARGET_SCALE_EXPANDED = 1.0f;
- private static final float TARGET_SCALE_COLLAPSED = 0.8f;
- private static final float RING_SCALE_EXPANDED = 1.0f;
- private static final float RING_SCALE_COLLAPSED = 0.5f;
-
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
- private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
- private AnimationBundle mWaveAnimations = new AnimationBundle();
- private AnimationBundle mTargetAnimations = new AnimationBundle();
- private AnimationBundle mGlowAnimations = new AnimationBundle();
- private ArrayList<String> mTargetDescriptions;
- private ArrayList<String> mDirectionDescriptions;
- private OnTriggerListener mOnTriggerListener;
- private TargetDrawable mHandleDrawable;
- private TargetDrawable mOuterRing;
- private Vibrator mVibrator;
-
- private int mFeedbackCount = 3;
- private int mVibrationDuration = 0;
- private int mGrabbedState;
- private int mActiveTarget = -1;
- private float mGlowRadius;
- private float mWaveCenterX;
- private float mWaveCenterY;
- private int mMaxTargetHeight;
- private int mMaxTargetWidth;
- private float mRingScaleFactor = 1f;
- private boolean mAllowScaling;
-
- private float mOuterRadius = 0.0f;
- private float mSnapMargin = 0.0f;
- private float mFirstItemOffset = 0.0f;
- private boolean mMagneticTargets = false;
- private boolean mDragging;
- private int mNewTargetResources;
-
- private class AnimationBundle extends ArrayList<Tweener> {
- private static final long serialVersionUID = 0xA84D78726F127468L;
- private boolean mSuspended;
-
- public void start() {
- if (mSuspended) return; // ignore attempts to start animations
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.start();
- }
- }
-
- public void cancel() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.cancel();
- }
- clear();
- }
-
- public void stop() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.end();
- }
- clear();
- }
-
- public void setSuspended(boolean suspend) {
- mSuspended = suspend;
- }
- };
-
- private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- ping();
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
- };
-
- private boolean mAnimatingTargets;
- private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- if (mNewTargetResources != 0) {
- internalSetTargetResources(mNewTargetResources);
- mNewTargetResources = 0;
- hideTargets(false, false);
- }
- mAnimatingTargets = false;
- }
- };
- private int mTargetResourceId;
- private int mTargetDescriptionsResourceId;
- private int mDirectionDescriptionsResourceId;
- private boolean mAlwaysTrackFinger;
- private int mHorizontalInset;
- private int mVerticalInset;
- private int mGravity = Gravity.TOP;
- private boolean mInitialLayout = true;
- private Tweener mBackgroundAnimator;
- private PointCloud mPointCloud;
- private float mInnerRadius;
- private int mPointerId;
-
- public GlowPadView(Context context) {
- this(context, null);
- }
-
- public GlowPadView(Context context, AttributeSet attrs) {
- super(context, attrs);
- Resources res = context.getResources();
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GlowPadView);
- mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius);
- mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius);
- mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin);
- mFirstItemOffset = (float) Math.toRadians(
- a.getFloat(R.styleable.GlowPadView_firstItemOffset,
- (float) Math.toDegrees(mFirstItemOffset)));
- mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration,
- mVibrationDuration);
- mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
- mFeedbackCount);
- mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false);
- TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable);
- mHandleDrawable = new TargetDrawable(res, handle != null ? handle.resourceId : 0);
- mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
- mOuterRing = new TargetDrawable(res,
- getResourceId(a, R.styleable.GlowPadView_outerRingDrawable));
-
- mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false);
- mMagneticTargets = a.getBoolean(R.styleable.GlowPadView_magneticTargets, mMagneticTargets);
-
- int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
- Drawable pointDrawable = pointId != 0 ? context.getDrawable(pointId) : null;
- mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f);
-
- mPointCloud = new PointCloud(pointDrawable);
- mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
- mPointCloud.glowManager.setRadius(mGlowRadius);
-
- TypedValue outValue = new TypedValue();
-
- // Read array of target drawables
- if (a.getValue(R.styleable.GlowPadView_targetDrawables, outValue)) {
- internalSetTargetResources(outValue.resourceId);
- }
- if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
- throw new IllegalStateException("Must specify at least one target drawable");
- }
-
- // Read array of target descriptions
- if (a.getValue(R.styleable.GlowPadView_targetDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify target descriptions");
- }
- setTargetDescriptionsResourceId(resourceId);
- }
-
- // Read array of direction descriptions
- if (a.getValue(R.styleable.GlowPadView_directionDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify direction descriptions");
- }
- setDirectionDescriptionsResourceId(resourceId);
- }
-
- mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP);
-
- a.recycle();
-
- setVibrateEnabled(mVibrationDuration > 0);
-
- assignDefaultsIfNeeded();
- }
-
- private int getResourceId(TypedArray a, int id) {
- TypedValue tv = a.peekValue(id);
- return tv == null ? 0 : tv.resourceId;
- }
-
- private void dump() {
- Log.v(TAG, "Outer Radius = " + mOuterRadius);
- Log.v(TAG, "SnapMargin = " + mSnapMargin);
- Log.v(TAG, "FeedbackCount = " + mFeedbackCount);
- Log.v(TAG, "VibrationDuration = " + mVibrationDuration);
- Log.v(TAG, "GlowRadius = " + mGlowRadius);
- Log.v(TAG, "WaveCenterX = " + mWaveCenterX);
- Log.v(TAG, "WaveCenterY = " + mWaveCenterY);
- }
-
- public void suspendAnimations() {
- mWaveAnimations.setSuspended(true);
- mTargetAnimations.setSuspended(true);
- mGlowAnimations.setSuspended(true);
- }
-
- public void resumeAnimations() {
- mWaveAnimations.setSuspended(false);
- mTargetAnimations.setSuspended(false);
- mGlowAnimations.setSuspended(false);
- mWaveAnimations.start();
- mTargetAnimations.start();
- mGlowAnimations.start();
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // View should be large enough to contain the background + handle and
- // target drawable on either edge.
- return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth);
- }
-
- @Override
- protected int getSuggestedMinimumHeight() {
- // View should be large enough to contain the unlock ring + target and
- // target drawable on either edge
- return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
- }
-
- /**
- * This gets the suggested width accounting for the ring's scale factor.
- */
- protected int getScaledSuggestedMinimumWidth() {
- return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius)
- + mMaxTargetWidth);
- }
-
- /**
- * This gets the suggested height accounting for the ring's scale factor.
- */
- protected int getScaledSuggestedMinimumHeight() {
- return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius)
- + mMaxTargetHeight);
- }
-
- private int resolveMeasured(int measureSpec, int desired)
- {
- int result = 0;
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (MeasureSpec.getMode(measureSpec)) {
- case MeasureSpec.UNSPECIFIED:
- result = desired;
- break;
- case MeasureSpec.AT_MOST:
- result = Math.min(specSize, desired);
- break;
- case MeasureSpec.EXACTLY:
- default:
- result = specSize;
- }
- return result;
- }
-
- private void switchToState(int state, float x, float y) {
- switch (state) {
- case STATE_IDLE:
- deactivateTargets();
- hideGlow(0, 0, 0.0f, null);
- startBackgroundAnimation(0, 0.0f);
- mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
- mHandleDrawable.setAlpha(1.0f);
- break;
-
- case STATE_START:
- startBackgroundAnimation(0, 0.0f);
- break;
-
- case STATE_FIRST_TOUCH:
- mHandleDrawable.setAlpha(0.0f);
- deactivateTargets();
- showTargets(true);
- startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
- setGrabbedState(OnTriggerListener.CENTER_HANDLE);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- announceTargets();
- }
- break;
-
- case STATE_TRACKING:
- mHandleDrawable.setAlpha(0.0f);
- showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 1.0f, null);
- break;
-
- case STATE_SNAP:
- // TODO: Add transition states (see list_selector_background_transition.xml)
- mHandleDrawable.setAlpha(0.0f);
- showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 0.0f, null);
- break;
-
- case STATE_FINISH:
- doFinish();
- break;
- }
- }
-
- private void showGlow(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mGlowAnimations.cancel();
- mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
- "ease", Ease.Cubic.easeIn,
- "delay", delay,
- "alpha", finalAlpha,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mGlowAnimations.start();
- }
-
- private void hideGlow(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mGlowAnimations.cancel();
- mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
- "ease", Ease.Quart.easeOut,
- "delay", delay,
- "alpha", finalAlpha,
- "x", 0.0f,
- "y", 0.0f,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mGlowAnimations.start();
- }
-
- private void deactivateTargets() {
- final int count = mTargetDrawables.size();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- mActiveTarget = -1;
- }
-
- /**
- * Dispatches a trigger event to listener. Ignored if a listener is not set.
- * @param whichTarget the target that was triggered.
- */
- private void dispatchTriggerEvent(int whichTarget) {
- vibrate();
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onTrigger(this, whichTarget);
- }
- }
-
- private void dispatchOnFinishFinalAnimation() {
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onFinishFinalAnimation();
- }
- }
-
- private void doFinish() {
- final int activeTarget = mActiveTarget;
- final boolean targetHit = activeTarget != -1;
-
- if (targetHit) {
- if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
-
- highlightSelected(activeTarget);
-
- // Inform listener of any active targets. Typically only one will be active.
- hideGlow(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
- dispatchTriggerEvent(activeTarget);
- if (!mAlwaysTrackFinger) {
- // Force ring and targets to finish animation to final expanded state
- mTargetAnimations.stop();
- }
- } else {
- // Animate handle back to the center based on current state.
- hideGlow(HIDE_ANIMATION_DURATION, 0, 0.0f, mResetListenerWithPing);
- hideTargets(true, false);
- }
-
- setGrabbedState(OnTriggerListener.NO_HANDLE);
- }
-
- private void highlightSelected(int activeTarget) {
- // Highlight the given target and fade others
- mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
- hideUnselected(activeTarget);
- }
-
- private void hideUnselected(int active) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- if (i != active) {
- mTargetDrawables.get(i).setAlpha(0.0f);
- }
- }
- }
-
- private void hideTargets(boolean animate, boolean expanded) {
- mTargetAnimations.cancel();
- // Note: these animations should complete at the same time so that we can swap out
- // the target assets asynchronously from the setTargetResources() call.
- mAnimatingTargets = animate;
- final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
- final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
-
- final float targetScale = expanded ?
- TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
- final int length = mTargetDrawables.size();
- final TimeInterpolator interpolator = Ease.Cubic.easeOut;
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", interpolator,
- "alpha", 0.0f,
- "scaleX", targetScale,
- "scaleY", targetScale,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
-
- float ringScaleTarget = expanded ?
- RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
- ringScaleTarget *= mRingScaleFactor;
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", interpolator,
- "alpha", 0.0f,
- "scaleX", ringScaleTarget,
- "scaleY", ringScaleTarget,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void showTargets(boolean animate) {
- mTargetAnimations.stop();
- mAnimatingTargets = animate;
- final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
- final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
- final int length = mTargetDrawables.size();
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", 1.0f,
- "scaleY", 1.0f,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
-
- float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED;
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", ringScale,
- "scaleY", ringScale,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void vibrate() {
- final boolean hapticEnabled = Settings.System.getIntForUser(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
- if (mVibrator != null && hapticEnabled) {
- mVibrator.vibrate(mVibrationDuration, VIBRATION_ATTRIBUTES);
- }
- }
-
- private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
- Resources res = getContext().getResources();
- TypedArray array = res.obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
- for (int i = 0; i < count; i++) {
- TypedValue value = array.peekValue(i);
- TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0);
- drawables.add(target);
- }
- array.recycle();
- return drawables;
- }
-
- private void internalSetTargetResources(int resourceId) {
- final ArrayList<TargetDrawable> targets = loadDrawableArray(resourceId);
- mTargetDrawables = targets;
- mTargetResourceId = resourceId;
-
- int maxWidth = mHandleDrawable.getWidth();
- int maxHeight = mHandleDrawable.getHeight();
- final int count = targets.size();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = targets.get(i);
- maxWidth = Math.max(maxWidth, target.getWidth());
- maxHeight = Math.max(maxHeight, target.getHeight());
- }
- if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
- mMaxTargetWidth = maxWidth;
- mMaxTargetHeight = maxHeight;
- requestLayout(); // required to resize layout and call updateTargetPositions()
- } else {
- updateTargetPositions(mWaveCenterX, mWaveCenterY);
- updatePointCloudPosition(mWaveCenterX, mWaveCenterY);
- }
- }
-
- /**
- * Loads an array of drawables from the given resourceId.
- *
- * @param resourceId
- */
- public void setTargetResources(int resourceId) {
- if (mAnimatingTargets) {
- // postpone this change until we return to the initial state
- mNewTargetResources = resourceId;
- } else {
- internalSetTargetResources(resourceId);
- }
- }
-
- public int getTargetResourceId() {
- return mTargetResourceId;
- }
-
- /**
- * Sets the resource id specifying the target descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setTargetDescriptionsResourceId(int resourceId) {
- mTargetDescriptionsResourceId = resourceId;
- if (mTargetDescriptions != null) {
- mTargetDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target descriptions for accessibility.
- *
- * @return The resource id.
- */
- public int getTargetDescriptionsResourceId() {
- return mTargetDescriptionsResourceId;
- }
-
- /**
- * Sets the resource id specifying the target direction descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setDirectionDescriptionsResourceId(int resourceId) {
- mDirectionDescriptionsResourceId = resourceId;
- if (mDirectionDescriptions != null) {
- mDirectionDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target direction descriptions.
- *
- * @return The resource id.
- */
- public int getDirectionDescriptionsResourceId() {
- return mDirectionDescriptionsResourceId;
- }
-
- /**
- * Enable or disable vibrate on touch.
- *
- * @param enabled
- */
- public void setVibrateEnabled(boolean enabled) {
- if (enabled && mVibrator == null) {
- mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
- } else {
- mVibrator = null;
- }
- }
-
- /**
- * Starts wave animation.
- *
- */
- public void ping() {
- if (mFeedbackCount > 0) {
- boolean doWaveAnimation = true;
- final AnimationBundle waveAnimations = mWaveAnimations;
-
- // Don't do a wave if there's already one in progress
- if (waveAnimations.size() > 0 && waveAnimations.get(0).animator.isRunning()) {
- long t = waveAnimations.get(0).animator.getCurrentPlayTime();
- if (t < WAVE_ANIMATION_DURATION/2) {
- doWaveAnimation = false;
- }
- }
-
- if (doWaveAnimation) {
- startWaveAnimation();
- }
- }
- }
-
- private void stopAndHideWaveAnimation() {
- mWaveAnimations.cancel();
- mPointCloud.waveManager.setAlpha(0.0f);
- }
-
- private void startWaveAnimation() {
- mWaveAnimations.cancel();
- mPointCloud.waveManager.setAlpha(1.0f);
- mPointCloud.waveManager.setRadius(mHandleDrawable.getWidth()/2.0f);
- mWaveAnimations.add(Tweener.to(mPointCloud.waveManager, WAVE_ANIMATION_DURATION,
- "ease", Ease.Quad.easeOut,
- "delay", 0,
- "radius", 2.0f * mOuterRadius,
- "onUpdate", mUpdateListener,
- "onComplete",
- new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- mPointCloud.waveManager.setRadius(0.0f);
- mPointCloud.waveManager.setAlpha(0.0f);
- }
- }));
- mWaveAnimations.start();
- }
-
- /**
- * Resets the widget to default state and cancels all animation. If animate is 'true', will
- * animate objects into place. Otherwise, objects will snap back to place.
- *
- * @param animate
- */
- public void reset(boolean animate) {
- mGlowAnimations.stop();
- mTargetAnimations.stop();
- startBackgroundAnimation(0, 0.0f);
- stopAndHideWaveAnimation();
- hideTargets(animate, false);
- hideGlow(0, 0, 0.0f, null);
- Tweener.reset();
- }
-
- private void startBackgroundAnimation(int duration, float alpha) {
- final Drawable background = getBackground();
- if (mAlwaysTrackFinger && background != null) {
- if (mBackgroundAnimator != null) {
- mBackgroundAnimator.animator.cancel();
- }
- mBackgroundAnimator = Tweener.to(background, duration,
- "ease", Ease.Cubic.easeIn,
- "alpha", (int)(255.0f * alpha),
- "delay", SHOW_ANIMATION_DELAY);
- mBackgroundAnimator.animator.start();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getActionMasked();
- boolean handled = false;
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_DOWN:
- if (DEBUG) Log.v(TAG, "*** DOWN ***");
- handleDown(event);
- handleMove(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (DEBUG) Log.v(TAG, "*** MOVE ***");
- handleMove(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_UP:
- if (DEBUG) Log.v(TAG, "*** UP ***");
- handleMove(event);
- handleUp(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (DEBUG) Log.v(TAG, "*** CANCEL ***");
- handleMove(event);
- handleCancel(event);
- handled = true;
- break;
-
- }
- invalidate();
- return handled ? true : super.onTouchEvent(event);
- }
-
- private void updateGlowPosition(float x, float y) {
- float dx = x - mOuterRing.getX();
- float dy = y - mOuterRing.getY();
- dx *= 1f / mRingScaleFactor;
- dy *= 1f / mRingScaleFactor;
- mPointCloud.glowManager.setX(mOuterRing.getX() + dx);
- mPointCloud.glowManager.setY(mOuterRing.getY() + dy);
- }
-
- private void handleDown(MotionEvent event) {
- int actionIndex = event.getActionIndex();
- float eventX = event.getX(actionIndex);
- float eventY = event.getY(actionIndex);
- switchToState(STATE_START, eventX, eventY);
- if (!trySwitchToFirstTouchState(eventX, eventY)) {
- mDragging = false;
- } else {
- mPointerId = event.getPointerId(actionIndex);
- updateGlowPosition(eventX, eventY);
- }
- }
-
- private void handleUp(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
- int actionIndex = event.getActionIndex();
- if (event.getPointerId(actionIndex) == mPointerId) {
- switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
- }
- }
-
- private void handleCancel(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
-
- // Drop the active target if canceled.
- mActiveTarget = -1;
-
- int actionIndex = event.findPointerIndex(mPointerId);
- actionIndex = actionIndex == -1 ? 0 : actionIndex;
- switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
- }
-
- private void handleMove(MotionEvent event) {
- int activeTarget = -1;
- final int historySize = event.getHistorySize();
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- int ntargets = targets.size();
- float x = 0.0f;
- float y = 0.0f;
- float activeAngle = 0.0f;
- int actionIndex = event.findPointerIndex(mPointerId);
-
- if (actionIndex == -1) {
- return; // no data for this pointer
- }
-
- for (int k = 0; k < historySize + 1; k++) {
- float eventX = k < historySize ? event.getHistoricalX(actionIndex, k)
- : event.getX(actionIndex);
- float eventY = k < historySize ? event.getHistoricalY(actionIndex, k)
- : event.getY(actionIndex);
- // tx and ty are relative to wave center
- float tx = eventX - mWaveCenterX;
- float ty = eventY - mWaveCenterY;
- float touchRadius = (float) Math.hypot(tx, ty);
- final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
- float limitX = tx * scale;
- float limitY = ty * scale;
- double angleRad = Math.atan2(-ty, tx);
-
- if (!mDragging) {
- trySwitchToFirstTouchState(eventX, eventY);
- }
-
- if (mDragging) {
- // For multiple targets, snap to the one that matches
- final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin;
- final float snapDistance2 = snapRadius * snapRadius;
- // Find first target in range
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = targets.get(i);
-
- double targetMinRad = mFirstItemOffset + (i - 0.5) * 2 * Math.PI / ntargets;
- double targetMaxRad = mFirstItemOffset + (i + 0.5) * 2 * Math.PI / ntargets;
- if (target.isEnabled()) {
- boolean angleMatches =
- (angleRad > targetMinRad && angleRad <= targetMaxRad) ||
- (angleRad + 2 * Math.PI > targetMinRad &&
- angleRad + 2 * Math.PI <= targetMaxRad) ||
- (angleRad - 2 * Math.PI > targetMinRad &&
- angleRad - 2 * Math.PI <= targetMaxRad);
- if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
- activeTarget = i;
- activeAngle = (float) -angleRad;
- }
- }
- }
- }
- x = limitX;
- y = limitY;
- }
-
- if (!mDragging) {
- return;
- }
-
- if (activeTarget != -1) {
- switchToState(STATE_SNAP, x,y);
- updateGlowPosition(x, y);
- } else {
- switchToState(STATE_TRACKING, x, y);
- updateGlowPosition(x, y);
- }
-
- if (mActiveTarget != activeTarget) {
- // Defocus the old target
- if (mActiveTarget != -1) {
- TargetDrawable target = targets.get(mActiveTarget);
- if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- if (mMagneticTargets) {
- updateTargetPosition(mActiveTarget, mWaveCenterX, mWaveCenterY);
- }
- }
- // Focus the new target
- if (activeTarget != -1) {
- TargetDrawable target = targets.get(activeTarget);
- if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
- target.setState(TargetDrawable.STATE_FOCUSED);
- }
- if (mMagneticTargets) {
- updateTargetPosition(activeTarget, mWaveCenterX, mWaveCenterY, activeAngle);
- }
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- String targetContentDescription = getTargetDescription(activeTarget);
- announceForAccessibility(targetContentDescription);
- }
- }
- }
- mActiveTarget = activeTarget;
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER:
- event.setAction(MotionEvent.ACTION_DOWN);
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- event.setAction(MotionEvent.ACTION_MOVE);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- event.setAction(MotionEvent.ACTION_UP);
- break;
- }
- onTouchEvent(event);
- event.setAction(action);
- }
- super.onHoverEvent(event);
- return true;
- }
-
- /**
- * Sets the current grabbed state, and dispatches a grabbed state change
- * event to our listener.
- */
- private void setGrabbedState(int newState) {
- if (newState != mGrabbedState) {
- if (newState != OnTriggerListener.NO_HANDLE) {
- vibrate();
- }
- mGrabbedState = newState;
- if (mOnTriggerListener != null) {
- if (newState == OnTriggerListener.NO_HANDLE) {
- mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
- } else {
- mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
- }
- mOnTriggerListener.onGrabbedStateChange(this, newState);
- }
- }
- }
-
- private boolean trySwitchToFirstTouchState(float x, float y) {
- final float tx = x - mWaveCenterX;
- final float ty = y - mWaveCenterY;
- if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledGlowRadiusSquared()) {
- if (DEBUG) Log.v(TAG, "** Handle HIT");
- switchToState(STATE_FIRST_TOUCH, x, y);
- updateGlowPosition(tx, ty);
- mDragging = true;
- return true;
- }
- return false;
- }
-
- private void assignDefaultsIfNeeded() {
- if (mOuterRadius == 0.0f) {
- mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
- }
- if (mSnapMargin == 0.0f) {
- mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
- }
- if (mInnerRadius == 0.0f) {
- mInnerRadius = mHandleDrawable.getWidth() / 10.0f;
- }
- }
-
- private void computeInsets(int dx, int dy) {
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- mHorizontalInset = 0;
- break;
- case Gravity.RIGHT:
- mHorizontalInset = dx;
- break;
- case Gravity.CENTER_HORIZONTAL:
- default:
- mHorizontalInset = dx / 2;
- break;
- }
- switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- mVerticalInset = 0;
- break;
- case Gravity.BOTTOM:
- mVerticalInset = dy;
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- mVerticalInset = dy / 2;
- break;
- }
- }
-
- /**
- * Given the desired width and height of the ring and the allocated width and height, compute
- * how much we need to scale the ring.
- */
- private float computeScaleFactor(int desiredWidth, int desiredHeight,
- int actualWidth, int actualHeight) {
-
- // Return unity if scaling is not allowed.
- if (!mAllowScaling) return 1f;
-
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- float scaleX = 1f;
- float scaleY = 1f;
-
- // We use the gravity as a cue for whether we want to scale on a particular axis.
- // We only scale to fit horizontally if we're not pinned to the left or right. Likewise,
- // we only scale to fit vertically if we're not pinned to the top or bottom. In these
- // cases, we want the ring to hang off the side or top/bottom, respectively.
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- case Gravity.RIGHT:
- break;
- case Gravity.CENTER_HORIZONTAL:
- default:
- if (desiredWidth > actualWidth) {
- scaleX = (1f * actualWidth - mMaxTargetWidth) /
- (desiredWidth - mMaxTargetWidth);
- }
- break;
- }
- switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- case Gravity.BOTTOM:
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- if (desiredHeight > actualHeight) {
- scaleY = (1f * actualHeight - mMaxTargetHeight) /
- (desiredHeight - mMaxTargetHeight);
- }
- break;
- }
- return Math.min(scaleX, scaleY);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int minimumWidth = getSuggestedMinimumWidth();
- final int minimumHeight = getSuggestedMinimumHeight();
- int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
- int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-
- mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight,
- computedWidth, computedHeight);
-
- int scaledWidth = getScaledSuggestedMinimumWidth();
- int scaledHeight = getScaledSuggestedMinimumHeight();
-
- computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight);
- setMeasuredDimension(computedWidth, computedHeight);
- }
-
- private float getRingWidth() {
- return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
- }
-
- private float getRingHeight() {
- return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- final int width = right - left;
- final int height = bottom - top;
-
- // Target placement width/height. This puts the targets on the greater of the ring
- // width or the specified outer radius.
- final float placementWidth = getRingWidth();
- final float placementHeight = getRingHeight();
- float newWaveCenterX = mHorizontalInset
- + Math.max(width, mMaxTargetWidth + placementWidth) / 2;
- float newWaveCenterY = mVerticalInset
- + Math.max(height, + mMaxTargetHeight + placementHeight) / 2;
-
- if (mInitialLayout) {
- stopAndHideWaveAnimation();
- hideTargets(false, false);
- mInitialLayout = false;
- }
-
- mOuterRing.setPositionX(newWaveCenterX);
- mOuterRing.setPositionY(newWaveCenterY);
-
- mPointCloud.setScale(mRingScaleFactor);
-
- mHandleDrawable.setPositionX(newWaveCenterX);
- mHandleDrawable.setPositionY(newWaveCenterY);
-
- updateTargetPositions(newWaveCenterX, newWaveCenterY);
- updatePointCloudPosition(newWaveCenterX, newWaveCenterY);
- updateGlowPosition(newWaveCenterX, newWaveCenterY);
-
- mWaveCenterX = newWaveCenterX;
- mWaveCenterY = newWaveCenterY;
-
- if (DEBUG) dump();
- }
-
- private void updateTargetPosition(int i, float centerX, float centerY) {
- final float angle = getAngle(getSliceAngle(), i);
- updateTargetPosition(i, centerX, centerY, angle);
- }
-
- private void updateTargetPosition(int i, float centerX, float centerY, float angle) {
- final float placementRadiusX = getRingWidth() / 2;
- final float placementRadiusY = getRingHeight() / 2;
- if (i >= 0) {
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- final TargetDrawable targetIcon = targets.get(i);
- targetIcon.setPositionX(centerX);
- targetIcon.setPositionY(centerY);
- targetIcon.setX(placementRadiusX * (float) Math.cos(angle));
- targetIcon.setY(placementRadiusY * (float) Math.sin(angle));
- }
- }
-
- private void updateTargetPositions(float centerX, float centerY) {
- updateTargetPositions(centerX, centerY, false);
- }
-
- private void updateTargetPositions(float centerX, float centerY, boolean skipActive) {
- final int size = mTargetDrawables.size();
- final float alpha = getSliceAngle();
- // Reposition the target drawables if the view changed.
- for (int i = 0; i < size; i++) {
- if (!skipActive || i != mActiveTarget) {
- updateTargetPosition(i, centerX, centerY, getAngle(alpha, i));
- }
- }
- }
-
- private float getAngle(float alpha, int i) {
- return mFirstItemOffset + alpha * i;
- }
-
- private float getSliceAngle() {
- return (float) (-2.0f * Math.PI / mTargetDrawables.size());
- }
-
- private void updatePointCloudPosition(float centerX, float centerY) {
- mPointCloud.setCenter(centerX, centerY);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mPointCloud.draw(canvas);
- mOuterRing.draw(canvas);
- final int ntargets = mTargetDrawables.size();
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- if (target != null) {
- target.draw(canvas);
- }
- }
- mHandleDrawable.draw(canvas);
- }
-
- public void setOnTriggerListener(OnTriggerListener listener) {
- mOnTriggerListener = listener;
- }
-
- private float square(float d) {
- return d * d;
- }
-
- private float dist2(float dx, float dy) {
- return dx*dx + dy*dy;
- }
-
- private float getScaledGlowRadiusSquared() {
- final float scaledTapRadius;
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mGlowRadius;
- } else {
- scaledTapRadius = mGlowRadius;
- }
- return square(scaledTapRadius);
- }
-
- private void announceTargets() {
- StringBuilder utterance = new StringBuilder();
- final int targetCount = mTargetDrawables.size();
- for (int i = 0; i < targetCount; i++) {
- String targetDescription = getTargetDescription(i);
- String directionDescription = getDirectionDescription(i);
- if (!TextUtils.isEmpty(targetDescription)
- && !TextUtils.isEmpty(directionDescription)) {
- String text = String.format(directionDescription, targetDescription);
- utterance.append(text);
- }
- }
- if (utterance.length() > 0) {
- announceForAccessibility(utterance.toString());
- }
- }
-
- private String getTargetDescription(int index) {
- if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
- mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
- if (mTargetDrawables.size() != mTargetDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " equal to the number of target descriptions.");
- return null;
- }
- }
- return mTargetDescriptions.get(index);
- }
-
- private String getDirectionDescription(int index) {
- if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
- mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
- if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " equal to the number of direction descriptions.");
- return null;
- }
- }
- return mDirectionDescriptions.get(index);
- }
-
- private ArrayList<String> loadDescriptions(int resourceId) {
- TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
- for (int i = 0; i < count; i++) {
- String contentDescription = array.getString(i);
- targetContentDescriptions.add(contentDescription);
- }
- array.recycle();
- return targetContentDescriptions;
- }
-
- public int getResourceIdForTarget(int index) {
- final TargetDrawable drawable = mTargetDrawables.get(index);
- return drawable == null ? 0 : drawable.getResourceId();
- }
-
- public void setEnableTarget(int resourceId, boolean enabled) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- target.setEnabled(enabled);
- break; // should never be more than one match
- }
- }
- }
-
- /**
- * Gets the position of a target in the array that matches the given resource.
- * @param resourceId
- * @return the index or -1 if not found
- */
- public int getTargetPosition(int resourceId) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- return i; // should never be more than one match
- }
- }
- return -1;
- }
-
- private boolean replaceTargetDrawables(Resources res, int existingResourceId,
- int newResourceId) {
- if (existingResourceId == 0 || newResourceId == 0) {
- return false;
- }
-
- boolean result = false;
- final ArrayList<TargetDrawable> drawables = mTargetDrawables;
- final int size = drawables.size();
- for (int i = 0; i < size; i++) {
- final TargetDrawable target = drawables.get(i);
- if (target != null && target.getResourceId() == existingResourceId) {
- target.setDrawable(res, newResourceId);
- result = true;
- }
- }
-
- if (result) {
- requestLayout(); // in case any given drawable's size changes
- }
-
- return result;
- }
-
- /**
- * Searches the given package for a resource to use to replace the Drawable on the
- * target with the given resource id
- * @param component of the .apk that contains the resource
- * @param name of the metadata in the .apk
- * @param existingResId the resource id of the target to search for
- * @return true if found in the given package and replaced at least one target Drawables
- */
- public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name,
- int existingResId) {
- if (existingResId == 0) return false;
-
- boolean replaced = false;
- if (component != null) {
- try {
- PackageManager packageManager = mContext.getPackageManager();
- // Look for the search icon specified in the activity meta-data
- Bundle metaData = packageManager.getActivityInfo(
- component, PackageManager.GET_META_DATA).metaData;
- if (metaData != null) {
- int iconResId = metaData.getInt(name);
- if (iconResId != 0) {
- Resources res = packageManager.getResourcesForActivity(component);
- replaced = replaceTargetDrawables(res, existingResId, iconResId);
- }
- }
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed to swap drawable; "
- + component.flattenToShortString() + " not found", e);
- } catch (Resources.NotFoundException nfe) {
- Log.w(TAG, "Failed to swap drawable from "
- + component.flattenToShortString(), nfe);
- }
- }
- if (!replaced) {
- // Restore the original drawable
- replaceTargetDrawables(mContext.getResources(), existingResId, existingResId);
- }
- return replaced;
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java b/core/java/com/android/internal/widget/multiwaveview/PointCloud.java
deleted file mode 100644
index 6f26b99..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget.multiwaveview;
-
-import java.util.ArrayList;
-
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-public class PointCloud {
- private static final float MIN_POINT_SIZE = 2.0f;
- private static final float MAX_POINT_SIZE = 4.0f;
- private static final int INNER_POINTS = 8;
- private static final String TAG = "PointCloud";
- private ArrayList<Point> mPointCloud = new ArrayList<Point>();
- private Drawable mDrawable;
- private float mCenterX;
- private float mCenterY;
- private Paint mPaint;
- private float mScale = 1.0f;
- private static final float PI = (float) Math.PI;
-
- // These allow us to have multiple concurrent animations.
- WaveManager waveManager = new WaveManager();
- GlowManager glowManager = new GlowManager();
- private float mOuterRadius;
-
- public class WaveManager {
- private float radius = 50;
- private float alpha = 0.0f;
-
- public void setRadius(float r) {
- radius = r;
- }
-
- public float getRadius() {
- return radius;
- }
-
- public void setAlpha(float a) {
- alpha = a;
- }
-
- public float getAlpha() {
- return alpha;
- }
- };
-
- public class GlowManager {
- private float x;
- private float y;
- private float radius = 0.0f;
- private float alpha = 0.0f;
-
- public void setX(float x1) {
- x = x1;
- }
-
- public float getX() {
- return x;
- }
-
- public void setY(float y1) {
- y = y1;
- }
-
- public float getY() {
- return y;
- }
-
- public void setAlpha(float a) {
- alpha = a;
- }
-
- public float getAlpha() {
- return alpha;
- }
-
- public void setRadius(float r) {
- radius = r;
- }
-
- public float getRadius() {
- return radius;
- }
- }
-
- class Point {
- float x;
- float y;
- float radius;
-
- public Point(float x2, float y2, float r) {
- x = (float) x2;
- y = (float) y2;
- radius = r;
- }
- }
-
- public PointCloud(Drawable drawable) {
- mPaint = new Paint();
- mPaint.setFilterBitmap(true);
- mPaint.setColor(Color.rgb(255, 255, 255)); // TODO: make configurable
- mPaint.setAntiAlias(true);
- mPaint.setDither(true);
-
- mDrawable = drawable;
- if (mDrawable != null) {
- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
- }
- }
-
- public void setCenter(float x, float y) {
- mCenterX = x;
- mCenterY = y;
- }
-
- public void makePointCloud(float innerRadius, float outerRadius) {
- if (innerRadius == 0) {
- Log.w(TAG, "Must specify an inner radius");
- return;
- }
- mOuterRadius = outerRadius;
- mPointCloud.clear();
- final float pointAreaRadius = (outerRadius - innerRadius);
- final float ds = (2.0f * PI * innerRadius / INNER_POINTS);
- final int bands = (int) Math.round(pointAreaRadius / ds);
- final float dr = pointAreaRadius / bands;
- float r = innerRadius;
- for (int b = 0; b <= bands; b++, r += dr) {
- float circumference = 2.0f * PI * r;
- final int pointsInBand = (int) (circumference / ds);
- float eta = PI/2.0f;
- float dEta = 2.0f * PI / pointsInBand;
- for (int i = 0; i < pointsInBand; i++) {
- float x = r * (float) Math.cos(eta);
- float y = r * (float) Math.sin(eta);
- eta += dEta;
- mPointCloud.add(new Point(x, y, r));
- }
- }
- }
-
- public void setScale(float scale) {
- mScale = scale;
- }
-
- public float getScale() {
- return mScale;
- }
-
- public int getAlphaForPoint(Point point) {
- // Contribution from positional glow
- float glowDistance = (float) Math.hypot(glowManager.x - point.x, glowManager.y - point.y);
- float glowAlpha = 0.0f;
- if (glowDistance < glowManager.radius) {
- float cosf = (float) Math.cos(PI * 0.25f * glowDistance / glowManager.radius);
- glowAlpha = glowManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 10.0f));
- }
-
- // Compute contribution from Wave
- float radius = (float) Math.hypot(point.x, point.y);
- float waveAlpha = 0.0f;
- if (radius < waveManager.radius * 2) {
- float distanceToWaveRing = (radius - waveManager.radius);
- float cosf = (float) Math.cos(PI * 0.5f * distanceToWaveRing / waveManager.radius);
- waveAlpha = waveManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 6.0f));
- }
- return (int) (Math.max(glowAlpha, waveAlpha) * 255);
- }
-
- private float interp(float min, float max, float f) {
- return min + (max - min) * f;
- }
-
- public void draw(Canvas canvas) {
- ArrayList<Point> points = mPointCloud;
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(mScale, mScale, mCenterX, mCenterY);
- for (int i = 0; i < points.size(); i++) {
- Point point = points.get(i);
- final float pointSize = interp(MAX_POINT_SIZE, MIN_POINT_SIZE,
- point.radius / mOuterRadius);
- final float px = point.x + mCenterX;
- final float py = point.y + mCenterY;
- int alpha = getAlphaForPoint(point);
-
- if (alpha == 0) continue;
-
- if (mDrawable != null) {
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- final float cx = mDrawable.getIntrinsicWidth() * 0.5f;
- final float cy = mDrawable.getIntrinsicHeight() * 0.5f;
- final float s = pointSize / MAX_POINT_SIZE;
- canvas.scale(s, s, px, py);
- canvas.translate(px - cx, py - cy);
- mDrawable.setAlpha(alpha);
- mDrawable.draw(canvas);
- canvas.restore();
- } else {
- mPaint.setAlpha(alpha);
- canvas.drawCircle(px, py, pointSize, mPaint);
- }
- }
- canvas.restore();
- }
-
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
deleted file mode 100644
index 5a4c441..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2011 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.internal.widget.multiwaveview;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.util.Log;
-
-public class TargetDrawable {
- private static final String TAG = "TargetDrawable";
- private static final boolean DEBUG = false;
-
- public static final int[] STATE_ACTIVE =
- { android.R.attr.state_enabled, android.R.attr.state_active };
- public static final int[] STATE_INACTIVE =
- { android.R.attr.state_enabled, -android.R.attr.state_active };
- public static final int[] STATE_FOCUSED =
- { android.R.attr.state_enabled, -android.R.attr.state_active,
- android.R.attr.state_focused };
-
- private float mTranslationX = 0.0f;
- private float mTranslationY = 0.0f;
- private float mPositionX = 0.0f;
- private float mPositionY = 0.0f;
- private float mScaleX = 1.0f;
- private float mScaleY = 1.0f;
- private float mAlpha = 1.0f;
- private Drawable mDrawable;
- private boolean mEnabled = true;
- private final int mResourceId;
-
- public TargetDrawable(Resources res, int resId) {
- mResourceId = resId;
- setDrawable(res, resId);
- }
-
- public void setDrawable(Resources res, int resId) {
- // Note we explicitly don't set mResourceId to resId since we allow the drawable to be
- // swapped at runtime and want to re-use the existing resource id for identification.
- Drawable drawable = resId == 0 ? null : res.getDrawable(resId);
- // Mutate the drawable so we can animate shared drawable properties.
- mDrawable = drawable != null ? drawable.mutate() : null;
- resizeDrawables();
- setState(STATE_INACTIVE);
- }
-
- public TargetDrawable(TargetDrawable other) {
- mResourceId = other.mResourceId;
- // Mutate the drawable so we can animate shared drawable properties.
- mDrawable = other.mDrawable != null ? other.mDrawable.mutate() : null;
- resizeDrawables();
- setState(STATE_INACTIVE);
- }
-
- public void setState(int [] state) {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- d.setState(state);
- }
- }
-
- public boolean hasState(int [] state) {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- // TODO: this doesn't seem to work
- return d.getStateDrawableIndex(state) != -1;
- }
- return false;
- }
-
- /**
- * Returns true if the drawable is a StateListDrawable and is in the focused state.
- *
- * @return
- */
- public boolean isActive() {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- int[] states = d.getState();
- for (int i = 0; i < states.length; i++) {
- if (states[i] == android.R.attr.state_focused) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns true if this target is enabled. Typically an enabled target contains a valid
- * drawable in a valid state. Currently all targets with valid drawables are valid.
- *
- * @return
- */
- public boolean isEnabled() {
- return mDrawable != null && mEnabled;
- }
-
- /**
- * Makes drawables in a StateListDrawable all the same dimensions.
- * If not a StateListDrawable, then justs sets the bounds to the intrinsic size of the
- * drawable.
- */
- private void resizeDrawables() {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- int maxWidth = 0;
- int maxHeight = 0;
- for (int i = 0; i < d.getStateCount(); i++) {
- Drawable childDrawable = d.getStateDrawable(i);
- maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
- maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
- }
- if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
- + maxWidth + "x" + maxHeight);
- d.setBounds(0, 0, maxWidth, maxHeight);
- for (int i = 0; i < d.getStateCount(); i++) {
- Drawable childDrawable = d.getStateDrawable(i);
- if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
- + maxWidth + "x" + maxHeight);
- childDrawable.setBounds(0, 0, maxWidth, maxHeight);
- }
- } else if (mDrawable != null) {
- mDrawable.setBounds(0, 0,
- mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
- }
- }
-
- public void setX(float x) {
- mTranslationX = x;
- }
-
- public void setY(float y) {
- mTranslationY = y;
- }
-
- public void setScaleX(float x) {
- mScaleX = x;
- }
-
- public void setScaleY(float y) {
- mScaleY = y;
- }
-
- public void setAlpha(float alpha) {
- mAlpha = alpha;
- }
-
- public float getX() {
- return mTranslationX;
- }
-
- public float getY() {
- return mTranslationY;
- }
-
- public float getScaleX() {
- return mScaleX;
- }
-
- public float getScaleY() {
- return mScaleY;
- }
-
- public float getAlpha() {
- return mAlpha;
- }
-
- public void setPositionX(float x) {
- mPositionX = x;
- }
-
- public void setPositionY(float y) {
- mPositionY = y;
- }
-
- public float getPositionX() {
- return mPositionX;
- }
-
- public float getPositionY() {
- return mPositionY;
- }
-
- public int getWidth() {
- return mDrawable != null ? mDrawable.getIntrinsicWidth() : 0;
- }
-
- public int getHeight() {
- return mDrawable != null ? mDrawable.getIntrinsicHeight() : 0;
- }
-
- public void draw(Canvas canvas) {
- if (mDrawable == null || !mEnabled) {
- return;
- }
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(mScaleX, mScaleY, mPositionX, mPositionY);
- canvas.translate(mTranslationX + mPositionX, mTranslationY + mPositionY);
- canvas.translate(-0.5f * getWidth(), -0.5f * getHeight());
- mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
- mDrawable.draw(canvas);
- canvas.restore();
- }
-
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
- public int getResourceId() {
- return mResourceId;
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
deleted file mode 100644
index d559d9d..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2011 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.internal.widget.multiwaveview;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map.Entry;
-
-import android.animation.Animator.AnimatorListener;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.util.Log;
-
-class Tweener {
- private static final String TAG = "Tweener";
- private static final boolean DEBUG = false;
-
- ObjectAnimator animator;
- private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>();
-
- public Tweener(ObjectAnimator anim) {
- animator = anim;
- }
-
- private static void remove(Animator animator) {
- Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator();
- while (iter.hasNext()) {
- Entry<Object, Tweener> entry = iter.next();
- if (entry.getValue().animator == animator) {
- if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey())
- + " sTweens.size() = " + sTweens.size());
- iter.remove();
- break; // an animator can only be attached to one object
- }
- }
- }
-
- public static Tweener to(Object object, long duration, Object... vars) {
- long delay = 0;
- AnimatorUpdateListener updateListener = null;
- AnimatorListener listener = null;
- TimeInterpolator interpolator = null;
-
- // Iterate through arguments and discover properties to animate
- ArrayList<PropertyValuesHolder> props = new ArrayList<PropertyValuesHolder>(vars.length/2);
- for (int i = 0; i < vars.length; i+=2) {
- if (!(vars[i] instanceof String)) {
- throw new IllegalArgumentException("Key must be a string: " + vars[i]);
- }
- String key = (String) vars[i];
- Object value = vars[i+1];
- if ("simultaneousTween".equals(key)) {
- // TODO
- } else if ("ease".equals(key)) {
- interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
- } else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
- updateListener = (AnimatorUpdateListener) value;
- } else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) {
- listener = (AnimatorListener) value;
- } else if ("delay".equals(key)) {
- delay = ((Number) value).longValue();
- } else if ("syncWith".equals(key)) {
- // TODO
- } else if (value instanceof float[]) {
- props.add(PropertyValuesHolder.ofFloat(key,
- ((float[])value)[0], ((float[])value)[1]));
- } else if (value instanceof int[]) {
- props.add(PropertyValuesHolder.ofInt(key,
- ((int[])value)[0], ((int[])value)[1]));
- } else if (value instanceof Number) {
- float floatValue = ((Number)value).floatValue();
- props.add(PropertyValuesHolder.ofFloat(key, floatValue));
- } else {
- throw new IllegalArgumentException(
- "Bad argument for key \"" + key + "\" with value " + value.getClass());
- }
- }
-
- // Re-use existing tween, if present
- Tweener tween = sTweens.get(object);
- ObjectAnimator anim = null;
- if (tween == null) {
- anim = ObjectAnimator.ofPropertyValuesHolder(object,
- props.toArray(new PropertyValuesHolder[props.size()]));
- tween = new Tweener(anim);
- sTweens.put(object, tween);
- if (DEBUG) Log.v(TAG, "Added new Tweener " + tween);
- } else {
- anim = sTweens.get(object).animator;
- replace(props, object); // Cancel all animators for given object
- }
-
- if (interpolator != null) {
- anim.setInterpolator(interpolator);
- }
-
- // Update animation with properties discovered in loop above
- anim.setStartDelay(delay);
- anim.setDuration(duration);
- if (updateListener != null) {
- anim.removeAllUpdateListeners(); // There should be only one
- anim.addUpdateListener(updateListener);
- }
- if (listener != null) {
- anim.removeAllListeners(); // There should be only one.
- anim.addListener(listener);
- }
- anim.addListener(mCleanupListener);
-
- return tween;
- }
-
- Tweener from(Object object, long duration, Object... vars) {
- // TODO: for v of vars
- // toVars[v] = object[v]
- // object[v] = vars[v]
- return Tweener.to(object, duration, vars);
- }
-
- // Listener to watch for completed animations and remove them.
- private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(Animator animation) {
- remove(animation);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- remove(animation);
- }
- };
-
- public static void reset() {
- if (DEBUG) {
- Log.v(TAG, "Reset()");
- if (sTweens.size() > 0) {
- Log.v(TAG, "Cleaning up " + sTweens.size() + " animations");
- }
- }
- sTweens.clear();
- }
-
- private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
- for (final Object killobject : args) {
- Tweener tween = sTweens.get(killobject);
- if (tween != null) {
- tween.animator.cancel();
- if (props != null) {
- tween.animator.setValues(
- props.toArray(new PropertyValuesHolder[props.size()]));
- } else {
- sTweens.remove(tween);
- }
- }
- }
- }
-}
diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
new file mode 100644
index 0000000..3f4b980
--- /dev/null
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -0,0 +1,380 @@
+/*
+ * 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 com.android.server.backup;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.backup.BackupDataInputStream;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupHelper;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SyncAdapterType;
+import android.content.SyncStatusObserver;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper for backing up account sync settings (whether or not a service should be synced). The
+ * sync settings are backed up as a JSON object containing all the necessary information for
+ * restoring the sync settings later.
+ */
+public class AccountSyncSettingsBackupHelper implements BackupHelper {
+
+ private static final String TAG = "AccountSyncSettingsBackupHelper";
+ private static final boolean DEBUG = false;
+
+ private static final int STATE_VERSION = 1;
+ private static final int MD5_BYTE_SIZE = 16;
+ private static final int SYNC_REQUEST_LATCH_TIMEOUT_SECONDS = 1;
+
+ private static final String JSON_FORMAT_HEADER_KEY = "account_data";
+ private static final String JSON_FORMAT_ENCODING = "UTF-8";
+ private static final int JSON_FORMAT_VERSION = 1;
+
+ private static final String KEY_VERSION = "version";
+ private static final String KEY_MASTER_SYNC_ENABLED = "masterSyncEnabled";
+ private static final String KEY_ACCOUNTS = "accounts";
+ private static final String KEY_ACCOUNT_NAME = "name";
+ private static final String KEY_ACCOUNT_TYPE = "type";
+ private static final String KEY_ACCOUNT_AUTHORITIES = "authorities";
+ private static final String KEY_AUTHORITY_NAME = "name";
+ private static final String KEY_AUTHORITY_SYNC_STATE = "syncState";
+ private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled";
+
+ private Context mContext;
+ private AccountManager mAccountManager;
+
+ public AccountSyncSettingsBackupHelper(Context context) {
+ mContext = context;
+ mAccountManager = AccountManager.get(mContext);
+ }
+
+ /**
+ * Take a snapshot of the current account sync settings and write them to the given output.
+ */
+ @Override
+ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput output,
+ ParcelFileDescriptor newState) {
+ try {
+ JSONObject dataJSON = serializeAccountSyncSettingsToJSON();
+
+ if (DEBUG) {
+ Log.d(TAG, "Account sync settings JSON: " + dataJSON);
+ }
+
+ // Encode JSON data to bytes.
+ byte[] dataBytes = dataJSON.toString().getBytes(JSON_FORMAT_ENCODING);
+ byte[] oldMd5Checksum = readOldMd5Checksum(oldState);
+ byte[] newMd5Checksum = generateMd5Checksum(dataBytes);
+ if (!Arrays.equals(oldMd5Checksum, newMd5Checksum)) {
+ int dataSize = dataBytes.length;
+ output.writeEntityHeader(JSON_FORMAT_HEADER_KEY, dataSize);
+ output.writeEntityData(dataBytes, dataSize);
+
+ Log.i(TAG, "Backup successful.");
+ } else {
+ Log.i(TAG, "Old and new MD5 checksums match. Skipping backup.");
+ }
+
+ writeNewMd5Checksum(newState, newMd5Checksum);
+ } catch (JSONException | IOException | NoSuchAlgorithmException e) {
+ Log.e(TAG, "Couldn't backup account sync settings\n" + e);
+ }
+ }
+
+ /**
+ * Fetch and serialize Account and authority information as a JSON Array.
+ */
+ private JSONObject serializeAccountSyncSettingsToJSON() throws JSONException {
+ Account[] accounts = mAccountManager.getAccounts();
+ SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
+ mContext.getUserId());
+
+ // Create a map of Account types to authorities. Later this will make it easier for us to
+ // generate our JSON.
+ HashMap<String, List<String>> accountTypeToAuthorities = new HashMap<String,
+ List<String>>();
+ for (SyncAdapterType syncAdapter : syncAdapters) {
+ // Skip adapters that aren’t visible to the user.
+ if (!syncAdapter.isUserVisible()) {
+ continue;
+ }
+ if (!accountTypeToAuthorities.containsKey(syncAdapter.accountType)) {
+ accountTypeToAuthorities.put(syncAdapter.accountType, new ArrayList<String>());
+ }
+ accountTypeToAuthorities.get(syncAdapter.accountType).add(syncAdapter.authority);
+ }
+
+ // Generate JSON.
+ JSONObject backupJSON = new JSONObject();
+ backupJSON.put(KEY_VERSION, JSON_FORMAT_VERSION);
+ backupJSON.put(KEY_MASTER_SYNC_ENABLED, ContentResolver.getMasterSyncAutomatically());
+
+ JSONArray accountJSONArray = new JSONArray();
+ for (Account account : accounts) {
+ List<String> authorities = accountTypeToAuthorities.get(account.type);
+
+ // We ignore Accounts that don't have any authorities because there would be no sync
+ // settings for us to restore.
+ if (authorities == null || authorities.isEmpty()) {
+ continue;
+ }
+
+ JSONObject accountJSON = new JSONObject();
+ accountJSON.put(KEY_ACCOUNT_NAME, account.name);
+ accountJSON.put(KEY_ACCOUNT_TYPE, account.type);
+
+ // Add authorities for this Account type and check whether or not sync is enabled.
+ JSONArray authoritiesJSONArray = new JSONArray();
+ for (String authority : authorities) {
+ int syncState = ContentResolver.getIsSyncable(account, authority);
+ boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority);
+
+ JSONObject authorityJSON = new JSONObject();
+ authorityJSON.put(KEY_AUTHORITY_NAME, authority);
+ authorityJSON.put(KEY_AUTHORITY_SYNC_STATE, syncState);
+ authorityJSON.put(KEY_AUTHORITY_SYNC_ENABLED, syncEnabled);
+ authoritiesJSONArray.put(authorityJSON);
+ }
+ accountJSON.put(KEY_ACCOUNT_AUTHORITIES, authoritiesJSONArray);
+
+ accountJSONArray.put(accountJSON);
+ }
+ backupJSON.put(KEY_ACCOUNTS, accountJSONArray);
+
+ return backupJSON;
+ }
+
+ /**
+ * Read the MD5 checksum from the old state.
+ *
+ * @return the old MD5 checksum
+ */
+ private byte[] readOldMd5Checksum(ParcelFileDescriptor oldState) throws IOException {
+ DataInputStream dataInput = new DataInputStream(
+ new FileInputStream(oldState.getFileDescriptor()));
+
+ byte[] oldMd5Checksum = new byte[MD5_BYTE_SIZE];
+ try {
+ int stateVersion = dataInput.readInt();
+ if (stateVersion <= STATE_VERSION) {
+ // If the state version is a version we can understand then read the MD5 sum,
+ // otherwise we return an empty byte array for the MD5 sum which will force a
+ // backup.
+ for (int i = 0; i < MD5_BYTE_SIZE; i++) {
+ oldMd5Checksum[i] = dataInput.readByte();
+ }
+ } else {
+ Log.i(TAG, "Backup state version is: " + stateVersion
+ + " (support only up to version " + STATE_VERSION + ")");
+ }
+ } catch (EOFException eof) {
+ // Initial state may be empty.
+ } finally {
+ dataInput.close();
+ }
+ return oldMd5Checksum;
+ }
+
+ /**
+ * Write the given checksum to the file descriptor.
+ */
+ private void writeNewMd5Checksum(ParcelFileDescriptor newState, byte[] md5Checksum)
+ throws IOException {
+ DataOutputStream dataOutput = new DataOutputStream(
+ new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor())));
+
+ dataOutput.writeInt(STATE_VERSION);
+ dataOutput.write(md5Checksum);
+ dataOutput.close();
+ }
+
+ private byte[] generateMd5Checksum(byte[] data) throws NoSuchAlgorithmException {
+ if (data == null) {
+ return null;
+ }
+
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ return md5.digest(data);
+ }
+
+ /**
+ * Restore account sync settings from the given data input stream.
+ */
+ @Override
+ public void restoreEntity(BackupDataInputStream data) {
+ byte[] dataBytes = new byte[data.size()];
+ try {
+ // Read the data and convert it to a String.
+ data.read(dataBytes);
+ String dataString = new String(dataBytes, JSON_FORMAT_ENCODING);
+
+ // Convert data to a JSON object.
+ JSONObject dataJSON = new JSONObject(dataString);
+ boolean masterSyncEnabled = dataJSON.getBoolean(KEY_MASTER_SYNC_ENABLED);
+ JSONArray accountJSONArray = dataJSON.getJSONArray(KEY_ACCOUNTS);
+
+ boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically();
+ if (currentMasterSyncEnabled) {
+ // Disable master sync to prevent any syncs from running.
+ ContentResolver.setMasterSyncAutomatically(false);
+ }
+
+ try {
+ HashSet<Account> currentAccounts = getAccountsHashSet();
+ for (int i = 0; i < accountJSONArray.length(); i++) {
+ JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
+ String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
+ String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
+
+ Account account = new Account(accountName, accountType);
+
+ // Check if the account already exists. Accounts that don't exist on the device
+ // yet won't be restored.
+ if (currentAccounts.contains(account)) {
+ restoreExistingAccountSyncSettingsFromJSON(accountJSON);
+ }
+ }
+ } finally {
+ // Set the master sync preference to the value from the backup set.
+ ContentResolver.setMasterSyncAutomatically(masterSyncEnabled);
+ }
+
+ Log.i(TAG, "Restore successful.");
+ } catch (IOException | JSONException e) {
+ Log.e(TAG, "Couldn't restore account sync settings\n" + e);
+ }
+ }
+
+ /**
+ * Helper method - fetch accounts and return them as a HashSet.
+ *
+ * @return Accounts in a HashSet.
+ */
+ private HashSet<Account> getAccountsHashSet() {
+ Account[] accounts = mAccountManager.getAccounts();
+ HashSet<Account> accountHashSet = new HashSet<Account>();
+ for (Account account : accounts) {
+ accountHashSet.add(account);
+ }
+ return accountHashSet;
+ }
+
+ /**
+ * Restore account sync settings using the given JSON. This function won't work if the account
+ * doesn't exist yet.
+ */
+ private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON)
+ throws JSONException {
+ // Restore authorities.
+ JSONArray authorities = accountJSON.getJSONArray(KEY_ACCOUNT_AUTHORITIES);
+ String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
+ String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
+ final Account account = new Account(accountName, accountType);
+ for (int i = 0; i < authorities.length(); i++) {
+ JSONObject authority = (JSONObject) authorities.get(i);
+ final String authorityName = authority.getString(KEY_AUTHORITY_NAME);
+ boolean syncEnabled = authority.getBoolean(KEY_AUTHORITY_SYNC_ENABLED);
+
+ // Cancel any active syncs.
+ if (ContentResolver.isSyncActive(account, authorityName)) {
+ ContentResolver.cancelSync(account, authorityName);
+ }
+
+ boolean overwriteSync = true;
+ Bundle initializationExtras = createSyncInitializationBundle();
+ int currentSyncState = ContentResolver.getIsSyncable(account, authorityName);
+ if (currentSyncState < 0) {
+ // Requesting a sync is an asynchronous operation, so we setup a countdown latch to
+ // wait for it to finish. Initialization syncs are generally very brief and
+ // shouldn't take too much time to finish.
+ final CountDownLatch latch = new CountDownLatch(1);
+ Object syncStatusObserverHandle = ContentResolver.addStatusChangeListener(
+ ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, new SyncStatusObserver() {
+ @Override
+ public void onStatusChanged(int which) {
+ if (!ContentResolver.isSyncActive(account, authorityName)) {
+ latch.countDown();
+ }
+ }
+ });
+
+ // If we set sync settings for a sync that hasn't been initialized yet, we run the
+ // risk of having our changes overwritten later on when the sync gets initialized.
+ // To prevent this from happening we will manually initiate the sync adapter. We
+ // also explicitly pass in a Bundle with SYNC_EXTRAS_INITIALIZE to prevent a data
+ // sync from running after the initialization sync. Two syncs will be scheduled, but
+ // the second one (data sync) will override the first one (initialization sync) and
+ // still behave as an initialization sync because of the Bundle.
+ ContentResolver.requestSync(account, authorityName, initializationExtras);
+
+ boolean done = false;
+ try {
+ done = latch.await(SYNC_REQUEST_LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "CountDownLatch interrupted\n" + e);
+ done = false;
+ }
+ if (!done) {
+ overwriteSync = false;
+ Log.i(TAG, "CountDownLatch timed out, skipping '" + authorityName
+ + "' authority.");
+ }
+ ContentResolver.removeStatusChangeListener(syncStatusObserverHandle);
+ }
+
+ if (overwriteSync) {
+ ContentResolver.setSyncAutomatically(account, authorityName, syncEnabled);
+ Log.i(TAG, "Set sync automatically for '" + authorityName + "': " + syncEnabled);
+ }
+ }
+ }
+
+ private Bundle createSyncInitializationBundle() {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+ return extras;
+ }
+
+ @Override
+ public void writeNewStateDescription(ParcelFileDescriptor newState) {
+
+ }
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 35a1a5a..b5f2f37 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -86,6 +86,8 @@ public class SystemBackupAgent extends BackupAgentHelper {
}
addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys));
addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
+ addHelper("account_sync_settings",
+ new AccountSyncSettingsBackupHelper(SystemBackupAgent.this));
super.onBackup(oldState, data, newState);
}
@@ -118,6 +120,8 @@ public class SystemBackupAgent extends BackupAgentHelper {
new String[] { WALLPAPER_IMAGE },
new String[] { WALLPAPER_IMAGE_KEY} ));
addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
+ addHelper("account_sync_settings",
+ new AccountSyncSettingsBackupHelper(SystemBackupAgent.this));
try {
super.onRestore(data, appVersionCode, newState);