Move android-common to framework/ex
This is one necessary step by unbundling. Change-Id: I9d922a52374ad6331fa2e39fa4b5e16ad7d108fa
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
# Copyright (C) 2009 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
# Note: the source code is in java/, not src/, because this code is also part of
|
||||
# the framework library, and build/core/pathmap.mk expects a java/ subdirectory.
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := android-common
|
||||
LOCAL_SDK_VERSION := current
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-java-files-under, java) \
|
||||
$(call all-logtags-files-under, java)
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
# Include this library in the build server's output directory
|
||||
$(call dist-for-goals, droidcore, $(LOCAL_BUILT_MODULE):android-common.jar)
|
||||
|
||||
# Build the test package
|
||||
include $(call all-makefiles-under, $(LOCAL_PATH))
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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.common;
|
||||
|
||||
import android.database.AbstractCursor;
|
||||
import android.database.CursorWindow;
|
||||
|
||||
import java.lang.System;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A convenience class that presents a two-dimensional ArrayList
|
||||
* as a Cursor.
|
||||
* @deprecated This is has been replaced by MatrixCursor.
|
||||
*/
|
||||
public class ArrayListCursor extends AbstractCursor {
|
||||
private String[] mColumnNames;
|
||||
private ArrayList<Object>[] mRows;
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public ArrayListCursor(String[] columnNames, ArrayList<ArrayList> rows) {
|
||||
int colCount = columnNames.length;
|
||||
boolean foundID = false;
|
||||
// Add an _id column if not in columnNames
|
||||
for (int i = 0; i < colCount; ++i) {
|
||||
if (columnNames[i].compareToIgnoreCase("_id") == 0) {
|
||||
mColumnNames = columnNames;
|
||||
foundID = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundID) {
|
||||
mColumnNames = new String[colCount + 1];
|
||||
System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length);
|
||||
mColumnNames[colCount] = "_id";
|
||||
}
|
||||
|
||||
int rowCount = rows.size();
|
||||
mRows = new ArrayList[rowCount];
|
||||
|
||||
for (int i = 0; i < rowCount; ++i) {
|
||||
mRows[i] = rows.get(i);
|
||||
if (!foundID) {
|
||||
mRows[i].add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillWindow(int position, CursorWindow window) {
|
||||
if (position < 0 || position > getCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.acquireReference();
|
||||
try {
|
||||
int oldpos = mPos;
|
||||
mPos = position - 1;
|
||||
window.clear();
|
||||
window.setStartPosition(position);
|
||||
int columnNum = getColumnCount();
|
||||
window.setNumColumns(columnNum);
|
||||
while (moveToNext() && window.allocRow()) {
|
||||
for (int i = 0; i < columnNum; i++) {
|
||||
final Object data = mRows[mPos].get(i);
|
||||
if (data != null) {
|
||||
if (data instanceof byte[]) {
|
||||
byte[] field = (byte[]) data;
|
||||
if (!window.putBlob(field, mPos, i)) {
|
||||
window.freeLastRow();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
String field = data.toString();
|
||||
if (!window.putString(field, mPos, i)) {
|
||||
window.freeLastRow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!window.putNull(mPos, i)) {
|
||||
window.freeLastRow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mPos = oldpos;
|
||||
} catch (IllegalStateException e){
|
||||
// simply ignore it
|
||||
} finally {
|
||||
window.releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mRows.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return mColumnNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBlob(int columnIndex) {
|
||||
return (byte[]) mRows[mPos].get(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int columnIndex) {
|
||||
Object cell = mRows[mPos].get(columnIndex);
|
||||
return (cell == null) ? null : cell.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int columnIndex) {
|
||||
Number num = (Number) mRows[mPos].get(columnIndex);
|
||||
return num.shortValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) {
|
||||
Number num = (Number) mRows[mPos].get(columnIndex);
|
||||
return num.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int columnIndex) {
|
||||
Number num = (Number) mRows[mPos].get(columnIndex);
|
||||
return num.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int columnIndex) {
|
||||
Number num = (Number) mRows[mPos].get(columnIndex);
|
||||
return num.floatValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int columnIndex) {
|
||||
Number num = (Number) mRows[mPos].get(columnIndex);
|
||||
return num.doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int columnIndex) {
|
||||
return mRows[mPos].get(columnIndex) == null;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +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.
|
||||
|
||||
option java_package com.android.common
|
||||
|
||||
#####
|
||||
# This file contains definitions for event log (android.util.EventLog) tags
|
||||
# used by Google Mobile Services applications. These definitions are part of
|
||||
# the platform even when the Google applications are not.
|
||||
#
|
||||
# See system/core/logcat/event.logtags for a description of the file format.
|
||||
#
|
||||
# These event log tags must be assigned specific numbers (no "?") in the range
|
||||
# 200000-300000. If new tags are added, be aware that older platforms will be
|
||||
# missing the tag definitions, and may not be able to show them in their logs.
|
||||
|
||||
#####
|
||||
# System Update (OTA)
|
||||
|
||||
# System update status bits
|
||||
# [31- 9] Reserved for future use
|
||||
# [ 8- 7] package verified (0=not attempted, 1=succeeded, 2=failed)
|
||||
# [ 6] install approved
|
||||
# [ 5] download approved
|
||||
# [ 4- 0] status
|
||||
201001 system_update (status|1|5),(download_result|1|5),(bytes|2|2),(url|3)
|
||||
201002 system_update_user (action|3)
|
||||
|
||||
#####
|
||||
# Android Market
|
||||
|
||||
# @param changes Number of changes made to database in reconstruct
|
||||
202001 vending_reconstruct (changes|1)
|
||||
|
||||
#####
|
||||
# Google Services Framework
|
||||
|
||||
203001 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3)
|
||||
|
||||
203002 google_http_request (elapsed|2|3),(status|1),(appname|3),(reused|1)
|
||||
|
||||
#####
|
||||
# Google Talk Service
|
||||
|
||||
# This event is logged when GTalkService encounters important events
|
||||
204001 gtalkservice (eventType|1)
|
||||
# This event is logged for GTalk connection state changes. The status field is an int, but
|
||||
# it really contains 4 separate values, each taking up a byte
|
||||
# (eventType << 24) + (connection state << 16) + (connection error << 8) + network state
|
||||
204002 gtalk_connection (status|1)
|
||||
|
||||
# This event is logged when GTalk connection is closed.
|
||||
# The status field is an int, but contains 2 different values, it's represented as
|
||||
#
|
||||
# (networkType << 8) + connection error
|
||||
#
|
||||
# the possible error values are
|
||||
#
|
||||
# no_error=0, no_network=1, connection_failed=2, unknown_host=3, auth_failed=4,
|
||||
# auth_expired=5, heart_beat_timeout=6, server_error=7, server_reject_rate_limiting=8, unknown=10
|
||||
#
|
||||
# duration is the connection duration.
|
||||
204003 gtalk_conn_close (status|1),(duration|1)
|
||||
|
||||
# This event is logged for GTalk heartbeat resets
|
||||
# interval_and_nt contains both the heartbeat interval and the network type, It's represented as
|
||||
# (networkType << 16) + interval
|
||||
# interval is in seconds; network type can be 0 (mobile) or 1 (wifi); ip is the host ip addr.
|
||||
204004 gtalk_heartbeat_reset (interval_and_nt|1),(ip|3)
|
||||
|
||||
# This event is logged when an Rmq v2 packet is sent or received.
|
||||
204005 c2dm (packet_type|1),(persistent_id|3),(stream_id|1),(last_stream_id|1)
|
||||
|
||||
#####
|
||||
# Google Login Service and Setup Wizard
|
||||
|
||||
# This event is for when communicating to the server times out during account setup
|
||||
205001 setup_server_timeout
|
||||
205002 setup_required_captcha (action|3)
|
||||
205003 setup_io_error (status|3)
|
||||
205004 setup_server_error
|
||||
205005 setup_retries_exhausted
|
||||
205006 setup_no_data_network
|
||||
205007 setup_completed
|
||||
|
||||
205008 gls_account_tried (status|1)
|
||||
205009 gls_account_saved (status|1)
|
||||
205010 gls_authenticate (status|1),(service|3)
|
||||
205011 google_mail_switch (direction|1)
|
||||
@@ -1,224 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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.common;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* A wrapper for a broadcast receiver that provides network connectivity
|
||||
* state information, independent of network type (mobile, Wi-Fi, etc.).
|
||||
* @deprecated Code tempted to use this class should simply listen for connectivity intents
|
||||
* (or poll ConnectivityManager) directly.
|
||||
* {@hide}
|
||||
*/
|
||||
public class NetworkConnectivityListener {
|
||||
private static final String TAG = "NetworkConnectivityListener";
|
||||
private static final boolean DBG = false;
|
||||
|
||||
private Context mContext;
|
||||
private HashMap<Handler, Integer> mHandlers = new HashMap<Handler, Integer>();
|
||||
private State mState;
|
||||
private boolean mListening;
|
||||
private String mReason;
|
||||
private boolean mIsFailover;
|
||||
|
||||
/** Network connectivity information */
|
||||
private NetworkInfo mNetworkInfo;
|
||||
|
||||
/**
|
||||
* In case of a Disconnect, the connectivity manager may have
|
||||
* already established, or may be attempting to establish, connectivity
|
||||
* with another network. If so, {@code mOtherNetworkInfo} will be non-null.
|
||||
*/
|
||||
private NetworkInfo mOtherNetworkInfo;
|
||||
|
||||
private ConnectivityBroadcastReceiver mReceiver;
|
||||
|
||||
private class ConnectivityBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
|
||||
mListening == false) {
|
||||
Log.w(TAG, "onReceived() called with " + mState.toString() + " and " + intent);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean noConnectivity =
|
||||
intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
|
||||
|
||||
if (noConnectivity) {
|
||||
mState = State.NOT_CONNECTED;
|
||||
} else {
|
||||
mState = State.CONNECTED;
|
||||
}
|
||||
|
||||
mNetworkInfo = (NetworkInfo)
|
||||
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
|
||||
mOtherNetworkInfo = (NetworkInfo)
|
||||
intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
|
||||
|
||||
mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
|
||||
mIsFailover =
|
||||
intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
|
||||
|
||||
if (DBG) {
|
||||
Log.d(TAG, "onReceive(): mNetworkInfo=" + mNetworkInfo + " mOtherNetworkInfo = "
|
||||
+ (mOtherNetworkInfo == null ? "[none]" : mOtherNetworkInfo +
|
||||
" noConn=" + noConnectivity) + " mState=" + mState.toString());
|
||||
}
|
||||
|
||||
// Notifiy any handlers.
|
||||
Iterator<Handler> it = mHandlers.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Handler target = it.next();
|
||||
Message message = Message.obtain(target, mHandlers.get(target));
|
||||
target.sendMessage(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public enum State {
|
||||
UNKNOWN,
|
||||
|
||||
/** This state is returned if there is connectivity to any network **/
|
||||
CONNECTED,
|
||||
/**
|
||||
* This state is returned if there is no connectivity to any network. This is set
|
||||
* to true under two circumstances:
|
||||
* <ul>
|
||||
* <li>When connectivity is lost to one network, and there is no other available
|
||||
* network to attempt to switch to.</li>
|
||||
* <li>When connectivity is lost to one network, and the attempt to switch to
|
||||
* another network fails.</li>
|
||||
*/
|
||||
NOT_CONNECTED
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new NetworkConnectivityListener.
|
||||
*/
|
||||
public NetworkConnectivityListener() {
|
||||
mState = State.UNKNOWN;
|
||||
mReceiver = new ConnectivityBroadcastReceiver();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method starts listening for network connectivity state changes.
|
||||
* @param context
|
||||
*/
|
||||
public synchronized void startListening(Context context) {
|
||||
if (!mListening) {
|
||||
mContext = context;
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
context.registerReceiver(mReceiver, filter);
|
||||
mListening = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method stops this class from listening for network changes.
|
||||
*/
|
||||
public synchronized void stopListening() {
|
||||
if (mListening) {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
mContext = null;
|
||||
mNetworkInfo = null;
|
||||
mOtherNetworkInfo = null;
|
||||
mIsFailover = false;
|
||||
mReason = null;
|
||||
mListening = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods registers a Handler to be called back onto with the specified what code when
|
||||
* the network connectivity state changes.
|
||||
*
|
||||
* @param target The target handler.
|
||||
* @param what The what code to be used when posting a message to the handler.
|
||||
*/
|
||||
public void registerHandler(Handler target, int what) {
|
||||
mHandlers.put(target, what);
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods unregisters the specified Handler.
|
||||
* @param target
|
||||
*/
|
||||
public void unregisterHandler(Handler target) {
|
||||
mHandlers.remove(target);
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the NetworkInfo associated with the most recent connectivity event.
|
||||
* @return {@code NetworkInfo} for the network that had the most recent connectivity event.
|
||||
*/
|
||||
public NetworkInfo getNetworkInfo() {
|
||||
return mNetworkInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the most recent connectivity event was a DISCONNECT, return
|
||||
* any information supplied in the broadcast about an alternate
|
||||
* network that might be available. If this returns a non-null
|
||||
* value, then another broadcast should follow shortly indicating
|
||||
* whether connection to the other network succeeded.
|
||||
*
|
||||
* @return NetworkInfo
|
||||
*/
|
||||
public NetworkInfo getOtherNetworkInfo() {
|
||||
return mOtherNetworkInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the most recent event was for an attempt to switch over to
|
||||
* a new network following loss of connectivity on another network.
|
||||
* @return {@code true} if this was a failover attempt, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isFailover() {
|
||||
return mIsFailover;
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional reason for the connectivity state change may have been supplied.
|
||||
* This returns it.
|
||||
* @return the reason for the state change, if available, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public String getReason() {
|
||||
return mReason;
|
||||
}
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.common;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.http.AndroidHttpClient;
|
||||
import android.text.format.Time;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Tracks the success/failure history of a particular network operation in
|
||||
* persistent storage and computes retry strategy accordingly. Handles
|
||||
* exponential backoff, periodic rescheduling, event-driven triggering,
|
||||
* retry-after moratorium intervals, etc. based on caller-specified parameters.
|
||||
*
|
||||
* <p>This class does not directly perform or invoke any operations,
|
||||
* it only keeps track of the schedule. Somebody else needs to call
|
||||
* {@link #getNextTimeMillis()} as appropriate and do the actual work.
|
||||
*/
|
||||
public class OperationScheduler {
|
||||
/** Tunable parameter options for {@link #getNextTimeMillis}. */
|
||||
public static class Options {
|
||||
/** Wait this long after every error before retrying. */
|
||||
public long backoffFixedMillis = 0;
|
||||
|
||||
/** Wait this long times the number of consecutive errors so far before retrying. */
|
||||
public long backoffIncrementalMillis = 5000;
|
||||
|
||||
/** Maximum duration of moratorium to honor. Mostly an issue for clock rollbacks. */
|
||||
public long maxMoratoriumMillis = 24 * 3600 * 1000;
|
||||
|
||||
/** Minimum duration after success to wait before allowing another trigger. */
|
||||
public long minTriggerMillis = 0;
|
||||
|
||||
/** Automatically trigger this long after the last success. */
|
||||
public long periodicIntervalMillis = 0;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"OperationScheduler.Options[backoff=%.1f+%.1f max=%.1f min=%.1f period=%.1f]",
|
||||
backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0,
|
||||
maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0,
|
||||
periodicIntervalMillis / 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PREFIX = "OperationScheduler_";
|
||||
private final SharedPreferences mStorage;
|
||||
|
||||
/**
|
||||
* Initialize the scheduler state.
|
||||
* @param storage to use for recording the state of operations across restarts/reboots
|
||||
*/
|
||||
public OperationScheduler(SharedPreferences storage) {
|
||||
mStorage = storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse scheduler options supplied in this string form:
|
||||
*
|
||||
* <pre>
|
||||
* backoff=(fixed)+(incremental) max=(maxmoratorium) min=(mintrigger) [period=](interval)
|
||||
* </pre>
|
||||
*
|
||||
* All values are times in (possibly fractional) <em>seconds</em> (not milliseconds).
|
||||
* Omitted settings are left at whatever existing default value was passed in.
|
||||
*
|
||||
* <p>
|
||||
* The default options: <code>backoff=0+5 max=86400 min=0 period=0</code><br>
|
||||
* Fractions are OK: <code>backoff=+2.5 period=10.0</code><br>
|
||||
* The "period=" can be omitted: <code>3600</code><br>
|
||||
*
|
||||
* @param spec describing some or all scheduler options.
|
||||
* @param options to update with parsed values.
|
||||
* @return the options passed in (for convenience)
|
||||
* @throws IllegalArgumentException if the syntax is invalid
|
||||
*/
|
||||
public static Options parseOptions(String spec, Options options)
|
||||
throws IllegalArgumentException {
|
||||
for (String param : spec.split(" +")) {
|
||||
if (param.length() == 0) continue;
|
||||
if (param.startsWith("backoff=")) {
|
||||
int plus = param.indexOf('+', 8);
|
||||
if (plus < 0) {
|
||||
options.backoffFixedMillis = parseSeconds(param.substring(8));
|
||||
} else {
|
||||
if (plus > 8) {
|
||||
options.backoffFixedMillis = parseSeconds(param.substring(8, plus));
|
||||
}
|
||||
options.backoffIncrementalMillis = parseSeconds(param.substring(plus + 1));
|
||||
}
|
||||
} else if (param.startsWith("max=")) {
|
||||
options.maxMoratoriumMillis = parseSeconds(param.substring(4));
|
||||
} else if (param.startsWith("min=")) {
|
||||
options.minTriggerMillis = parseSeconds(param.substring(4));
|
||||
} else if (param.startsWith("period=")) {
|
||||
options.periodicIntervalMillis = parseSeconds(param.substring(7));
|
||||
} else {
|
||||
options.periodicIntervalMillis = parseSeconds(param);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private static long parseSeconds(String param) throws NumberFormatException {
|
||||
return (long) (Float.parseFloat(param) * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the time of the next operation. Does not modify any state
|
||||
* (unless the clock rolls backwards, in which case timers are reset).
|
||||
*
|
||||
* @param options to use for this computation.
|
||||
* @return the wall clock time ({@link System#currentTimeMillis()}) when the
|
||||
* next operation should be attempted -- immediately, if the return value is
|
||||
* before the current time.
|
||||
*/
|
||||
public long getNextTimeMillis(Options options) {
|
||||
boolean enabledState = mStorage.getBoolean(PREFIX + "enabledState", true);
|
||||
if (!enabledState) return Long.MAX_VALUE;
|
||||
|
||||
boolean permanentError = mStorage.getBoolean(PREFIX + "permanentError", false);
|
||||
if (permanentError) return Long.MAX_VALUE;
|
||||
|
||||
// We do quite a bit of limiting to prevent a clock rollback from totally
|
||||
// hosing the scheduler. Times which are supposed to be in the past are
|
||||
// clipped to the current time so we don't languish forever.
|
||||
|
||||
int errorCount = mStorage.getInt(PREFIX + "errorCount", 0);
|
||||
long now = currentTimeMillis();
|
||||
long lastSuccessTimeMillis = getTimeBefore(PREFIX + "lastSuccessTimeMillis", now);
|
||||
long lastErrorTimeMillis = getTimeBefore(PREFIX + "lastErrorTimeMillis", now);
|
||||
long triggerTimeMillis = mStorage.getLong(PREFIX + "triggerTimeMillis", Long.MAX_VALUE);
|
||||
long moratoriumSetMillis = getTimeBefore(PREFIX + "moratoriumSetTimeMillis", now);
|
||||
long moratoriumTimeMillis = getTimeBefore(PREFIX + "moratoriumTimeMillis",
|
||||
moratoriumSetMillis + options.maxMoratoriumMillis);
|
||||
|
||||
long time = triggerTimeMillis;
|
||||
if (options.periodicIntervalMillis > 0) {
|
||||
time = Math.min(time, lastSuccessTimeMillis + options.periodicIntervalMillis);
|
||||
}
|
||||
|
||||
time = Math.max(time, moratoriumTimeMillis);
|
||||
time = Math.max(time, lastSuccessTimeMillis + options.minTriggerMillis);
|
||||
if (errorCount > 0) {
|
||||
time = Math.max(time, lastErrorTimeMillis + options.backoffFixedMillis +
|
||||
options.backoffIncrementalMillis * errorCount);
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last time the operation completed. Does not modify any state.
|
||||
*
|
||||
* @return the wall clock time when {@link #onSuccess()} was last called.
|
||||
*/
|
||||
public long getLastSuccessTimeMillis() {
|
||||
return mStorage.getLong(PREFIX + "lastSuccessTimeMillis", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last time the operation was attempted. Does not modify any state.
|
||||
*
|
||||
* @return the wall clock time when {@link #onSuccess()} or {@link
|
||||
* #onTransientError()} was last called.
|
||||
*/
|
||||
public long getLastAttemptTimeMillis() {
|
||||
return Math.max(
|
||||
mStorage.getLong(PREFIX + "lastSuccessTimeMillis", 0),
|
||||
mStorage.getLong(PREFIX + "lastErrorTimeMillis", 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a {@link SharedPreferences} property, but force it to be before
|
||||
* a certain time, updating the value if necessary. This is to recover
|
||||
* gracefully from clock rollbacks which could otherwise strand our timers.
|
||||
*
|
||||
* @param name of SharedPreferences key
|
||||
* @param max time to allow in result
|
||||
* @return current value attached to key (default 0), limited by max
|
||||
*/
|
||||
private long getTimeBefore(String name, long max) {
|
||||
long time = mStorage.getLong(name, 0);
|
||||
if (time > max) mStorage.edit().putLong(name, (time = max)).commit();
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request an operation to be performed at a certain time. The actual
|
||||
* scheduled time may be affected by error backoff logic and defined
|
||||
* minimum intervals. Use {@link Long#MAX_VALUE} to disable triggering.
|
||||
*
|
||||
* @param millis wall clock time ({@link System#currentTimeMillis()}) to
|
||||
* trigger another operation; 0 to trigger immediately
|
||||
*/
|
||||
public void setTriggerTimeMillis(long millis) {
|
||||
mStorage.edit().putLong(PREFIX + "triggerTimeMillis", millis).commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forbid any operations until after a certain (absolute) time.
|
||||
* Limited by {@link #Options.maxMoratoriumMillis}.
|
||||
*
|
||||
* @param millis wall clock time ({@link System#currentTimeMillis()})
|
||||
* when operations should be allowed again; 0 to remove moratorium
|
||||
*/
|
||||
public void setMoratoriumTimeMillis(long millis) {
|
||||
mStorage.edit()
|
||||
.putLong(PREFIX + "moratoriumTimeMillis", millis)
|
||||
.putLong(PREFIX + "moratoriumSetTimeMillis", currentTimeMillis())
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forbid any operations until after a certain time, as specified in
|
||||
* the format used by the HTTP "Retry-After" header.
|
||||
* Limited by {@link #Options.maxMoratoriumMillis}.
|
||||
*
|
||||
* @param retryAfter moratorium time in HTTP format
|
||||
* @return true if a time was successfully parsed
|
||||
*/
|
||||
public boolean setMoratoriumTimeHttp(String retryAfter) {
|
||||
try {
|
||||
long ms = Long.valueOf(retryAfter) * 1000;
|
||||
setMoratoriumTimeMillis(ms + currentTimeMillis());
|
||||
return true;
|
||||
} catch (NumberFormatException nfe) {
|
||||
try {
|
||||
setMoratoriumTimeMillis(AndroidHttpClient.parseDate(retryAfter));
|
||||
return true;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable all operations. When disabled, all calls to
|
||||
* {@link #getNextTimeMillis()} return {@link Long#MAX_VALUE}.
|
||||
* Commonly used when data network availability goes up and down.
|
||||
*
|
||||
* @param enabled if operations can be performed
|
||||
*/
|
||||
public void setEnabledState(boolean enabled) {
|
||||
mStorage.edit().putBoolean(PREFIX + "enabledState", enabled).commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Report successful completion of an operation. Resets all error
|
||||
* counters, clears any trigger directives, and records the success.
|
||||
*/
|
||||
public void onSuccess() {
|
||||
resetTransientError();
|
||||
resetPermanentError();
|
||||
mStorage.edit()
|
||||
.remove(PREFIX + "errorCount")
|
||||
.remove(PREFIX + "lastErrorTimeMillis")
|
||||
.remove(PREFIX + "permanentError")
|
||||
.remove(PREFIX + "triggerTimeMillis")
|
||||
.putLong(PREFIX + "lastSuccessTimeMillis", currentTimeMillis()).commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a transient error (usually a network failure). Increments
|
||||
* the error count and records the time of the latest error for backoff
|
||||
* purposes.
|
||||
*/
|
||||
public void onTransientError() {
|
||||
mStorage.edit().putLong(PREFIX + "lastErrorTimeMillis", currentTimeMillis()).commit();
|
||||
mStorage.edit().putInt(PREFIX + "errorCount",
|
||||
mStorage.getInt(PREFIX + "errorCount", 0) + 1).commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all transient error counts, allowing the next operation to proceed
|
||||
* immediately without backoff. Commonly used on network state changes, when
|
||||
* partial progress occurs (some data received), and in other circumstances
|
||||
* where there is reason to hope things might start working better.
|
||||
*/
|
||||
public void resetTransientError() {
|
||||
mStorage.edit().remove(PREFIX + "errorCount").commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a permanent error that will not go away until further notice.
|
||||
* No operation will be scheduled until {@link #resetPermanentError()}
|
||||
* is called. Commonly used for authentication failures (which are reset
|
||||
* when the accounts database is updated).
|
||||
*/
|
||||
public void onPermanentError() {
|
||||
mStorage.edit().putBoolean(PREFIX + "permanentError", true).commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset any permanent error status set by {@link #onPermanentError},
|
||||
* allowing operations to be scheduled as normal.
|
||||
*/
|
||||
public void resetPermanentError() {
|
||||
mStorage.edit().remove(PREFIX + "permanentError").commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string description of the scheduler state for debugging.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder out = new StringBuilder("[OperationScheduler:");
|
||||
for (String key : new TreeSet<String>(mStorage.getAll().keySet())) { // Sort keys
|
||||
if (key.startsWith(PREFIX)) {
|
||||
if (key.endsWith("TimeMillis")) {
|
||||
Time time = new Time();
|
||||
time.set(mStorage.getLong(key, 0));
|
||||
out.append(" ").append(key.substring(PREFIX.length(), key.length() - 10));
|
||||
out.append("=").append(time.format("%Y-%m-%d/%H:%M:%S"));
|
||||
} else {
|
||||
out.append(" ").append(key.substring(PREFIX.length()));
|
||||
out.append("=").append(mStorage.getAll().get(key).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.append("]").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time. Can be overridden for unit testing.
|
||||
*
|
||||
* @return {@link System#currentTimeMillis()}
|
||||
*/
|
||||
protected long currentTimeMillis() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +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.common;
|
||||
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
/**
|
||||
* Implements special address cleanup rules:
|
||||
* The first space key entry following an "@" symbol that is followed by any combination
|
||||
* of letters and symbols, including one+ dots and zero commas, should insert an extra
|
||||
* comma (followed by the space).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class Rfc822InputFilter implements InputFilter {
|
||||
|
||||
public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
|
||||
int dstart, int dend) {
|
||||
|
||||
// quick check - did they enter a single space?
|
||||
if (end-start != 1 || source.charAt(start) != ' ') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// determine if the characters before the new space fit the pattern
|
||||
// follow backwards and see if we find a comma, dot, or @
|
||||
int scanBack = dstart;
|
||||
boolean dotFound = false;
|
||||
while (scanBack > 0) {
|
||||
char c = dest.charAt(--scanBack);
|
||||
switch (c) {
|
||||
case '.':
|
||||
dotFound = true; // one or more dots are req'd
|
||||
break;
|
||||
case ',':
|
||||
return null;
|
||||
case '@':
|
||||
if (!dotFound) {
|
||||
return null;
|
||||
}
|
||||
// we have found a comma-insert case. now just do it
|
||||
// in the least expensive way we can.
|
||||
if (source instanceof Spanned) {
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder(",");
|
||||
sb.append(source);
|
||||
return sb;
|
||||
} else {
|
||||
return ", ";
|
||||
}
|
||||
default:
|
||||
// just keep going
|
||||
}
|
||||
}
|
||||
|
||||
// no termination cases were found, so don't edit the input
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,134 +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.common;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.text.util.Rfc822Token;
|
||||
import android.text.util.Rfc822Tokenizer;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This class works as a Validator for AutoCompleteTextView for
|
||||
* email addresses. If a token does not appear to be a valid address,
|
||||
* it is trimmed of characters that cannot legitimately appear in one
|
||||
* and has the specified domain name added. It is meant for use with
|
||||
* {@link Rfc822Token} and {@link Rfc822Tokenizer}.
|
||||
*
|
||||
* @deprecated In the future make sure we don't quietly alter the user's
|
||||
* text in ways they did not intend. Meanwhile, hide this
|
||||
* class from the public API because it does not even have
|
||||
* a full understanding of the syntax it claims to correct.
|
||||
* @hide
|
||||
*/
|
||||
public class Rfc822Validator implements AutoCompleteTextView.Validator {
|
||||
/*
|
||||
* Regex.EMAIL_ADDRESS_PATTERN hardcodes the TLD that we accept, but we
|
||||
* want to make sure we will keep accepting email addresses with TLD's
|
||||
* that don't exist at the time of this writing, so this regexp relaxes
|
||||
* that constraint by accepting any kind of top level domain, not just
|
||||
* ".com", ".fr", etc...
|
||||
*/
|
||||
private static final Pattern EMAIL_ADDRESS_PATTERN =
|
||||
Pattern.compile("[^\\s@]+@[^\\s@]+\\.[a-zA-z][a-zA-Z][a-zA-Z]*");
|
||||
|
||||
private String mDomain;
|
||||
|
||||
/**
|
||||
* Constructs a new validator that uses the specified domain name as
|
||||
* the default when none is specified.
|
||||
*/
|
||||
public Rfc822Validator(String domain) {
|
||||
mDomain = domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isValid(CharSequence text) {
|
||||
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);
|
||||
|
||||
return tokens.length == 1 &&
|
||||
EMAIL_ADDRESS_PATTERN.
|
||||
matcher(tokens[0].getAddress()).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a string in which all the characters that are illegal for the username
|
||||
* or the domain name part of the email address have been removed.
|
||||
*/
|
||||
private String removeIllegalCharacters(String s) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
int length = s.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
/*
|
||||
* An RFC822 atom can contain any ASCII printing character
|
||||
* except for periods and any of the following punctuation.
|
||||
* A local-part can contain multiple atoms, concatenated by
|
||||
* periods, so do allow periods here.
|
||||
*/
|
||||
|
||||
if (c <= ' ' || c > '~') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '(' || c == ')' || c == '<' || c == '>' ||
|
||||
c == '@' || c == ',' || c == ';' || c == ':' ||
|
||||
c == '\\' || c == '"' || c == '[' || c == ']') {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.append(c);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public CharSequence fixText(CharSequence cs) {
|
||||
// Return an empty string if the email address only contains spaces, \n or \t
|
||||
if (TextUtils.getTrimmedLength(cs) == 0) return "";
|
||||
|
||||
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String text = tokens[i].getAddress();
|
||||
int index = text.indexOf('@');
|
||||
if (index < 0) {
|
||||
// If there is no @, just append the domain of the account
|
||||
tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
|
||||
} else {
|
||||
// Otherwise, remove the illegal characters on both sides of the '@'
|
||||
String fix = removeIllegalCharacters(text.substring(0, index));
|
||||
String domain = removeIllegalCharacters(text.substring(index + 1));
|
||||
tokens[i].setAddress(fix + "@" + (domain.length() != 0 ? domain : mDomain));
|
||||
}
|
||||
|
||||
sb.append(tokens[i].toString());
|
||||
if (i + 1 < tokens.length) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +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.common;
|
||||
|
||||
/**
|
||||
* Utilities for search implementations.
|
||||
*
|
||||
* @see android.app.SearchManager
|
||||
*/
|
||||
public class Search {
|
||||
|
||||
/**
|
||||
* Key for the source identifier set by the application that launched a search intent.
|
||||
* The identifier is search-source specific string. It can be used
|
||||
* by the search provider to keep statistics of where searches are started from.
|
||||
*
|
||||
* The source identifier is stored in the {@link android.app.SearchManager#APP_DATA}
|
||||
* Bundle in {@link android.content.Intent#ACTION_SEARCH} and
|
||||
* {@link android.content.Intent#ACTION_WEB_SEARCH} intents.
|
||||
*/
|
||||
public final static String SOURCE = "source";
|
||||
|
||||
private Search() { } // don't instantiate
|
||||
}
|
||||
@@ -1,136 +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.common.speech;
|
||||
|
||||
/**
|
||||
* Logging event constants used for Voice Search and VoiceIME. These are the
|
||||
* keys and values of extras to be specified in logging broadcast intents.
|
||||
* This class is used by clients of the android.speech APIs to log how the
|
||||
* user interacts with the IME settings and speech recognition result.
|
||||
*/
|
||||
public class LoggingEvents {
|
||||
// The name of the broadcast intent for logging.
|
||||
public static final String ACTION_LOG_EVENT = "com.android.common.speech.LOG_EVENT";
|
||||
|
||||
// The extra key used for the name of the app being logged.
|
||||
public static final String EXTRA_APP_NAME = "app_name";
|
||||
|
||||
// The extra key used for the name of the app issuing the VoiceSearch
|
||||
// or VoiceIME request
|
||||
public static final String EXTRA_CALLING_APP_NAME = "";
|
||||
|
||||
// The extra key used for the event value. The possible event values depend
|
||||
// on the app being logged for, and are defined in the subclasses below.
|
||||
public static final String EXTRA_EVENT = "extra_event";
|
||||
|
||||
// The extra key used to log the time in milliseconds at which the EXTRA_EVENT
|
||||
// occurred in the client.
|
||||
public static final String EXTRA_TIMESTAMP = "timestamp";
|
||||
|
||||
// The extra key used (with a boolean value of 'true') as a way to trigger a
|
||||
// flush of the log events to the server.
|
||||
public static final String EXTRA_FLUSH = "flush";
|
||||
|
||||
/**
|
||||
* Logging event constants for voice search. Below are the extra values for
|
||||
* {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
|
||||
* extras for some events that need to be included as additional fields in
|
||||
* the event. Note that this is not representative of *all* voice search
|
||||
* events - only the ones that need to be reported from outside the voice
|
||||
* search app, such as from Browser.
|
||||
*/
|
||||
public class VoiceSearch {
|
||||
// The app name to be used for logging VoiceSearch events.
|
||||
public static final String APP_NAME = "googlemobile";
|
||||
|
||||
public static final int RETRY = 0;
|
||||
|
||||
public static final int N_BEST_REVEAL = 1;
|
||||
|
||||
public static final int N_BEST_CHOOSE = 2;
|
||||
public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index"; // value should be int
|
||||
|
||||
public static final int QUERY_UPDATED = 3;
|
||||
public static final String EXTRA_QUERY_UPDATED_VALUE = "value"; // value should be String
|
||||
}
|
||||
|
||||
/**
|
||||
* Logging event constants for VoiceIME. Below are the extra values for
|
||||
* {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
|
||||
* extras for some events that need to be included as additional fields in
|
||||
* the event.
|
||||
*/
|
||||
public class VoiceIme {
|
||||
// The app name to be used for logging VoiceIME events.
|
||||
public static final String APP_NAME = "voiceime";
|
||||
|
||||
public static final int KEYBOARD_WARNING_DIALOG_SHOWN = 0;
|
||||
|
||||
public static final int KEYBOARD_WARNING_DIALOG_DISMISSED = 1;
|
||||
|
||||
public static final int KEYBOARD_WARNING_DIALOG_OK = 2;
|
||||
|
||||
public static final int KEYBOARD_WARNING_DIALOG_CANCEL = 3;
|
||||
|
||||
public static final int SETTINGS_WARNING_DIALOG_SHOWN = 4;
|
||||
|
||||
public static final int SETTINGS_WARNING_DIALOG_DISMISSED = 5;
|
||||
|
||||
public static final int SETTINGS_WARNING_DIALOG_OK = 6;
|
||||
|
||||
public static final int SETTINGS_WARNING_DIALOG_CANCEL = 7;
|
||||
|
||||
public static final int SWIPE_HINT_DISPLAYED = 8;
|
||||
|
||||
public static final int PUNCTUATION_HINT_DISPLAYED = 9;
|
||||
|
||||
public static final int CANCEL_DURING_LISTENING = 10;
|
||||
|
||||
public static final int CANCEL_DURING_WORKING = 11;
|
||||
|
||||
public static final int CANCEL_DURING_ERROR = 12;
|
||||
|
||||
public static final int ERROR = 13;
|
||||
public static final String EXTRA_ERROR_CODE = "code"; // value should be int
|
||||
|
||||
public static final int START = 14;
|
||||
public static final String EXTRA_START_LOCALE = "locale"; // value should be String
|
||||
public static final String EXTRA_START_SWIPE = "swipe"; // value should be boolean
|
||||
|
||||
public static final int VOICE_INPUT_DELIVERED = 15;
|
||||
|
||||
public static final int N_BEST_CHOOSE = 16;
|
||||
public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index"; // value should be int
|
||||
|
||||
public static final int TEXT_MODIFIED = 17;
|
||||
public static final String EXTRA_TEXT_MODIFIED_LENGTH = "length"; // value should be int
|
||||
public static final String EXTRA_TEXT_MODIFIED_TYPE = "type"; // value should be int below
|
||||
public static final int TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION = 1;
|
||||
public static final int TEXT_MODIFIED_TYPE_TYPING_DELETION = 2;
|
||||
public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION = 3;
|
||||
public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION_PUNCTUATION = 4;
|
||||
|
||||
public static final int INPUT_ENDED = 18;
|
||||
|
||||
public static final int VOICE_INPUT_SETTING_ENABLED = 19;
|
||||
|
||||
public static final int VOICE_INPUT_SETTING_DISABLED = 20;
|
||||
|
||||
public static final int IME_TEXT_ACCEPTED = 21;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +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.common.speech;
|
||||
|
||||
/**
|
||||
* Utilities for voice recognition implementations.
|
||||
*
|
||||
* @see android.speech.RecognitionService
|
||||
* @see android.speech.RecognizerIntent
|
||||
*/
|
||||
public class Recognition {
|
||||
|
||||
/**
|
||||
* The key to the extra in the Bundle returned by
|
||||
* android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS
|
||||
* which is an ArrayList of CharSequences which are hints that can be shown to
|
||||
* the user for voice actions currently supported by voice search for the user's current
|
||||
* language preference for voice search (i.e., the one defined in the extra
|
||||
* android.speech.RecognizerIntent#EXTRA_LANGUAGE_PREFERENCE).
|
||||
*
|
||||
* If this is paired with EXTRA_HINT_CONTEXT, should return a set of hints that are
|
||||
* appropriate for the provided context.
|
||||
*
|
||||
* The CharSequences are SpannedStrings and will contain segments wrapped in
|
||||
* <annotation action="true"></annotation>. This is to indicate the section of the text
|
||||
* which represents the voice action, to be highlighted in the UI if so desired.
|
||||
*/
|
||||
public static final String EXTRA_HINT_STRINGS = "android.speech.extra.HINT_STRINGS";
|
||||
|
||||
/**
|
||||
* The key to an extra to be included in the request intent for
|
||||
* android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS.
|
||||
* Should be an int of one of the values defined below. If an
|
||||
* unknown int value is provided, it should be ignored.
|
||||
*/
|
||||
public static final String EXTRA_HINT_CONTEXT = "android.speech.extra.HINT_CONTEXT";
|
||||
|
||||
/**
|
||||
* A set of values for EXTRA_HINT_CONTEXT.
|
||||
*/
|
||||
public static final int HINT_CONTEXT_UNKNOWN = 0;
|
||||
public static final int HINT_CONTEXT_VOICE_SEARCH_HELP = 1;
|
||||
public static final int HINT_CONTEXT_CAR_HOME = 2;
|
||||
public static final int HINT_CONTEXT_LAUNCHER = 3;
|
||||
|
||||
private Recognition() { } // don't instantiate
|
||||
}
|
||||
@@ -1,45 +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.common.userhappiness;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import com.android.common.speech.LoggingEvents;
|
||||
|
||||
/**
|
||||
* Metrics for User Happiness are recorded here. Each app can define when to
|
||||
* call these User Happiness metrics.
|
||||
*/
|
||||
public class UserHappinessSignals {
|
||||
|
||||
/**
|
||||
* Log when a user "accepted" IME text. Each application can define what
|
||||
* it means to "accept" text. In the case of Gmail, pressing the "Send"
|
||||
* button indicates text acceptance. We broadcast this information to
|
||||
* VoiceSearch LoggingEvents and use it to aggregate VoiceIME Happiness Metrics
|
||||
*/
|
||||
public static void userAcceptedImeText(Context context) {
|
||||
// Create a Voice IME Logging intent.
|
||||
Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
|
||||
i.putExtra(LoggingEvents.EXTRA_APP_NAME, LoggingEvents.VoiceIme.APP_NAME);
|
||||
i.putExtra(LoggingEvents.EXTRA_EVENT, LoggingEvents.VoiceIme.IME_TEXT_ACCEPTED);
|
||||
i.putExtra(LoggingEvents.EXTRA_CALLING_APP_NAME, context.getPackageName());
|
||||
i.putExtra(LoggingEvents.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
context.sendBroadcast(i);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
# Copyright (C) 2009 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CERTIFICATE := platform
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_PACKAGE_NAME := AndroidCommonTests
|
||||
LOCAL_SDK_VERSION := current
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := android-common
|
||||
|
||||
LOCAL_PROGUARD_ENABLED := disabled
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2009 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.common.tests"
|
||||
android:sharedUserId="com.android.uid.test">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<!-- Run tests with "runtest common" -->
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="com.android.common.tests"
|
||||
android:label="Android Common Library Tests" />
|
||||
|
||||
</manifest>
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.common;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
public class OperationSchedulerTest extends AndroidTestCase {
|
||||
/**
|
||||
* OperationScheduler subclass which uses an artificial time.
|
||||
* Set {@link #timeMillis} to whatever value you like.
|
||||
*/
|
||||
private class TimeTravelScheduler extends OperationScheduler {
|
||||
static final long DEFAULT_TIME = 1250146800000L; // 13-Aug-2009, 12:00:00 am
|
||||
public long timeMillis = DEFAULT_TIME;
|
||||
|
||||
@Override
|
||||
protected long currentTimeMillis() { return timeMillis; }
|
||||
public TimeTravelScheduler() { super(getFreshStorage()); }
|
||||
}
|
||||
|
||||
private SharedPreferences getFreshStorage() {
|
||||
SharedPreferences sp = getContext().getSharedPreferences("OperationSchedulerTest", 0);
|
||||
sp.edit().clear().commit();
|
||||
return sp;
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testScheduler() throws Exception {
|
||||
TimeTravelScheduler scheduler = new TimeTravelScheduler();
|
||||
OperationScheduler.Options options = new OperationScheduler.Options();
|
||||
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
|
||||
assertEquals(0, scheduler.getLastSuccessTimeMillis());
|
||||
assertEquals(0, scheduler.getLastAttemptTimeMillis());
|
||||
|
||||
long beforeTrigger = scheduler.timeMillis;
|
||||
scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
|
||||
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// It will schedule for the later of the trigger and the moratorium...
|
||||
scheduler.setMoratoriumTimeMillis(beforeTrigger + 500000);
|
||||
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
|
||||
scheduler.setMoratoriumTimeMillis(beforeTrigger + 1500000);
|
||||
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Test enable/disable toggle
|
||||
scheduler.setEnabledState(false);
|
||||
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
|
||||
scheduler.setEnabledState(true);
|
||||
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Backoff interval after an error
|
||||
long beforeError = (scheduler.timeMillis += 100);
|
||||
scheduler.onTransientError();
|
||||
assertEquals(0, scheduler.getLastSuccessTimeMillis());
|
||||
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
|
||||
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
|
||||
options.backoffFixedMillis = 1000000;
|
||||
options.backoffIncrementalMillis = 500000;
|
||||
assertEquals(beforeError + 1500000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Two errors: backoff interval increases
|
||||
beforeError = (scheduler.timeMillis += 100);
|
||||
scheduler.onTransientError();
|
||||
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
|
||||
assertEquals(beforeError + 2000000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Reset transient error: no backoff interval
|
||||
scheduler.resetTransientError();
|
||||
assertEquals(0, scheduler.getLastSuccessTimeMillis());
|
||||
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
|
||||
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
|
||||
|
||||
// Permanent error holds true even if transient errors are reset
|
||||
// However, we remember that the transient error was reset...
|
||||
scheduler.onPermanentError();
|
||||
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
|
||||
scheduler.resetTransientError();
|
||||
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
|
||||
scheduler.resetPermanentError();
|
||||
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Success resets the trigger
|
||||
long beforeSuccess = (scheduler.timeMillis += 100);
|
||||
scheduler.onSuccess();
|
||||
assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
|
||||
assertEquals(beforeSuccess, scheduler.getLastSuccessTimeMillis());
|
||||
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// The moratorium is not reset by success!
|
||||
scheduler.setTriggerTimeMillis(0);
|
||||
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
|
||||
scheduler.setMoratoriumTimeMillis(0);
|
||||
assertEquals(beforeSuccess, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Periodic interval after success
|
||||
options.periodicIntervalMillis = 250000;
|
||||
scheduler.setTriggerTimeMillis(Long.MAX_VALUE);
|
||||
assertEquals(beforeSuccess + 250000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Trigger minimum is also since the last success
|
||||
options.minTriggerMillis = 1000000;
|
||||
assertEquals(beforeSuccess + 1000000, scheduler.getNextTimeMillis(options));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testParseOptions() throws Exception {
|
||||
OperationScheduler.Options options = new OperationScheduler.Options();
|
||||
assertEquals(
|
||||
"OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]",
|
||||
OperationScheduler.parseOptions("3600", options).toString());
|
||||
|
||||
assertEquals(
|
||||
"OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]",
|
||||
OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString());
|
||||
|
||||
assertEquals(
|
||||
"OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
|
||||
OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800",
|
||||
options).toString());
|
||||
|
||||
assertEquals(
|
||||
"OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
|
||||
OperationScheduler.parseOptions("", options).toString());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testMoratoriumWithHttpDate() throws Exception {
|
||||
TimeTravelScheduler scheduler = new TimeTravelScheduler();
|
||||
OperationScheduler.Options options = new OperationScheduler.Options();
|
||||
|
||||
long beforeTrigger = scheduler.timeMillis;
|
||||
scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
|
||||
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
scheduler.setMoratoriumTimeMillis(beforeTrigger + 2000000);
|
||||
assertEquals(beforeTrigger + 2000000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
long beforeMoratorium = scheduler.timeMillis;
|
||||
assertTrue(scheduler.setMoratoriumTimeHttp("3000"));
|
||||
long afterMoratorium = scheduler.timeMillis;
|
||||
assertTrue(beforeMoratorium + 3000000 <= scheduler.getNextTimeMillis(options));
|
||||
assertTrue(afterMoratorium + 3000000 >= scheduler.getNextTimeMillis(options));
|
||||
|
||||
options.maxMoratoriumMillis = Long.MAX_VALUE / 2;
|
||||
assertTrue(scheduler.setMoratoriumTimeHttp("Fri, 31 Dec 2030 23:59:59 GMT"));
|
||||
assertEquals(1924991999000L, scheduler.getNextTimeMillis(options));
|
||||
|
||||
assertFalse(scheduler.setMoratoriumTimeHttp("not actually a date"));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testClockRollbackScenario() throws Exception {
|
||||
TimeTravelScheduler scheduler = new TimeTravelScheduler();
|
||||
OperationScheduler.Options options = new OperationScheduler.Options();
|
||||
options.minTriggerMillis = 2000;
|
||||
|
||||
// First, set up a scheduler with reasons to wait: a transient
|
||||
// error with backoff and a moratorium for a few minutes.
|
||||
|
||||
long beforeTrigger = scheduler.timeMillis;
|
||||
long triggerTime = beforeTrigger - 10000000;
|
||||
scheduler.setTriggerTimeMillis(triggerTime);
|
||||
assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
|
||||
assertEquals(0, scheduler.getLastAttemptTimeMillis());
|
||||
|
||||
long beforeSuccess = (scheduler.timeMillis += 100);
|
||||
scheduler.onSuccess();
|
||||
scheduler.setTriggerTimeMillis(triggerTime);
|
||||
assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
|
||||
assertEquals(beforeSuccess + 2000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
long beforeError = (scheduler.timeMillis += 100);
|
||||
scheduler.onTransientError();
|
||||
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
|
||||
assertEquals(beforeError + 5000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
long beforeMoratorium = (scheduler.timeMillis += 100);
|
||||
scheduler.setMoratoriumTimeMillis(beforeTrigger + 1000000);
|
||||
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// Now set the time back a few seconds.
|
||||
// The moratorium time should still be honored.
|
||||
long beforeRollback = (scheduler.timeMillis = beforeTrigger - 10000);
|
||||
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
|
||||
|
||||
// The rollback also moved the last-attempt clock back to the rollback time.
|
||||
assertEquals(scheduler.timeMillis, scheduler.getLastAttemptTimeMillis());
|
||||
|
||||
// But if we set the time back more than a day, the moratorium
|
||||
// resets to the maximum moratorium (a day, by default), exposing
|
||||
// the original trigger time.
|
||||
beforeRollback = (scheduler.timeMillis = beforeTrigger - 100000000);
|
||||
assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
|
||||
assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
|
||||
|
||||
// If we roll forward until after the re-set moratorium, then it expires.
|
||||
scheduler.timeMillis = triggerTime + 5000000;
|
||||
assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
|
||||
assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
|
||||
assertEquals(beforeRollback, scheduler.getLastSuccessTimeMillis());
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from urllib2 import urlopen
|
||||
|
||||
TLD_PREFIX = r"""
|
||||
/**
|
||||
* Regular expression to match all IANA top-level domains.
|
||||
* List accurate as of 2010/02/05. List taken from:
|
||||
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt
|
||||
* This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
|
||||
*/
|
||||
public static final String TOP_LEVEL_DOMAIN_STR =
|
||||
"""
|
||||
TLD_SUFFIX = '";'
|
||||
|
||||
URL_PREFIX = r"""
|
||||
/**
|
||||
* Regular expression to match all IANA top-level domains for WEB_URL.
|
||||
* List accurate as of 2010/02/05. List taken from:
|
||||
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt
|
||||
* This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
|
||||
*/
|
||||
public static final String TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL =
|
||||
"(?:"
|
||||
"""
|
||||
|
||||
URL_SUFFIX = ';'
|
||||
|
||||
class Bucket:
|
||||
def __init__(self, baseLetter):
|
||||
self.base=baseLetter
|
||||
self.words=[]
|
||||
self.letters=[]
|
||||
|
||||
def dump(self, isWebUrl=False, isFirst=False, isLast=False):
|
||||
if (len(self.words) == 0) and (len(self.letters) == 0):
|
||||
return ''
|
||||
|
||||
self.words.sort()
|
||||
self.letters.sort()
|
||||
|
||||
output = ' ';
|
||||
|
||||
if isFirst:
|
||||
if isWebUrl:
|
||||
output += '+ "'
|
||||
else:
|
||||
output += '"('
|
||||
else:
|
||||
output += '+ "|'
|
||||
|
||||
if len(self.words) != 0:
|
||||
output += '('
|
||||
|
||||
if isWebUrl:
|
||||
output += '?:'
|
||||
|
||||
firstWord = 1
|
||||
for word in self.words:
|
||||
if firstWord == 0:
|
||||
output += '|'
|
||||
firstWord = 0
|
||||
for letter in word:
|
||||
if letter == '-':
|
||||
output += '\\\\' # escape the '-' character.
|
||||
output += letter
|
||||
|
||||
if len(self.words) > 0 and len(self.letters) > 0:
|
||||
output += '|'
|
||||
|
||||
if len(self.letters) == 1:
|
||||
output += '%c%c' % (self.base, self.letters[0])
|
||||
elif len(self.letters) > 0:
|
||||
output += '%c[' % self.base
|
||||
|
||||
for letter in self.letters:
|
||||
output += letter
|
||||
|
||||
output += ']'
|
||||
|
||||
if len(self.words) != 0:
|
||||
output += ')'
|
||||
|
||||
if not isLast:
|
||||
output += '"'
|
||||
output += '\n'
|
||||
|
||||
return output;
|
||||
|
||||
def add(self, line):
|
||||
length = len(line)
|
||||
|
||||
if line.startswith('#') or (length == 0):
|
||||
return;
|
||||
|
||||
if length == 2:
|
||||
self.letters.append(line[1:2])
|
||||
else:
|
||||
self.words.append(line)
|
||||
|
||||
def getBucket(buckets, line):
|
||||
letter = line[0]
|
||||
bucket = buckets.get(letter)
|
||||
|
||||
if bucket is None:
|
||||
bucket = Bucket(letter)
|
||||
buckets[letter] = bucket
|
||||
|
||||
return bucket
|
||||
|
||||
def makePattern(prefix, suffix, buckets, isWebUrl=False):
|
||||
output = prefix
|
||||
|
||||
output += getBucket(buckets, 'a').dump(isFirst=True, isWebUrl=isWebUrl)
|
||||
|
||||
for letter in range(ord('b'), ord('z')):
|
||||
output += getBucket(buckets, chr(letter)).dump(isWebUrl=isWebUrl)
|
||||
|
||||
output += getBucket(buckets, 'z').dump(isLast=True, isWebUrl=isWebUrl)
|
||||
|
||||
if isWebUrl:
|
||||
output += '))"'
|
||||
else:
|
||||
output += ')'
|
||||
|
||||
output += suffix
|
||||
|
||||
print output
|
||||
|
||||
if __name__ == "__main__":
|
||||
f = urlopen('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')
|
||||
domains = f.readlines()
|
||||
f.close()
|
||||
|
||||
buckets = {}
|
||||
|
||||
for domain in domains:
|
||||
domain = domain.lower()
|
||||
|
||||
if len(domain) > 0:
|
||||
getBucket(buckets, domain[0]).add(domain.strip())
|
||||
|
||||
makePattern(TLD_PREFIX, TLD_SUFFIX, buckets, isWebUrl=False)
|
||||
makePattern(URL_PREFIX, URL_SUFFIX, buckets, isWebUrl=True)
|
||||
Reference in New Issue
Block a user