From d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9 Mon Sep 17 00:00:00 2001 From: Eric Erfanian Date: Wed, 15 Mar 2017 14:41:07 -0700 Subject: Update Dialer source from latest green build. * Refactor voicemail component * Add new enriched calling components Test: treehugger, manual aosp testing Change-Id: I521a0f86327d4b42e14d93927c7d613044ed5942 --- .../voicemail/impl/utils/IndentingPrintWriter.java | 155 ++++++++++++++ .../impl/utils/VoicemailDatabaseUtil.java | 85 ++++++++ .../voicemail/impl/utils/VvmDumpHandler.java | 43 ++++ .../com/android/voicemail/impl/utils/XmlUtils.java | 238 +++++++++++++++++++++ 4 files changed, 521 insertions(+) create mode 100644 java/com/android/voicemail/impl/utils/IndentingPrintWriter.java create mode 100644 java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java create mode 100644 java/com/android/voicemail/impl/utils/VvmDumpHandler.java create mode 100644 java/com/android/voicemail/impl/utils/XmlUtils.java (limited to 'java/com/android/voicemail/impl/utils') diff --git a/java/com/android/voicemail/impl/utils/IndentingPrintWriter.java b/java/com/android/voicemail/impl/utils/IndentingPrintWriter.java new file mode 100644 index 000000000..bbc1d6601 --- /dev/null +++ b/java/com/android/voicemail/impl/utils/IndentingPrintWriter.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.voicemail.impl.utils; + +import java.io.PrintWriter; +import java.io.Writer; +import java.util.Arrays; + +/** + * Lightweight wrapper around {@link PrintWriter} that automatically indents newlines based on + * internal state. It also automatically wraps long lines based on given line length. + * + *

Delays writing indent until first actual write on a newline, enabling indent modification + * after newline. + */ +public class IndentingPrintWriter extends PrintWriter { + + private final String mSingleIndent; + private final int mWrapLength; + + /** Mutable version of current indent */ + private StringBuilder mIndentBuilder = new StringBuilder(); + /** Cache of current {@link #mIndentBuilder} value */ + private char[] mCurrentIndent; + /** Length of current line being built, excluding any indent */ + private int mCurrentLength; + + /** + * Flag indicating if we're currently sitting on an empty line, and that next write should be + * prefixed with the current indent. + */ + private boolean mEmptyLine = true; + + private char[] mSingleChar = new char[1]; + + public IndentingPrintWriter(Writer writer, String singleIndent) { + this(writer, singleIndent, -1); + } + + public IndentingPrintWriter(Writer writer, String singleIndent, int wrapLength) { + super(writer); + mSingleIndent = singleIndent; + mWrapLength = wrapLength; + } + + public void increaseIndent() { + mIndentBuilder.append(mSingleIndent); + mCurrentIndent = null; + } + + public void decreaseIndent() { + mIndentBuilder.delete(0, mSingleIndent.length()); + mCurrentIndent = null; + } + + public void printPair(String key, Object value) { + print(key + "=" + String.valueOf(value) + " "); + } + + public void printPair(String key, Object[] value) { + print(key + "=" + Arrays.toString(value) + " "); + } + + public void printHexPair(String key, int value) { + print(key + "=0x" + Integer.toHexString(value) + " "); + } + + @Override + public void println() { + write('\n'); + } + + @Override + public void write(int c) { + mSingleChar[0] = (char) c; + write(mSingleChar, 0, 1); + } + + @Override + public void write(String s, int off, int len) { + final char[] buf = new char[len]; + s.getChars(off, len - off, buf, 0); + write(buf, 0, len); + } + + @Override + public void write(char[] buf, int offset, int count) { + final int indentLength = mIndentBuilder.length(); + final int bufferEnd = offset + count; + int lineStart = offset; + int lineEnd = offset; + + // March through incoming buffer looking for newlines + while (lineEnd < bufferEnd) { + char ch = buf[lineEnd++]; + mCurrentLength++; + if (ch == '\n') { + maybeWriteIndent(); + super.write(buf, lineStart, lineEnd - lineStart); + lineStart = lineEnd; + mEmptyLine = true; + mCurrentLength = 0; + } + + // Wrap if we've pushed beyond line length + if (mWrapLength > 0 && mCurrentLength >= mWrapLength - indentLength) { + if (!mEmptyLine) { + // Give ourselves a fresh line to work with + super.write('\n'); + mEmptyLine = true; + mCurrentLength = lineEnd - lineStart; + } else { + // We need more than a dedicated line, slice it hard + maybeWriteIndent(); + super.write(buf, lineStart, lineEnd - lineStart); + super.write('\n'); + mEmptyLine = true; + lineStart = lineEnd; + mCurrentLength = 0; + } + } + } + + if (lineStart != lineEnd) { + maybeWriteIndent(); + super.write(buf, lineStart, lineEnd - lineStart); + } + } + + private void maybeWriteIndent() { + if (mEmptyLine) { + mEmptyLine = false; + if (mIndentBuilder.length() != 0) { + if (mCurrentIndent == null) { + mCurrentIndent = mIndentBuilder.toString().toCharArray(); + } + super.write(mCurrentIndent, 0, mCurrentIndent.length); + } + } + } +} diff --git a/java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java b/java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java new file mode 100644 index 000000000..711d6a8a4 --- /dev/null +++ b/java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.voicemail.impl.utils; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.net.Uri; +import android.provider.VoicemailContract.Voicemails; +import android.telecom.PhoneAccountHandle; +import com.android.voicemail.impl.Voicemail; +import java.util.List; + +public class VoicemailDatabaseUtil { + + /** + * Inserts a new voicemail into the voicemail content provider. + * + * @param context The context of the app doing the inserting + * @param voicemail Data to be inserted + * @return {@link Uri} of the newly inserted {@link Voicemail} + * @hide + */ + public static Uri insert(Context context, Voicemail voicemail) { + ContentResolver contentResolver = context.getContentResolver(); + ContentValues contentValues = getContentValues(voicemail); + return contentResolver.insert( + Voicemails.buildSourceUri(context.getPackageName()), contentValues); + } + + /** + * Inserts a list of voicemails into the voicemail content provider. + * + * @param context The context of the app doing the inserting + * @param voicemails Data to be inserted + * @return the number of voicemails inserted + * @hide + */ + public static int insert(Context context, List voicemails) { + for (Voicemail voicemail : voicemails) { + insert(context, voicemail); + } + return voicemails.size(); + } + + /** Maps structured {@link Voicemail} to {@link ContentValues} in content provider. */ + private static ContentValues getContentValues(Voicemail voicemail) { + ContentValues contentValues = new ContentValues(); + contentValues.put(Voicemails.DATE, String.valueOf(voicemail.getTimestampMillis())); + contentValues.put(Voicemails.NUMBER, voicemail.getNumber()); + contentValues.put(Voicemails.DURATION, String.valueOf(voicemail.getDuration())); + contentValues.put(Voicemails.SOURCE_PACKAGE, voicemail.getSourcePackage()); + contentValues.put(Voicemails.SOURCE_DATA, voicemail.getSourceData()); + contentValues.put(Voicemails.IS_READ, voicemail.isRead() ? 1 : 0); + contentValues.put(Voicemails.IS_OMTP_VOICEMAIL, 1); + + PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount(); + if (phoneAccount != null) { + contentValues.put( + Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, + phoneAccount.getComponentName().flattenToString()); + contentValues.put(Voicemails.PHONE_ACCOUNT_ID, phoneAccount.getId()); + } + + if (voicemail.getTranscription() != null) { + contentValues.put(Voicemails.TRANSCRIPTION, voicemail.getTranscription()); + } + + return contentValues; + } +} diff --git a/java/com/android/voicemail/impl/utils/VvmDumpHandler.java b/java/com/android/voicemail/impl/utils/VvmDumpHandler.java new file mode 100644 index 000000000..5290f2cbe --- /dev/null +++ b/java/com/android/voicemail/impl/utils/VvmDumpHandler.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.voicemail.impl.utils; + +import android.content.Context; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; +import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper; +import com.android.voicemail.impl.VvmLog; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +public class VvmDumpHandler { + + public static void dump(Context context, FileDescriptor fd, PrintWriter writer, String[] args) { + IndentingPrintWriter indentedWriter = new IndentingPrintWriter(writer, " "); + indentedWriter.println("******* OmtpVvm *******"); + indentedWriter.println("======= Configs ======="); + indentedWriter.increaseIndent(); + for (PhoneAccountHandle handle : + context.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts()) { + OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(context, handle); + indentedWriter.println(config.toString()); + } + indentedWriter.decreaseIndent(); + indentedWriter.println("======== Logs ========="); + VvmLog.dump(fd, indentedWriter, args); + } +} diff --git a/java/com/android/voicemail/impl/utils/XmlUtils.java b/java/com/android/voicemail/impl/utils/XmlUtils.java new file mode 100644 index 000000000..f5703f30f --- /dev/null +++ b/java/com/android/voicemail/impl/utils/XmlUtils.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.voicemail.impl.utils; + +import android.util.ArrayMap; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +public class XmlUtils { + + public static final ArrayMap readThisArrayMapXml( + XmlPullParser parser, String endTag, String[] name, ReadMapCallback callback) + throws XmlPullParserException, java.io.IOException { + ArrayMap map = new ArrayMap<>(); + + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + Object val = readThisValueXml(parser, name, callback, true); + map.put(name[0], val); + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(endTag)) { + return map; + } + throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + + throw new XmlPullParserException("Document ended before " + endTag + " end tag"); + } + + /** + * Read an ArrayList object from an XmlPullParser. The XML data could previously have been + * generated by writeListXml(). The XmlPullParser must be positioned after the tag that + * begins the list. + * + * @param parser The XmlPullParser from which to read the list data. + * @param endTag Name of the tag that will end the list, usually "list". + * @param name An array of one string, used to return the name attribute of the list's tag. + * @return HashMap The newly generated list. + */ + public static final ArrayList readThisListXml( + XmlPullParser parser, + String endTag, + String[] name, + ReadMapCallback callback, + boolean arrayMap) + throws XmlPullParserException, java.io.IOException { + ArrayList list = new ArrayList(); + + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + Object val = readThisValueXml(parser, name, callback, arrayMap); + list.add(val); + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(endTag)) { + return list; + } + throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + + throw new XmlPullParserException("Document ended before " + endTag + " end tag"); + } + + /** + * Read a String[] object from an XmlPullParser. The XML data could previously have been generated + * by writeStringArrayXml(). The XmlPullParser must be positioned after the tag that + * begins the list. + * + * @param parser The XmlPullParser from which to read the list data. + * @param endTag Name of the tag that will end the list, usually "string-array". + * @param name An array of one string, used to return the name attribute of the list's tag. + * @return Returns a newly generated String[]. + */ + public static String[] readThisStringArrayXml(XmlPullParser parser, String endTag, String[] name) + throws XmlPullParserException, java.io.IOException { + + parser.next(); + + List array = new ArrayList<>(); + + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("item")) { + try { + array.add(parser.getAttributeValue(null, "value")); + } catch (NullPointerException e) { + throw new XmlPullParserException("Need value attribute in item"); + } catch (NumberFormatException e) { + throw new XmlPullParserException("Not a number in value attribute in item"); + } + } else { + throw new XmlPullParserException("Expected item tag at: " + parser.getName()); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(endTag)) { + return array.toArray(new String[0]); + } else if (parser.getName().equals("item")) { + + } else { + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + } + eventType = parser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + + throw new XmlPullParserException("Document ended before " + endTag + " end tag"); + } + + private static Object readThisValueXml( + XmlPullParser parser, String[] name, ReadMapCallback callback, boolean arrayMap) + throws XmlPullParserException, java.io.IOException { + final String valueName = parser.getAttributeValue(null, "name"); + final String tagName = parser.getName(); + + Object res; + + if (tagName.equals("null")) { + res = null; + } else if (tagName.equals("string")) { + String value = ""; + int eventType; + while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("string")) { + name[0] = valueName; + return value; + } + throw new XmlPullParserException("Unexpected end tag in : " + parser.getName()); + } else if (eventType == XmlPullParser.TEXT) { + value += parser.getText(); + } else if (eventType == XmlPullParser.START_TAG) { + throw new XmlPullParserException("Unexpected start tag in : " + parser.getName()); + } + } + throw new XmlPullParserException("Unexpected end of document in "); + } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) { + // all work already done by readThisPrimitiveValueXml + } else if (tagName.equals("string-array")) { + res = readThisStringArrayXml(parser, "string-array", name); + name[0] = valueName; + return res; + } else if (tagName.equals("list")) { + parser.next(); + res = readThisListXml(parser, "list", name, callback, arrayMap); + name[0] = valueName; + return res; + } else if (callback != null) { + res = callback.readThisUnknownObjectXml(parser, tagName); + name[0] = valueName; + return res; + } else { + throw new XmlPullParserException("Unknown tag: " + tagName); + } + + // Skip through to end tag. + int eventType; + while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(tagName)) { + name[0] = valueName; + return res; + } + throw new XmlPullParserException( + "Unexpected end tag in <" + tagName + ">: " + parser.getName()); + } else if (eventType == XmlPullParser.TEXT) { + throw new XmlPullParserException( + "Unexpected text in <" + tagName + ">: " + parser.getName()); + } else if (eventType == XmlPullParser.START_TAG) { + throw new XmlPullParserException( + "Unexpected start tag in <" + tagName + ">: " + parser.getName()); + } + } + throw new XmlPullParserException("Unexpected end of document in <" + tagName + ">"); + } + + private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName) + throws XmlPullParserException, java.io.IOException { + try { + if (tagName.equals("int")) { + return Integer.parseInt(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("long")) { + return Long.valueOf(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("float")) { + return Float.valueOf(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("double")) { + return Double.valueOf(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("boolean")) { + return Boolean.valueOf(parser.getAttributeValue(null, "value")); + } else { + return null; + } + } catch (NullPointerException e) { + throw new XmlPullParserException("Need value attribute in <" + tagName + ">"); + } catch (NumberFormatException e) { + throw new XmlPullParserException("Not a number in value attribute in <" + tagName + ">"); + } + } + + public interface ReadMapCallback { + + /** + * Called from readThisMapXml when a START_TAG is not recognized. The input stream is positioned + * within the start tag so that attributes can be read using in.getAttribute. + * + * @param in the XML input stream + * @param tag the START_TAG that was not recognized. + * @return the Object parsed from the stream which will be put into the map. + * @throws XmlPullParserException if the START_TAG is not recognized. + * @throws IOException on XmlPullParser serialization errors. + */ + Object readThisUnknownObjectXml(XmlPullParser in, String tag) + throws XmlPullParserException, IOException; + } +} -- cgit v1.2.3