diff options
Diffstat (limited to 'core/java/com')
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); |