toKeep) {
for (final AlgorithmicContainer c : toKeep) {
MyLog.i(CLS_NAME, "after order: " + c.getGenericMatch() + " ~ " + c.getScore());
}
MyLog.i(CLS_NAME, "would select: " + toKeep.get(0).getGenericMatch());
}
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
@Override
public Object call() throws Exception {
if (UtilsList.notNaked(genericData)) {
if (genericData.get(0) instanceof String) {
return executeGeneric();
} else {
return executeCustomCommand();
}
}
return null;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/api/SaiyDefaults.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.api;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import ai.saiy.android.utils.MyLog;
/**
* Class to map the {@link Defaults} exposed in the library to default values used by Saiy, which
* may differ and offer further functionality.
*
* Created by benrandall76@gmail.com on 12/08/2016.
*/
public class SaiyDefaults {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = SaiyDefaults.class.getSimpleName();
public enum LanguageModel {
LOCAL(Defaults.LanguageModel.LOCAL),
NUANCE(Defaults.LanguageModel.NUANCE),
MICROSOFT(Defaults.LanguageModel.MICROSOFT),
API_AI(Defaults.LanguageModel.API_AI),
WIT(Defaults.LanguageModel.WIT),
IBM(null),
REMOTE(Defaults.LanguageModel.REMOTE);
private final Defaults.LanguageModel languageModel;
LanguageModel(final Defaults.LanguageModel languageModel) {
this.languageModel = languageModel;
}
public Defaults.LanguageModel getRemoteLanguageModel() {
return this.languageModel;
}
/**
* Convert a remote LanguageModel object to the local variant
*
* @param languageModel the remote {@link ai.saiy.android.api.Defaults.LanguageModel}
* @return the local variant of {@link SaiyDefaults.LanguageModel}
*/
public static LanguageModel remoteToLocal(@NonNull final Defaults.LanguageModel languageModel) {
switch (languageModel) {
case LOCAL:
return LanguageModel.LOCAL;
case NUANCE:
return LanguageModel.NUANCE;
case MICROSOFT:
return LanguageModel.MICROSOFT;
case API_AI:
return LanguageModel.API_AI;
case WIT:
return LanguageModel.WIT;
case REMOTE:
return LanguageModel.REMOTE;
default:
return LanguageModel.LOCAL;
}
}
}
public enum TTS {
LOCAL(Defaults.TTS.LOCAL),
NETWORK_NUANCE(Defaults.TTS.NETWORK_NUANCE);
private final Defaults.TTS tts;
TTS(final Defaults.TTS tts) {
this.tts = tts;
}
public Defaults.TTS getRemoteTTS() {
return this.tts;
}
/**
* Convert a remote TTS Default object to the local variant
*
* @param remoteTTS the remote {@link ai.saiy.android.api.Defaults.TTS}
* @return the local variant of {@link SaiyDefaults.TTS}
*/
public static TTS remoteToLocal(@NonNull final Defaults.TTS remoteTTS) {
switch (remoteTTS) {
case LOCAL:
return TTS.LOCAL;
case NETWORK_NUANCE:
return TTS.NETWORK_NUANCE;
default:
return TTS.LOCAL;
}
}
}
public enum VR {
NATIVE(Defaults.VR.NATIVE),
GOOGLE_CLOUD(Defaults.VR.GOOGLE_CLOUD),
GOOGLE_CHROMIUM(Defaults.VR.GOOGLE_CHROMIUM),
NUANCE(Defaults.VR.NUANCE),
MICROSOFT(Defaults.VR.MICROSOFT),
WIT(Defaults.VR.WIT),
IBM(Defaults.VR.IBM),
REMOTE(Defaults.VR.REMOTE),
MIC(null);
private final Defaults.VR vr;
VR(final Defaults.VR vr) {
this.vr = vr;
}
public Defaults.VR getRemoteVR() {
return vr;
}
/**
* Convert a remote VR Default object to the local variant
*
* @param remoteVR the remote {@link ai.saiy.android.api.Defaults.VR}
* @return the local variant of {@link SaiyDefaults.VR}
*/
public static VR remoteToLocal(@NonNull final Defaults.VR remoteVR) {
switch (remoteVR) {
case NATIVE:
return VR.NATIVE;
case GOOGLE_CLOUD:
return VR.GOOGLE_CLOUD;
case GOOGLE_CHROMIUM:
return VR.GOOGLE_CHROMIUM;
case NUANCE:
return VR.NUANCE;
case MICROSOFT:
return VR.MICROSOFT;
case WIT:
return VR.WIT;
case IBM:
return VR.IBM;
case REMOTE:
return VR.REMOTE;
default:
return VR.NATIVE;
}
}
}
public static TTS getProviderTTS(@Nullable final String name) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getProviderTTS: " + name);
}
if (name != null) {
try {
return Enum.valueOf(TTS.class, name.trim());
} catch (final IllegalArgumentException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProviderTTS: IllegalArgumentException");
e.printStackTrace();
}
return TTS.LOCAL;
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProviderTTS: name null");
}
return TTS.LOCAL;
}
}
public static VR getProviderVR(final String name) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getProviderVR: " + name);
}
if (name != null) {
try {
return Enum.valueOf(VR.class, name.trim());
} catch (final IllegalArgumentException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProviderVR: IllegalArgumentException");
e.printStackTrace();
}
return VR.NATIVE;
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProviderVR: name null");
}
return VR.NATIVE;
}
}
public static LanguageModel getLanguageModel(final String name) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getLanguageModel: " + name);
}
if (name != null) {
try {
return Enum.valueOf(LanguageModel.class, name.trim());
} catch (final IllegalArgumentException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getLanguageModel: IllegalArgumentException");
e.printStackTrace();
}
return LanguageModel.LOCAL;
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "getLanguageModel: name null");
}
return LanguageModel.LOCAL;
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/api/helper/BlackList.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.api.helper;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.ListIterator;
import ai.saiy.android.utils.MyLog;
/**
* Helper class to store remote requests that have been denied due to a throttle limit.
*
* Created by benrandall76@gmail.com on 06/03/2016.
*/
public class BlackList {
private transient static final boolean DEBUG = MyLog.DEBUG;
private transient static final String CLS_NAME = BlackList.class.getSimpleName();
private static final int MAX_ACQUIRE_REJECT = 10;
private static final int MAX_PAST_TIME = 30000;
private final String packageName;
private final int callingUid;
private final long requestTime;
/**
* Constructor
*
* Creates a BlackList object to store for future reference
*
* @param packageName of the calling application
* @param callingUid of the calling application
* @param requestTime at which the remote request was made
*/
public BlackList(@NonNull final String packageName, final int callingUid, final long requestTime) {
this.packageName = packageName;
this.callingUid = callingUid;
this.requestTime = requestTime;
}
/**
* Get the Uid of the calling package
*
* @return the calling Uid
*/
private int getCallingUid() {
return callingUid;
}
/**
* Get the package name of the remote caller
*
* @return the remote package name
*/
public String getPackageName() {
return packageName;
}
/**
* Get the time the remote request was made
*
* @return the EPOCH time the request was made
*/
private long getRequestTime() {
return requestTime;
}
/**
* Check if the remote package that is making the denied requests has done this too many times,
* calculated using {@link #MAX_ACQUIRE_REJECT} & {@link #MAX_PAST_TIME}
*
* It would also be possible to extend this method to analyse the Uid, but that is perhaps a
* little too cautious?
*
* @param blackListArray the ArrayList of {@link BlackList}
* @return true if the package should be permanently blacklisted
*/
public static synchronized boolean shouldBlackList(final ArrayList blackListArray) {
if (DEBUG) {
MyLog.i(CLS_NAME, "shouldBlackList: " + blackListArray.size());
}
final int blackListSize = blackListArray.size();
if (blackListSize > 5) {
final BlackList currentBlackList = blackListArray.get(blackListSize - 1);
final String currentPackageName = currentBlackList.getPackageName();
final int currentUid = currentBlackList.getCallingUid();
final long currentRequestTime = currentBlackList.getRequestTime();
if (DEBUG) {
MyLog.v(CLS_NAME, "shouldBlackList: currentPackageName: " + currentPackageName);
MyLog.v(CLS_NAME, "shouldBlackList: currentUid: " + currentUid);
MyLog.v(CLS_NAME, "shouldBlackList: currentRequestTime: " + currentRequestTime);
}
BlackList blackList;
String packageName;
int matches = 0;
for (int i = 0; i < (blackListSize - 1); i++) {
blackList = blackListArray.get(i);
packageName = blackList.getPackageName();
if (packageName.matches(currentPackageName)) {
if (DEBUG) {
MyLog.v(CLS_NAME, "shouldBlackList: difference: "
+ (currentRequestTime - blackList.getRequestTime()));
}
if ((currentRequestTime - blackList.getRequestTime()) < MAX_PAST_TIME) {
matches++;
}
}
}
if (DEBUG) {
MyLog.v(CLS_NAME, "shouldBlackList: matches: " + matches);
}
final ListIterator itr = blackListArray.listIterator();
if (matches > MAX_ACQUIRE_REJECT) {
if (DEBUG) {
MyLog.e(CLS_NAME, "Blacklisted package: " + currentPackageName);
}
while (itr.hasNext()) {
blackList = (BlackList) itr.next();
packageName = blackList.getPackageName();
if (packageName.matches(currentPackageName)) {
itr.remove();
} else if ((currentRequestTime - blackList.getRequestTime()) > MAX_PAST_TIME) {
itr.remove();
}
}
return true;
} else {
while (itr.hasNext()) {
blackList = (BlackList) itr.next();
if ((currentRequestTime - blackList.getRequestTime()) > MAX_PAST_TIME) {
itr.remove();
}
}
}
if (DEBUG) {
MyLog.v(CLS_NAME, "shouldBlackList: blackListArray trimmed size: " + blackListArray.size());
}
}
return false;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/api/helper/BlackListHelper.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.api.helper;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import ai.saiy.android.custom.CustomCommandHelper;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
import ai.saiy.android.utils.UtilsList;
/**
* Created by benrandall76@gmail.com on 14/07/2016.
*/
public class BlackListHelper {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = BlackListHelper.class.getSimpleName();
private final Type type = new TypeToken>() {
}.getType();
/**
* Fetch an ArrayList of currently blacklisted application package names
*
* @param ctx the application context
* @return an ArrayList containing blacklisted application package names or an empty array
*/
public ArrayList fetch(@NonNull final Context ctx) {
final ArrayList blackListArray;
if (haveBlacklist(ctx)) {
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
try {
blackListArray = gson.fromJson(SPH.getBlacklistArray(ctx), type);
if (DEBUG) {
MyLog.i(CLS_NAME, "blackListArray: " + gson.toJson(blackListArray));
}
return blackListArray;
} catch (final JsonSyntaxException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "blackListArray: JsonSyntaxException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "blackListArray: NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "blackListArray: Exception");
e.printStackTrace();
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "blackListArray: empty");
}
}
return new ArrayList<>();
}
/**
* Save the array list of blacklisted application package names into the the user's shared preferences,
* once it has been serialised.
*
* Additionally, remove any current custom commands the application may have registered.
*
* @param ctx the application context
* @param blackListArray the array list of blacklisted application package names
*/
public void save(@NonNull final Context ctx, @Nullable final ArrayList blackListArray) {
if (UtilsList.notNaked(blackListArray)) {
CustomCommandHelper.deleteCommandsForPackage(ctx, blackListArray);
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final String gsonString = gson.toJson(blackListArray, type);
if (DEBUG) {
MyLog.i(CLS_NAME, "save: gsonString: " + gsonString);
}
SPH.setBlacklistArray(ctx, gsonString);
} else {
SPH.setBlacklistArray(ctx, null);
}
}
/**
* Check if the calling package is currently blacklisted
*
* @param ctx the application context
* @param packageName the calling package name
* @return true if the application is blacklisted, false otherwise
*/
public boolean isBlacklisted(@NonNull final Context ctx, @NonNull final String packageName) {
final ArrayList blacklistArray = fetch(ctx);
for (final String name : blacklistArray) {
if (name.matches(packageName)) {
return true;
}
}
return false;
}
/**
* Check if we currently have any applications blacklisted
*
* @param ctx the application context
* @return true if there are blacklisted applications, false otherwise
*/
private boolean haveBlacklist(@NonNull final Context ctx) {
return SPH.getBlacklistArray(ctx) != null;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/api/helper/Callback.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.api.helper;
import android.support.annotation.NonNull;
import ai.saiy.android.api.RequestParcel;
/**
* Helper class to store remote request information.
*
* Created by benrandall76@gmail.com on 06/03/2016.
*/
public class Callback {
private final RequestParcel parcel;
private final String packageName;
private final int callingUid;
private final long requestTime;
/**
* Constructor
*
* @param parcel of request information sent via IPC
* @param packageName of the remote application
* @param callingUid of the remote application
* @param requestTime of the remote request
*/
public Callback(final RequestParcel parcel, @NonNull final String packageName,
final int callingUid, final long requestTime) {
this.parcel = parcel;
this.packageName = packageName;
this.callingUid = callingUid;
this.requestTime = requestTime;
}
/**
* Get the Uid of the remote application
*
* @return the Uid
*/
public int getCallingUid() {
return callingUid;
}
/**
* Get the package name of the remote application
*
* @return the package name
*/
public String getPackageName() {
return packageName;
}
/**
* Get the time of the remote request
*
* @return the EPOCH time
*/
public long getRequestTime() {
return requestTime;
}
/**
* Get the request parcel sent via IPC
*
* @return the {@link RequestParcel}
*/
public RequestParcel getParcel() {
return parcel;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/api/helper/CallbackType.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.api.helper;
import android.content.res.Resources;
/**
* Class to hold the constant values of callback types.
*
* Created by benrandall76@gmail.com on 03/03/2016.
*/
public final class CallbackType {
/**
* Prevent instantiation
*/
public CallbackType() {
throw new IllegalArgumentException(Resources.getSystem().getString(android.R.string.no));
}
public static final int CB_UNKNOWN = 0;
public static final int CB_INTERRUPTED = 1;
public static final int CB_ERROR_NO_MATCH = 2;
public static final int CB_ERROR_NETWORK = 3;
public static final int CB_ERROR_BUSY = 4;
public static final int CB_ERROR_DENIED = 5;
public static final int CB_ERROR_SAIY = 6;
public static final int CB_ERROR_DEVELOPER = 7;
public static final int CB_UTTERANCE_COMPLETED = 99;
public static final int CB_RESULTS_RECOGNITION = 98;
}
================================================
FILE: app/src/main/java/ai/saiy/android/api/helper/Validation.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.api.helper;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.support.annotation.NonNull;
import ai.saiy.android.R;
import ai.saiy.android.api.Defaults;
import ai.saiy.android.api.RequestParcel;
import ai.saiy.android.configuration.NuanceConfiguration;
import ai.saiy.android.service.SelfAware;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsString;
import static ai.saiy.android.api.Defaults.ACTION.SPEAK_LISTEN;
import static ai.saiy.android.api.Defaults.ACTION.SPEAK_ONLY;
import static ai.saiy.android.api.Defaults.LanguageModel.API_AI;
import static ai.saiy.android.api.Defaults.LanguageModel.LOCAL;
import static ai.saiy.android.api.Defaults.VR.GOOGLE_CHROMIUM;
import static ai.saiy.android.api.Defaults.VR.GOOGLE_CLOUD;
import static ai.saiy.android.api.Defaults.VR.IBM;
import static ai.saiy.android.api.Defaults.VR.MICROSOFT;
import static ai.saiy.android.api.Defaults.VR.NATIVE;
import static ai.saiy.android.api.Defaults.VR.NUANCE;
import static ai.saiy.android.api.Defaults.VR.REMOTE;
import static ai.saiy.android.api.Defaults.VR.WIT;
/**
* Helper class to OTT check the remote request parameters and make sure they contain nothing
* erroneous, null or anything in between, despite the library already attempting to do this....
* Once checked, we can allow {@link SelfAware} to not have to apply such
* checks and cause clutter.
*
* Whilst this class can confirm the above, it cannot however be sure that the supplied parameters
* are correctly applied and will not be declined by the API they are to be used for. Such situations
* will be dealt with elsewhere
*
* Created by benrandall76@gmail.com on 25/02/2016.
*/
public final class Validation {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = Validation.class.getSimpleName();
private static final String _YOUR_ = "_your_";
public static final String ID_UNKNOWN = "id_unknown";
/**
* Prevent instantiation
*/
public Validation() {
throw new IllegalArgumentException(Resources.getSystem().getString(android.R.string.no));
}
/**
* Validate the {@link RequestParcel} parameters received by the remote request. At minimum,
* they must contain a valid speech string and a request id.
*
* The request id may have been auto-generated by the library, rather than the caller applying it.
*
* If the caller intended no speech to be uttered, they needed to
* set {@link ai.saiy.android.api.request.SaiyRequestParams#SILENCE}
*
* If tests fail, there will be output in the log, that a developer can spot and we decline
* the request silently to the user.
*
* @param parcel the {@link RequestParcel} received from the remote request
* @return true if the values are configured correctly enough to make an API call with them.
*/
public static boolean validateParams(@NonNull final Context ctx, @NonNull final RequestParcel parcel) {
final String words = parcel.getUtterance();
final String utteranceId = parcel.getRequestId();
if (UtilsString.notNaked(words)) {
if (DEBUG) {
MyLog.d(CLS_NAME, "validateParams: words: " + words);
}
if (UtilsString.notNaked(utteranceId)) {
if (DEBUG) {
MyLog.d(CLS_NAME, "validateParams: utteranceId: " + utteranceId);
}
return true;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(ai.saiy.android.R.string.error_requestid_invalid));
}
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_utterance_invalid));
}
return false;
}
/**
* Validate the {@link RequestParcel} received by the remote request.
*
* If tests fail, there will be output in the log, that a developer can spot and we decline
* the request silently to the user.
*
* @param parcel the {@link RequestParcel} received from the remote request
* @return true if the values are configured correctly enough to make an API call with them.
*/
public static boolean validateParcel(@NonNull final Context ctx, final RequestParcel parcel) {
return parcel != null && checkAction(ctx, parcel);
}
/**
* Check the {@link Defaults.ACTION} is correctly defined.
*
* @param ctx the application context
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkAction(@NonNull final Context ctx, final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAction");
}
final Defaults.ACTION action = parcel.getAction();
switch (action) {
case SPEAK_ONLY:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAction: " + SPEAK_ONLY.name());
}
break;
case SPEAK_LISTEN:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAction: " + SPEAK_LISTEN.name());
}
break;
default:
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(ai.saiy.android.R.string.error_action_invalid));
return false;
}
return checkProviderTTS(ctx, parcel);
}
/**
* Check the {@link Defaults.TTS} are correctly defined.
*
* @param ctx the application context
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkProviderTTS(@NonNull final Context ctx, final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkProviderTTS");
}
final Defaults.TTS providerTTS = parcel.getProviderTTS();
switch (providerTTS) {
case LOCAL:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkProviderTTS: Defaults.TTS.LOCAL");
}
break;
case NETWORK_NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkProviderTTS: Defaults.TTS.NETWORK_NUANCE");
}
break;
default:
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(ai.saiy.android.R.string.error_tts_invalid));
return false;
}
return checkVRProvider(ctx, parcel);
}
/**
* Check the {@link Defaults.VR} are correctly defined.
*
* @param ctx the application context
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkVRProvider(@NonNull final Context ctx, final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider");
}
final Defaults.VR providerVR = parcel.getProviderVR();
switch (providerVR) {
case NATIVE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + NATIVE.name());
}
break;
case GOOGLE_CLOUD:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + GOOGLE_CLOUD.name());
}
break;
case GOOGLE_CHROMIUM:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + GOOGLE_CHROMIUM.name());
}
break;
case NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + NUANCE.name());
}
break;
case MICROSOFT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + MICROSOFT.name());
}
break;
case WIT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + WIT.name());
}
break;
case IBM:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + IBM.name());
}
break;
case REMOTE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + REMOTE.name());
}
break;
default:
switch (parcel.getLanguageModel()) {
case LOCAL:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + LOCAL.name());
}
break;
case NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + NUANCE.name());
}
break;
case MICROSOFT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + MICROSOFT.name());
}
break;
case API_AI:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + API_AI.name());
}
break;
case REMOTE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkVRProvider: " + REMOTE.name());
}
break;
default:
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_vr_invalid));
return false;
}
}
return checkAPIKey(ctx, parcel);
}
/**
* Check the API keys that will need to be used are correctly set.
*
* @param ctx the application context
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkAPIKey(@NonNull final Context ctx, final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey");
}
final Defaults.TTS providerTTS = parcel.getProviderTTS();
switch (providerTTS) {
case LOCAL:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: LOCAL: skipping");
}
break;
case NETWORK_NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: Defaults.TTS.NETWORK_NUANCE: skipping");
}
if (checkNuanceConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: Defaults.TTS.NETWORK_NUANCE: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_nuance_config));
return false;
}
}
final Defaults.VR providerVR = parcel.getProviderVR();
switch (providerVR) {
case NATIVE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + NATIVE.name());
}
break;
case GOOGLE_CLOUD:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + GOOGLE_CLOUD.name());
}
if (checkGoogleCloudConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_api_google_cloud));
return false;
}
case GOOGLE_CHROMIUM:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + GOOGLE_CHROMIUM.name());
}
if (checkGoogleChromiumConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_api_google_chromium));
return false;
}
case NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + NUANCE.name());
}
if (checkNuanceConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_nuance_config));
return false;
}
case MICROSOFT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + MICROSOFT.name());
}
if (checkMicrosoftConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_microsoft_config));
return false;
}
case WIT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + WIT.name());
}
if (checkWitConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_api_wit));
return false;
}
case IBM:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + IBM.name());
}
if (checkIBMConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_api_ibm));
return false;
}
case REMOTE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + REMOTE.name());
}
if (checkRemoteConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_remote_config));
return false;
}
default:
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_vr_invalid));
return false;
}
switch (parcel.getLanguageModel()) {
case LOCAL:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + LOCAL.name());
}
break;
case NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + NUANCE.name());
}
if (checkNuanceConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present " + NUANCE.name());
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(ai.saiy.android.R.string.error_nuance_config));
return false;
}
case MICROSOFT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + MICROSOFT.name());
}
if (checkMicrosoftConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present " + MICROSOFT.name());
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(ai.saiy.android.R.string.error_microsoft_config));
return false;
}
case API_AI:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + API_AI.name());
}
if (checkAPIAIConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present " + API_AI.name());
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(ai.saiy.android.R.string.error_api_ai_config));
return false;
}
case WIT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + Defaults.LanguageModel.WIT.name());
}
if (checkWitConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present");
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_api_wit));
return false;
}
case REMOTE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: " + REMOTE.name());
}
if (checkRemoteConfig(parcel)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIKey: API key present " + REMOTE.name());
}
break;
} else {
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(ai.saiy.android.R.string.error_remote_config));
return false;
}
default:
MyLog.e("Remote Saiy Request", ctx.getApplicationContext().getString(R.string.error_vr_invalid));
return false;
}
return true;
}
/**
* Check the remote credentials are correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkRemoteConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkRemoteConfig");
}
final String REMOTE_ACCESS_TOKEN = parcel.getREMOTE_ACCESS_TOKEN();
final Uri REMOTE_SERVER_URI = parcel.getREMOTE_SERVER_URI();
return UtilsString.notNaked(REMOTE_ACCESS_TOKEN)
&& !REMOTE_ACCESS_TOKEN.startsWith(_YOUR_)
&& REMOTE_SERVER_URI != null
&& UtilsString.notNaked(REMOTE_SERVER_URI.toString())
&& !REMOTE_SERVER_URI.toString().startsWith(_YOUR_);
}
/**
* Check the API AI credentials are correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkAPIAIConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkAPIAIConfig");
}
final String API_AI_CLIENT_ACCESS_TOKEN = parcel.getAPI_AI_CLIENT_ACCESS_TOKEN();
return UtilsString.notNaked(API_AI_CLIENT_ACCESS_TOKEN)
&& !API_AI_CLIENT_ACCESS_TOKEN.startsWith(_YOUR_);
}
/**
* Check the IBM credentials are correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkIBMConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkIBMConfig");
}
final String IBM_SERVICE_USER_NAME = parcel.getIBM_SERVICE_USER_NAME();
final String IBM_SERVICE_PASSWORD = parcel.getIBM_SERVICE_PASSWORD();
return UtilsString.notNaked(IBM_SERVICE_USER_NAME)
&& !IBM_SERVICE_USER_NAME.startsWith(_YOUR_)
&& UtilsString.notNaked(IBM_SERVICE_PASSWORD)
&& !IBM_SERVICE_PASSWORD.startsWith(_YOUR_);
}
/**
* Check the Wit credentials are correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkWitConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkWitConfig");
}
final String WIT_SERVER_ACCESS_TOKEN = parcel.getWIT_SERVER_ACCESS_TOKEN();
return UtilsString.notNaked(WIT_SERVER_ACCESS_TOKEN)
&& !WIT_SERVER_ACCESS_TOKEN.startsWith(_YOUR_);
}
/**
* Check the Microsoft credentials are correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkMicrosoftConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkMicrosoftConfig");
}
final String OXFORD_KEY_1 = parcel.getOXFORD_KEY_1();
final String OXFORD_KEY_2 = parcel.getOXFORD_KEY_2();
if (UtilsString.notNaked(OXFORD_KEY_1) && !OXFORD_KEY_1.startsWith(_YOUR_)
&& UtilsString.notNaked(OXFORD_KEY_2) && !OXFORD_KEY_2.startsWith(_YOUR_)) {
switch (parcel.getLanguageModel()) {
case MICROSOFT:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkMicrosoftConfig");
}
final String LUIS_APP_ID = parcel.getLUIS_APP_ID();
final String LUIS_SUBSCRIPTION_ID = parcel.getLUIS_SUBSCRIPTION_ID();
if (UtilsString.notNaked(LUIS_APP_ID) && !LUIS_APP_ID.startsWith(_YOUR_)
&& UtilsString.notNaked(LUIS_SUBSCRIPTION_ID) && !LUIS_SUBSCRIPTION_ID.startsWith(_YOUR_)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkMicrosoftConfig: Credentials present");
}
return true;
} else {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkMicrosoftConfig: NLU parameters missing");
}
return false;
}
default:
return true;
}
} else {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkMicrosoftConfig: API keys missing");
}
return false;
}
}
/**
* Check the Google API key is correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkGoogleCloudConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkGoogleCloudConfig");
}
final String GOOGLE_CLOUD_ACCESS_TOKEN = parcel.getGOOGLE_CLOUD_ACCESS_TOKEN();
return UtilsString.notNaked(GOOGLE_CLOUD_ACCESS_TOKEN) && !GOOGLE_CLOUD_ACCESS_TOKEN.startsWith(_YOUR_)
&& parcel.getGOOGLE_CLOUD_ACCESS_EXPIRY() > 0;
}
/**
* Check the Google API key is correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkGoogleChromiumConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkGoogleChromiumConfig");
}
final String GOOGLE_CHROMIUM_API_KEY = parcel.getGOOGLE_CHROMIUM_API_KEY();
return UtilsString.notNaked(GOOGLE_CHROMIUM_API_KEY) && !GOOGLE_CHROMIUM_API_KEY.startsWith(_YOUR_);
}
/**
* Check the required Nuance parameters are correctly defined
*
* @param parcel the {@link RequestParcel}
* @return true if the parameters are configured correctly.
*/
private static boolean checkNuanceConfig(@NonNull final RequestParcel parcel) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkNuanceConfig");
}
switch (parcel.getProviderTTS()) {
case NETWORK_NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkNuanceConfig");
}
final String NUANCE_APP_KEY = parcel.getNUANCE_APP_KEY();
final Uri NUANCE_SERVER_URI = parcel.getNUANCE_SERVER_URI();
if (!UtilsString.notNaked(NUANCE_APP_KEY) || NUANCE_APP_KEY.startsWith(_YOUR_)) {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkNuanceConfig: API key missing");
}
return false;
} else if (!UtilsString.notNaked(NUANCE_SERVER_URI.toString())
|| NUANCE_SERVER_URI.toString().startsWith(_YOUR_)) {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkNuanceConfig: URI missing");
}
return false;
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkNuanceConfig: Credentials present");
}
break;
}
}
switch (parcel.getLanguageModel()) {
case NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkNuanceConfig");
}
final String NUANCE_CONTEXT_TAG = parcel.getNUANCE_CONTEXT_TAG();
final Uri NUANCE_SERVER_URI_NLU = parcel.getNUANCE_SERVER_URI_NLU();
if (!UtilsString.notNaked(NUANCE_CONTEXT_TAG) || NUANCE_CONTEXT_TAG.startsWith(_YOUR_)) {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkNuanceConfig: CONTEXT TAG missing");
}
return false;
} else if (!UtilsString.notNaked(NUANCE_SERVER_URI_NLU.toString())
|| NUANCE_SERVER_URI_NLU.toString().startsWith(_YOUR_)) {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkNuanceConfig: URI missing");
}
return false;
} else if (!NUANCE_SERVER_URI_NLU.toString().contains(NuanceConfiguration.SERVER_HOST_NLU)) {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkNuanceConfig: HOST requires "
+ NuanceConfiguration.SERVER_HOST_NLU);
}
return false;
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkNuanceConfig: Credentials present");
}
break;
}
default:
switch (parcel.getProviderVR()) {
case NUANCE:
if (DEBUG) {
MyLog.i(CLS_NAME, "checkNuanceConfig");
}
final String NUANCE_APP_KEY = parcel.getNUANCE_APP_KEY();
final Uri NUANCE_SERVER_URI = parcel.getNUANCE_SERVER_URI();
if (!UtilsString.notNaked(NUANCE_APP_KEY) || NUANCE_APP_KEY.startsWith(_YOUR_)) {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkNuanceConfig: API key missing");
}
return false;
} else if (!UtilsString.notNaked(NUANCE_SERVER_URI.toString())
|| NUANCE_SERVER_URI.toString().startsWith(_YOUR_)) {
if (DEBUG) {
MyLog.e(CLS_NAME, "checkNuanceConfig: URI missing");
}
return false;
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkNuanceConfig: Credentials present");
}
break;
}
}
}
return true;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/applications/Install.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.applications;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import java.io.IOException;
import ai.saiy.android.utils.Global;
import ai.saiy.android.utils.MyLog;
/**
* Created by benrandall76@gmail.com on 11/08/2016.
*/
public class Install {
public enum Location {
PLAYSTORE, AMAZON
}
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = Install.class.getSimpleName();
/**
* Depending on the version of Saiy, the install link needs to vary. This class handles all
* eventualities, so no changes will be needed elsewhere in the code.
*
* @param ctx the application context
* @param packageName the package name to open
* @return true if the intent was successful. False otherwise.
*/
public static boolean showInstallLink(@NonNull final Context ctx, @NonNull final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "showInstall");
}
switch (Global.installLocation) {
case PLAYSTORE:
if (DEBUG) {
MyLog.i(CLS_NAME, "showInstall PLAYSTORE");
}
return InstallPlayStore.showInstall(ctx, packageName);
case AMAZON:
if (DEBUG) {
MyLog.i(CLS_NAME, "showInstall AMAZON");
}
return InstallAmazon.showInstall(ctx, packageName);
default:
return false;
}
}
/**
* Get the account type associated with the install location of the app
*
* @return the account type
*/
public static String getAccountType() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getAccountType");
}
switch (Global.installLocation) {
case PLAYSTORE:
if (DEBUG) {
MyLog.i(CLS_NAME, "getAccountType PLAYSTORE");
}
return GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE;
case AMAZON:
if (DEBUG) {
MyLog.i(CLS_NAME, "getAccountType AMAZON");
}
return "";
default:
return "";
}
}
/**
* Asynchronously get the account id associated with the email address
*
* @param ctx the application context
* @param accountName the email address
* @return the account id or null if the operation failed
*/
@WorkerThread
public static String getAccountId(@NonNull final Context ctx, @NonNull final String accountName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getAccountId");
}
try {
switch (Global.installLocation) {
case PLAYSTORE:
if (DEBUG) {
MyLog.i(CLS_NAME, "getAccountId PLAYSTORE");
}
final String accountId = GoogleAuthUtil.getAccountId(ctx, accountName);
if (DEBUG) {
MyLog.i(CLS_NAME, "getAccountId PLAYSTORE: " + accountId);
}
return accountId;
case AMAZON:
if (DEBUG) {
MyLog.i(CLS_NAME, "getAccountId AMAZON");
}
default:
break;
}
} catch (final GoogleAuthException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getAccountId GoogleAuthException");
e.printStackTrace();
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getAccountId IOException");
e.printStackTrace();
}
}
return null;
}
/**
* Get the install link for Saiy
*
* @return the String install link
*/
public static String getSaiyInstallLink(@NonNull final Context ctx) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getSaiyInstallLink");
}
switch (Global.installLocation) {
case PLAYSTORE:
if (DEBUG) {
MyLog.i(CLS_NAME, "getSaiyInstallLink PLAYSTORE");
}
return InstallPlayStore.getSaiyInstallLink(ctx);
case AMAZON:
if (DEBUG) {
MyLog.i(CLS_NAME, "getSaiyInstallLink AMAZON");
}
return InstallAmazon.getSaiyInstallLink(ctx);
default:
return "";
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/applications/InstallAmazon.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.applications;
import android.content.Context;
import android.support.annotation.NonNull;
import ai.saiy.android.intent.IntentConstants;
import ai.saiy.android.utils.MyLog;
/**
* Created by benrandall76@gmail.com on 11/08/2016.
*/
public class InstallAmazon {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = InstallAmazon.class.getSimpleName();
/**
* Open the Amazon App Store using the package name of the desired application. If the user has the
* Amazon application installed and defaulted, this URL will open directly in the application.
* Otherwise, it will default to a browser search.
*
* @param ctx the application context
* @param packageName the package name to open
* @return true if an Activity was available to handle the intent. False otherwise.
*/
public static boolean showInstall(@NonNull final Context ctx, @NonNull final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "showInstall");
}
return false;
}
/**
* Get the install link for Saiy
*
* @param ctx the application context
* @return the String install link
*/
public static String getSaiyInstallLink(@NonNull final Context ctx) {
return IntentConstants.AMAZON_PACKAGE_URL + ctx.getPackageName();
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/applications/InstallPlayStore.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.applications;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import ai.saiy.android.intent.IntentConstants;
import ai.saiy.android.utils.MyLog;
/**
* Created by benrandall76@gmail.com on 11/08/2016.
*/
public class InstallPlayStore {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = InstallPlayStore.class.getSimpleName();
/**
* Open the Play Store using the package name of the desired application. If the user has the
* Play Store application installed and defaulted, this URL will open directly in the application.
* Otherwise, it will default to a browser search.
*
* @param ctx the application context
* @param packageName the package name to open
* @return true if an Activity was available to handle the intent. False otherwise.
*/
public static boolean showInstall(@NonNull final Context ctx, @NonNull final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "showInstall");
}
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(IntentConstants.PLAY_STORE_PACKAGE_URL +
packageName));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
ctx.startActivity(intent);
return true;
} catch (final ActivityNotFoundException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "showInstall: ActivityNotFoundException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "showInstall: Exception");
e.printStackTrace();
}
}
return false;
}
/**
* Get the install link for Saiy
*
* @param ctx the application context
* @return the String install link
*/
public static String getSaiyInstallLink(@NonNull final Context ctx) {
return IntentConstants.PLAY_STORE_PACKAGE_URL + ctx.getPackageName();
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/applications/Installed.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.applications;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import ai.saiy.android.BuildConfig;
import ai.saiy.android.Manifest;
import ai.saiy.android.defaults.songrecognition.SongRecognitionProvider;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 20/04/2016.
*/
public class Installed {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = Installed.class.getSimpleName();
public static final String PACKAGE_FACEBOOK = "com.facebook.katana";
public static final String PACKAGE_TWITTER = "com.twitter.android";
public static final String PACKAGE_SNAPCHAT = "com.snapchat.android";
public static final String PACKAGE_WHATSAPP = "com.whatsapp";
public static final String PACKAGE_TINDER = "com.tinder";
public static final String PACKAGE_SHAZAM = "com.shazam.android";
public static final String PACKAGE_SHAZAM_ENCORE = "com.shazam.encore.android";
public static final String PACKAGE_SOUND_HOUND = "com.melodis.midomiMusicIdentifier.freemium";
public static final String PACKAGE_SOUND_HOUND_PREMIUM = "com.melodis.midomiMusicIdentifier";
public static final String PACKAGE_TRACK_ID = "com.sonyericsson.trackid";
public static final String PACKAGE_GOOGLE_SOUND_SEARCH = "com.google.android.ears";
public static final String PACKAGE_NAME_GOOGLE = "com.google.android";
public static final String PACKAGE_NAME_GOOGLE_NOW = "com.google.android.googlequicksearchbox";
public static final String PACKAGE_NAME_GOOGLE_NOW_LAUNCHER = "com.google.android.googlequicksearchbox";
public static final String PACKAGE_TASKER_DIRECT = "net.dinglisch.android.tasker";
public static final String PACKAGE_TASKER_MARKET = PACKAGE_TASKER_DIRECT + "m";
public static final String PACKAGE_WOLFRAM_ALPHA = "com.wolfram.android.alpha";
private static final Pattern pCONTROL_SAIY = Pattern.compile(Manifest.permission.CONTROL_SAIY);
private static final Pattern pSAIY_PACKAGE = Pattern.compile(BuildConfig.APPLICATION_ID);
/**
* Get a list of all of the installed applications that hold the {@link Manifest.permission#CONTROL_SAIY},
* regardless of whether or not the permission has been explicitly granted. The results exclude our own
* package.
*
* This method is slower than {@link #declaresSaiyPermissionLegacy(Context)} but avoids the 'transaction
* too large' errors.
*
* @param ctx the application context
* @return an ArrayList containing a {@link Pair} with the first parameter containing the application name
* and the second the package name.
*/
public static ArrayList> declaresSaiyPermission(@NonNull final Context ctx) {
final long then = System.nanoTime();
final ArrayList> holdsPermission = new ArrayList<>();
final PackageManager pm = ctx.getPackageManager();
final List apps = pm.getInstalledApplications(0);
CharSequence appNameChar;
String appName;
String[] permissions;
PackageInfo packageInfo;
for (final ApplicationInfo applicationInfo : apps) {
try {
packageInfo = pm.getPackageInfo(applicationInfo.packageName, PackageManager.GET_PERMISSIONS);
permissions = packageInfo.requestedPermissions;
if (permissions != null) {
for (final String permission : permissions) {
if (pCONTROL_SAIY.matcher(permission).matches()) {
appNameChar = packageInfo.applicationInfo.loadLabel(pm);
if (appNameChar != null) {
appName = packageInfo.applicationInfo.loadLabel(pm).toString();
if (UtilsString.notNaked(appName)) {
if (!pSAIY_PACKAGE.matcher(packageInfo.packageName).matches()) {
holdsPermission.add(new Pair<>(appName, packageInfo.packageName));
}
break;
}
}
holdsPermission.add(new Pair<>(packageInfo.packageName, packageInfo.packageName));
}
}
}
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isPackageInstalled: NameNotFoundException");
}
}
}
if (DEBUG) {
MyLog.getElapsed(Installed.class.getSimpleName(), then);
}
return holdsPermission;
}
/**
* Get a list of all of the installed applications that hold the {@link Manifest.permission#CONTROL_SAIY},
* regardless of whether or not the permission has been explicitly granted. The results exclude our own
* package.
*
* @param ctx the application context
* @return an ArrayList containing a {@link Pair} with the first parameter containing the application name
* and the second the package name.
*/
public static ArrayList> declaresSaiyPermissionLegacy(@NonNull final Context ctx) {
final ArrayList> holdsPermission = new ArrayList<>();
final PackageManager pm = ctx.getPackageManager();
final List apps = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
CharSequence appNameChar;
String appName;
String[] permissions;
for (final PackageInfo packageInfo : apps) {
permissions = packageInfo.requestedPermissions;
if (permissions != null) {
for (final String permission : permissions) {
if (pCONTROL_SAIY.matcher(permission).matches()) {
appNameChar = packageInfo.applicationInfo.loadLabel(pm);
if (appNameChar != null) {
appName = packageInfo.applicationInfo.loadLabel(pm).toString();
if (UtilsString.notNaked(appName)) {
if (!pSAIY_PACKAGE.matcher(packageInfo.packageName).matches()) {
holdsPermission.add(new Pair<>(appName, packageInfo.packageName));
}
break;
}
}
holdsPermission.add(new Pair<>(packageInfo.packageName, packageInfo.packageName));
}
}
}
}
return holdsPermission;
}
/**
* Check if the user has a package installed
*
* @param ctx the application context
* @param packageName the application package name
* @return true if the package is installed
*/
public static boolean isPackageInstalled(@NonNull final Context ctx, @NonNull final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "isPackageInstalled");
}
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(packageName, 0);
return true;
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isPackageInstalled: NameNotFoundException");
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isPackageInstalled: NullPointerException");
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isPackageInstalled: Exception");
}
}
return false;
}
/**
* Check if the user has either of the Shazam applications installed
*
* @param ctx the application context
* @return true if the package is installed
*/
public static boolean shazamInstalled(@NonNull final Context ctx) {
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SHAZAM, 0);
return true;
} catch (final PackageManager.NameNotFoundException e) {
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SHAZAM_ENCORE, 0);
return true;
} catch (final PackageManager.NameNotFoundException ee) {
return false;
}
}
}
/**
* Check if the user has either of the Sound Hound applications installed
*
* @param ctx the application context
* @return true if the package is installed
*/
public static boolean soundHoundInstalled(@NonNull final Context ctx) {
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SOUND_HOUND, 0);
return true;
} catch (final PackageManager.NameNotFoundException e) {
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SOUND_HOUND_PREMIUM, 0);
return true;
} catch (final PackageManager.NameNotFoundException ee) {
return false;
}
}
}
/**
* Check to see which supported applications are installed that can recognise music tracks.
*
* We don't add both the freemium and premium packages, as they both respond to the same
* application specific intent.
*
* In the future, only applications that respond to saiy.intent.action.MEDIA_RECOGNIZE will
* be supported.
*
* @param ctx the application context
* @return an {@link ArrayList} containing {@link SongRecognitionProvider} providers
*/
public static ArrayList getSongRecognitionProviders(@NonNull final Context ctx) {
final ArrayList providers = new ArrayList<>();
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SHAZAM, 0);
providers.add(SongRecognitionProvider.SHAZAM);
if (DEBUG) {
MyLog.d(CLS_NAME, "Shazam installed");
}
} catch (final PackageManager.NameNotFoundException e) {
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SHAZAM_ENCORE, 0);
providers.add(SongRecognitionProvider.SHAZAM);
if (DEBUG) {
MyLog.d(CLS_NAME, "Shazam Encore installed");
}
} catch (final PackageManager.NameNotFoundException ee) {
if (DEBUG) {
MyLog.d(CLS_NAME, "Shazam variant not installed");
}
}
}
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SOUND_HOUND, 0);
providers.add(SongRecognitionProvider.SOUND_HOUND);
if (DEBUG) {
MyLog.d(CLS_NAME, "Sound Hound installed");
}
} catch (final PackageManager.NameNotFoundException e) {
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_SOUND_HOUND_PREMIUM, 0);
providers.add(SongRecognitionProvider.SOUND_HOUND);
if (DEBUG) {
MyLog.d(CLS_NAME, "Sound Hound Premium installed");
}
} catch (final PackageManager.NameNotFoundException ee) {
if (DEBUG) {
MyLog.d(CLS_NAME, "Sound Hound variant not installed");
}
}
}
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_GOOGLE_SOUND_SEARCH, 0);
providers.add(SongRecognitionProvider.GOOGLE);
if (DEBUG) {
MyLog.d(CLS_NAME, "Google Sound Search installed");
}
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.d(CLS_NAME, "Google Sound Search not installed");
}
}
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(PACKAGE_TRACK_ID, 0);
providers.add(SongRecognitionProvider.TRACK_ID);
if (DEBUG) {
MyLog.d(CLS_NAME, "TrackID installed");
}
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.d(CLS_NAME, "TrackID not installed");
}
}
return providers;
}
/**
* Get the package name of the user's default launcher
*
* @param ctx the application context
* @return the package name of the default launcher
*/
public static String getDefaultLauncherPackage(@NonNull final Context ctx) {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
final ResolveInfo resolveInfo = ctx.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfo != null) {
return resolveInfo.activityInfo.packageName;
} else {
return null;
}
}
/**
* Check if the user's default launcher is Google Now Launcher
*
* @param ctx the application context
* @return the package name of the default launcher
*/
public static boolean isGoogleNowLauncherDefault(@NonNull final Context ctx) {
final String defaultLauncherPackage = getDefaultLauncherPackage(ctx);
if (DEBUG) {
MyLog.d(CLS_NAME, "isGoogleNowLauncherDefault: " + defaultLauncherPackage);
}
return UtilsString.notNaked(defaultLauncherPackage)
&& defaultLauncherPackage.matches(PACKAGE_NAME_GOOGLE_NOW_LAUNCHER);
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/applications/UtilsApplication.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.applications;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.util.Pair;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsList;
/**
* Utility class for application related methods.
*
* Created by benrandall76@gmail.com on 26/04/2016.
*/
public class UtilsApplication {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = UtilsApplication.class.getSimpleName();
public static final String USAGE_STATS_SERVICE = "usagestats";
/**
* Helper method to get an application name from a package name
*
* @param ctx the application context
* @param packageName the application package name
* @return a {@link Pair} of which the first parameter denotes success and the second the application
* name or null if the process failed.
*/
public static Pair getAppNameFromPackage(@NonNull final Context ctx, @NonNull final String packageName) {
final PackageManager packageManager = ctx.getPackageManager();
final ApplicationInfo applicationInfo;
try {
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
return new Pair<>(true, packageManager.getApplicationLabel(applicationInfo).toString());
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getAppNameFromPackage: NameNotFoundException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getAppNameFromPackage: NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getAppNameFromPackage: Exception");
e.printStackTrace();
}
}
return new Pair<>(false, null);
}
/**
* Kill a specific application
*
* @param ctx the application context
* @param packageName of the application to kill
* @return true if the process was killed successfully, false otherwise
*/
public static boolean killPackage(@NonNull final Context ctx, @NonNull final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "killPackage");
}
try {
((ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE))
.killBackgroundProcesses(packageName);
return true;
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "killPackage: Exception");
e.printStackTrace();
}
}
return false;
}
/**
* Check if a specific application package is installed
*
* @param ctx the application context
* @param packageName of the application
* @return true if the application is installed, false otherwise
*/
public static boolean isAppInstalled(@NonNull final Context ctx, @NonNull final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "isAppInstalled");
}
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(packageName, 0);
return true;
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isAppInstalled: NameNotFoundException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isAppInstalled: NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isAppInstalled: Exception");
e.printStackTrace();
}
}
return false;
}
/**
* Launch an application from its package name
*
* @param ctx the application context
* @param packageName of the application
* @return true if the application was successfully launched, false otherwise
*/
public static boolean launchAppFromPackageName(final Context ctx, final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "launchAppFromPackageName");
}
final Intent intent = ctx.getPackageManager().getLaunchIntentForPackage(packageName);
if (intent != null) {
try {
ctx.startActivity(intent);
return true;
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "launchAppFromPackageName: Exception");
e.printStackTrace();
}
}
}
return false;
}
/**
* Get the package name of the current foreground application
*
* @param ctx the application context
* @return the package name or null
*/
@SuppressLint("InlinedApi")
public static String getForegroundPackage(@NonNull final Context ctx, final long history) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getForegroundPackage");
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//noinspection NewApi
return getForegroundPackage21(ctx, history);
} else {
return getForegroundPackageDeprecated(ctx);
}
}
/**
* Get the package name of the current foreground application
*
* @param ctx the application context
* @return the package name or null
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static String getForegroundPackage21(@NonNull final Context ctx, final long history) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getForegroundPackage21");
}
try {
@SuppressWarnings("WrongConstant")
final UsageStatsManager mUsageStatsManager = (UsageStatsManager)
ctx.getSystemService(USAGE_STATS_SERVICE);
final long currentTime = System.currentTimeMillis();
final List statsList = mUsageStatsManager.queryUsageStats(
UsageStatsManager.INTERVAL_DAILY, currentTime - history, currentTime);
if (UtilsList.notNaked(statsList)) {
final SortedMap sortedMap = new TreeMap<>();
for (final UsageStats usageStats : statsList) {
sortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (!sortedMap.isEmpty()) {
final String packageName = sortedMap.get(sortedMap.lastKey()).getPackageName();
if (DEBUG) {
MyLog.i(CLS_NAME, "getForegroundPackage21: packageName: " + packageName);
}
return packageName;
}
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackage21 NullPointerException");
e.printStackTrace();
}
} catch (final IndexOutOfBoundsException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackage21 IndexOutOfBoundsException");
e.printStackTrace();
}
} catch (final SecurityException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackage21 SecurityException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackage21 Exception");
e.printStackTrace();
}
}
return null;
}
/**
* Get the package name of the current foreground application
*
* @param ctx the application context
* @return the package name or null
*/
@SuppressWarnings("deprecation")
private static String getForegroundPackageDeprecated(@NonNull final Context ctx) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getForegroundPackageDeprecated");
}
try {
final ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
final List taskInfo = am.getRunningTasks(1);
if (taskInfo != null && taskInfo.size() > 0) {
final ActivityManager.RunningTaskInfo ti = taskInfo.get(0);
if (ti != null) {
final PackageManager pm = ctx.getPackageManager();
final ComponentName cn = ti.topActivity;
if (cn != null) {
final PackageInfo foregroundAppPackageInfo = pm.getPackageInfo(cn.getPackageName(), 0);
if (foregroundAppPackageInfo != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getForegroundPackageDeprecated: packageName: "
+ foregroundAppPackageInfo.packageName);
}
return foregroundAppPackageInfo.packageName;
}
}
}
}
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackageDeprecated NameNotFoundException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackageDeprecated NullPointerException");
e.printStackTrace();
}
} catch (final IndexOutOfBoundsException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackageDeprecated IndexOutOfBoundsException");
e.printStackTrace();
}
} catch (final SecurityException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackageDeprecated SecurityException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getForegroundPackageDeprecated Exception");
e.printStackTrace();
}
}
return null;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/AudioCompression.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Process;
import android.support.annotation.NonNull;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import ai.saiy.android.cache.speech.IAudioCompression;
import ai.saiy.android.database.DBSpeech;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsFile;
/**
* Class to handle compression of audio bytes.
*
* Created by benrandall76@gmail.com on 27/04/2016.
*/
public class AudioCompression {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = AudioCompression.class.getSimpleName();
/**
* Compress the audio bytes using GZIP
*
* @param listener the {@link IAudioCompression}
* @param bytes the byte array to compress
*/
public static void compressBytes(@NonNull final IAudioCompression listener, @NonNull final byte[] bytes) {
if (DEBUG) {
MyLog.d(CLS_NAME, "compressBytes: bytes size: " + bytes.length);
}
final long then = System.nanoTime();
ByteArrayOutputStream byteArrayOutputStream = null;
GZIPOutputStream gzipOutputStream = null;
byte[] returnBytes = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream(bytes.length);
gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream) {
{
def.setLevel(Deflater.BEST_COMPRESSION);
}
};
gzipOutputStream.write(bytes);
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "compressBytes IOException1");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "compressBytes NullPointerException1");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "compressBytes Exception1");
e.printStackTrace();
}
} finally {
try {
if (gzipOutputStream != null) {
gzipOutputStream.close();
}
if (byteArrayOutputStream != null) {
byteArrayOutputStream.close();
returnBytes = byteArrayOutputStream.toByteArray();
if (DEBUG) {
MyLog.d(CLS_NAME, "compressBytes returnBytes size: " + returnBytes.length);
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "compressBytes byteArrayOutputStream: null");
}
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "compressBytes IOException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "compressBytes NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "compressBytes Exception");
e.printStackTrace();
}
}
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
listener.onCompressionCompleted(returnBytes);
}
/**
* Decompress the audio bytes
*
* @param ctx the application context
* @param bytes the array of audio bytes
* @param rowId the row id of the {@link DBSpeech} they were stored in
* @return an array of decompressed audio bytes
*/
public static byte[] decompressBytes(final Context ctx, final byte[] bytes, final long rowId) {
if (DEBUG) {
MyLog.d(CLS_NAME, "decompressBytes bytes size: " + bytes.length);
}
final long then = System.nanoTime();
ByteArrayInputStream byteArrayInputStream = null;
GZIPInputStream gzipInputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
byte[] returnBytes = null;
try {
byteArrayInputStream = new ByteArrayInputStream(bytes);
gzipInputStream = new GZIPInputStream(byteArrayInputStream);
byteArrayOutputStream = new ByteArrayOutputStream();
final byte[] buffer = new byte[1024];
int count;
while ((count = gzipInputStream.read(buffer)) > 0) {
byteArrayOutputStream.write(buffer, 0, count);
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytes IOException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytes NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytes Exception");
e.printStackTrace();
}
} finally {
try {
if (byteArrayInputStream != null) {
byteArrayInputStream.close();
}
if (gzipInputStream != null) {
gzipInputStream.close();
}
if (byteArrayOutputStream != null) {
byteArrayOutputStream.close();
returnBytes = byteArrayOutputStream.toByteArray();
if (DEBUG) {
MyLog.d(CLS_NAME, "decompressBytes returnBytes size: " + returnBytes.length);
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "decompressBytes byteArrayOutputStream: null");
}
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytes IOException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytes NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytes Exception");
e.printStackTrace();
}
}
}
if (returnBytes == null || returnBytes.length <= 0) {
if (DEBUG) {
MyLog.w(CLS_NAME, "decompressBytes null or empty: deleting entry");
}
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_LESS_FAVORABLE);
final DBSpeech dbs = new DBSpeech(ctx);
dbs.deleteEntry(rowId);
}
});
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
return returnBytes;
}
/**
* Decompress the audio bytes to a file
*
* @param ctx the application context
* @param bytes the array of audio bytes
* @param rowId the row id of the {@link DBSpeech} they were stored in
* @return a file containing the decompressed audio bytes
*/
public static File decompressBytesToFile(final Context ctx, final byte[] bytes, final long rowId) {
if (DEBUG) {
MyLog.d(CLS_NAME, "decompressBytesToFile bytes size: " + bytes.length);
}
final long then = System.nanoTime();
File tempAudioFile = null;
ByteArrayInputStream byteArrayInputStream = null;
GZIPInputStream gzipInputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
byte[] returnBytes = null;
try {
byteArrayInputStream = new ByteArrayInputStream(bytes);
gzipInputStream = new GZIPInputStream(byteArrayInputStream);
byteArrayOutputStream = new ByteArrayOutputStream();
final byte[] buffer = new byte[1024];
int count;
while ((count = gzipInputStream.read(buffer)) > 0) {
byteArrayOutputStream.write(buffer, 0, count);
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytesToFile IOException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytesToFile NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytesToFile Exception");
e.printStackTrace();
}
} finally {
try {
if (byteArrayInputStream != null) {
byteArrayInputStream.close();
}
if (gzipInputStream != null) {
gzipInputStream.close();
}
if (byteArrayOutputStream != null) {
returnBytes = byteArrayOutputStream.toByteArray();
if (returnBytes != null && returnBytes.length > 0) {
if (DEBUG) {
MyLog.d(CLS_NAME, "decompressBytesToFile returnBytes size: " + returnBytes.length);
}
tempAudioFile = UtilsFile.getTempAudioFile(ctx);
if (tempAudioFile != null) {
if (DEBUG) {
MyLog.d(CLS_NAME, "decompressBytesToFile: file name: " + tempAudioFile.getName());
}
final FileOutputStream fos = new FileOutputStream(tempAudioFile);
byteArrayOutputStream.writeTo(fos);
byteArrayOutputStream.close();
fos.write(bytes);
fos.flush();
fos.close();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "tempAudioFile null");
}
}
} else {
if (DEBUG) {
MyLog.d(CLS_NAME, "decompressBytesToFile returnBytes null or empty");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "decompressBytesToFile byteArrayOutputStream: null");
}
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytesToFile IOException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytesToFile NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "decompressBytesToFile Exception");
e.printStackTrace();
}
}
}
if (returnBytes == null || returnBytes.length <= 0) {
if (DEBUG) {
MyLog.w(CLS_NAME, "decompressBytesToFile null or empty: deleting entry");
}
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_LESS_FAVORABLE);
final DBSpeech dbs = new DBSpeech(ctx);
dbs.deleteEntry(rowId);
}
});
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
return tempAudioFile;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/AudioParameters.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
import android.media.AudioFormat;
import android.media.MediaRecorder;
/**
* Created by benrandall76@gmail.com on 12/08/2016.
*/
public class AudioParameters {
private int nChannels;
private int bSamples;
private int audioSource;
private int sampleRateInHz;
private int channelConfig;
private int audioFormat;
public AudioParameters() {
}
public AudioParameters(final int audioFormat, final int audioSource, final int channelConfig,
final int nChannels, final int sampleRateInHz, final int bSamples) {
this.audioFormat = audioFormat;
this.audioSource = audioSource;
this.channelConfig = channelConfig;
this.nChannels = nChannels;
this.sampleRateInHz = sampleRateInHz;
this.bSamples = bSamples;
}
public static AudioParameters getDefault() {
return new AudioParameters(AudioFormat.ENCODING_PCM_16BIT,
MediaRecorder.AudioSource.VOICE_RECOGNITION,
AudioFormat.CHANNEL_IN_MONO, 1, 16000, 16);
}
public static AudioParameters getDefaultBeyondVerbal(){
return new AudioParameters(AudioFormat.ENCODING_PCM_16BIT,
MediaRecorder.AudioSource.VOICE_RECOGNITION,
AudioFormat.CHANNEL_IN_MONO, 1, 8000, 16);
}
public static AudioParameters getDefaultMicrosoft(){
return new AudioParameters(AudioFormat.ENCODING_PCM_16BIT,
MediaRecorder.AudioSource.VOICE_RECOGNITION,
AudioFormat.CHANNEL_IN_MONO, 1, 16000, 16);
}
public int getbSamples() {
return bSamples;
}
public void setbSamples(final int bSamples) {
this.bSamples = bSamples;
}
public int getAudioFormat() {
return audioFormat;
}
public void setAudioFormat(final int audioFormat) {
this.audioFormat = audioFormat;
}
public int getAudioSource() {
return audioSource;
}
public void setAudioSource(final int audioSource) {
this.audioSource = audioSource;
}
public int getChannelConfig() {
return channelConfig;
}
public void setChannelConfig(final int channelConfig) {
this.channelConfig = channelConfig;
}
public int getnChannels() {
return nChannels;
}
public void setnChannels(final int nChannels) {
this.nChannels = nChannels;
}
public int getSampleRateInHz() {
return sampleRateInHz;
}
public void setSampleRateInHz(final int sampleRateInHz) {
this.sampleRateInHz = sampleRateInHz;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/IMic.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
/**
* Created by benrandall76@gmail.com on 12/08/2016.
*/
public interface IMic {
void onBufferReceived(final int bufferReadResult, final byte[] buffer);
void onError(final int error);
void onPauseDetected();
void onRecordingStarted();
void onRecordingEnded();
void onFileWriteComplete(final boolean success);
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/RecognitionMic.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
import android.content.Context;
import android.media.AudioRecord;
import android.speech.SpeechRecognizer;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
import ai.saiy.android.audio.pause.PauseDetector;
import ai.saiy.android.audio.pause.PauseListener;
import ai.saiy.android.files.FileCreator;
import ai.saiy.android.recognition.Recognition;
import ai.saiy.android.recognition.SaiyRecognitionListener;
import ai.saiy.android.utils.MyLog;
/**
* Created by benrandall76@gmail.com on 12/08/2016.
*/
public class RecognitionMic implements PauseListener {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = RecognitionMic.class.getSimpleName();
public static final int SAMPLE_RATE_HZ_16000 = 16000;
public static final int SAMPLE_RATE_HZ_8000 = 8000;
private static final int ON_READY_FOR_SPEECH = 0;
private static final int ON_BEGINNING_OF_SPEECH = 1;
private static final int ON_ERROR = 2;
private static final int ON_END_OF_SPEECH = 3;
private final AtomicBoolean isRecording = new AtomicBoolean();
private final AtomicBoolean isAvailable = new AtomicBoolean(true);
private final AtomicBoolean isInterrupted = new AtomicBoolean();
private final AtomicBoolean thrown = new AtomicBoolean();
private final SaiySoundPool ssp;
private FileCreator fileCreator;
private PauseDetector pauseDetector;
private volatile SaiyRecorder saiyRecorder;
private final Object audioLock = new Object();
private final Object errorLock = new Object();
private final SaiyRecognitionListener listener;
private final boolean pauseDetection;
private final AtomicBoolean writeToFile;
private IMic iMic;
private final Object lock = new Object();
private final Context mContext;
public RecognitionMic(@NonNull final Context mContext, @Nullable final SaiyRecognitionListener listener,
@NonNull final AudioParameters audioParameters, final boolean pauseDetection,
final long pauseIgnoreTime, final boolean enhance, final boolean writeToFile,
@NonNull final SaiySoundPool ssp) {
this.mContext = mContext;
this.listener = listener;
this.pauseDetection = pauseDetection;
this.writeToFile = new AtomicBoolean(writeToFile);
this.ssp = ssp;
if (this.pauseDetection) {
pauseDetector = new PauseDetector(this, audioParameters.getSampleRateInHz(),
audioParameters.getnChannels(), pauseIgnoreTime);
}
saiyRecorder = new SaiyRecorder(audioParameters.getAudioSource(),
audioParameters.getSampleRateInHz(), audioParameters.getChannelConfig(),
audioParameters.getAudioFormat(), enhance);
if (this.writeToFile.get()) {
fileCreator = new FileCreator(this.mContext, audioParameters.getnChannels(),
audioParameters.getSampleRateInHz(), audioParameters.getbSamples());
}
}
public File getFile() {
return fileCreator.getDefaultFile();
}
public void setMicListener(@NonNull final IMic iMic) {
this.iMic = iMic;
}
public IMic getMicListener() {
return iMic;
}
public Object getLock() {
return lock;
}
public boolean isRecording() {
return isRecording.get();
}
public boolean isAvailable() {
return isAvailable.get();
}
public boolean isInterrupted() {
return isInterrupted.get();
}
public SaiyRecognitionListener getRecognitionListener() {
return listener;
}
public Context getContext() {
return this.mContext;
}
public void stopRecording() {
if (DEBUG) {
MyLog.i(CLS_NAME, "stopRecording");
}
if (isRecording.get()) {
isInterrupted.set(true);
isRecording.set(false);
Recognition.setState(Recognition.State.IDLE);
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "stopRecording: not recording");
}
}
}
@Override
public void onPauseDetected() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onPauseDetected");
}
if (isRecording.get()) {
isRecording.set(false);
Recognition.setState(Recognition.State.IDLE);
iMic.onPauseDetected();
}
}
public void startRecording() {
if (DEBUG) {
MyLog.i(CLS_NAME, "startRecording");
}
isRecording.set(true);
if (iMic == null) {
throw new IllegalArgumentException("No iMic listener is set");
}
new Thread() {
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
ssp.play(ssp.getBeepStart());
if (pauseDetection) {
pauseDetector.begin();
}
final int bufferSize = saiyRecorder.getBufferSize();
final byte[] buffer = new byte[bufferSize];
switch (saiyRecorder.initialise()) {
case AudioRecord.STATE_INITIALIZED:
try {
switch (saiyRecorder.startRecording()) {
case AudioRecord.RECORDSTATE_RECORDING:
if (DEBUG) {
MyLog.i(CLS_NAME, "AudioRecord.RECORDSTATE_RECORDING");
}
int count = 0;
while (isRecording.get() && saiyRecorder != null
&& saiyRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
if (count == 0) {
if (DEBUG) {
MyLog.i(CLS_NAME, "Recording Started");
}
Recognition.setState(Recognition.State.LISTENING);
recognitionListenerAction(ON_READY_FOR_SPEECH);
iMic.onRecordingStarted();
count++;
}
final int bufferReadResult = saiyRecorder.read(buffer);
if (pauseDetection && !pauseDetector.hasDetected()) {
pauseDetector.addLength(buffer, bufferReadResult);
pauseDetector.monitor();
}
if (writeToFile.get()) {
fileCreator.passBuffer(buffer);
}
iMic.onBufferReceived(bufferReadResult, buffer);
}
break;
case AudioRecord.ERROR:
default:
if (DEBUG) {
MyLog.w(CLS_NAME, "AudioRecord.ERROR");
}
onError(SpeechRecognizer.ERROR_AUDIO);
break;
}
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IllegalStateException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
}
audioShutdown();
break;
case AudioRecord.STATE_UNINITIALIZED:
if (DEBUG) {
MyLog.w(CLS_NAME, "AudioRecord.STATE_UNINITIALIZED");
}
onError(SpeechRecognizer.ERROR_AUDIO);
break;
}
}
}.start();
}
private void onError(final int error) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onError");
}
synchronized (errorLock) {
if (!thrown.get()) {
thrown.set(true);
recognitionListenerAction(ON_ERROR);
audioShutdown();
iMic.onError(error);
}
}
}
/**
* Force shutdown the microphone and release the resources, without calling any of the
* listeners
*/
public void forceAudioShutdown() {
synchronized (audioLock) {
if (saiyRecorder != null) {
isAvailable.set(false);
Recognition.setState(Recognition.State.IDLE);
saiyRecorder.shutdown(CLS_NAME);
saiyRecorder = null;
fileCreator = null;
} else {
if (DEBUG) {
MyLog.v(CLS_NAME, "forceAudioShutdown: saiyRecorder already null");
}
}
System.gc();
}
}
/**
* Shutdown the microphone and release the resources
*/
private void audioShutdown() {
if (DEBUG) {
MyLog.i(CLS_NAME, "audioShutdown");
}
synchronized (audioLock) {
if (saiyRecorder != null) {
isAvailable.set(false);
ssp.play(ssp.getBeepStop());
Recognition.setState(Recognition.State.IDLE);
saiyRecorder.shutdown(CLS_NAME);
saiyRecorder = null;
releaseLock();
recognitionListenerAction(ON_END_OF_SPEECH);
iMic.onRecordingEnded();
} else {
if (DEBUG) {
MyLog.v(CLS_NAME, "audioShutdown: saiyRecorder already null");
}
}
if (writeToFile.get()) {
writeToFile.set(false);
iMic.onFileWriteComplete(fileCreator.completeWrite());
}
if (DEBUG) {
MyLog.v(CLS_NAME, "audioShutdown: finished synchronisation");
}
System.gc();
}
}
/**
* Notify waiting threads
*/
private void releaseLock() {
if (DEBUG) {
MyLog.i(CLS_NAME, "audioShutdown: releaseLock");
}
synchronized (lock) {
lock.notifyAll();
}
}
/**
* Send callbacks through to our {@link SaiyRecognitionListener}
*
* @param action of the listener
*/
private void recognitionListenerAction(final int action) {
if (DEBUG) {
MyLog.i(CLS_NAME, "recognitionListenerAction");
}
if (listener == null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "recognitionListenerAction: listener not required");
}
return;
}
switch (action) {
case ON_READY_FOR_SPEECH:
if (DEBUG) {
MyLog.i(CLS_NAME, "recognitionListenerAction: ON_READY_FOR_SPEECH");
}
listener.onReadyForSpeech(null);
break;
case ON_BEGINNING_OF_SPEECH:
if (DEBUG) {
MyLog.i(CLS_NAME, "recognitionListenerAction: ON_BEGINNING_OF_SPEECH");
}
listener.onBeginningOfSpeech();
break;
case ON_ERROR:
if (DEBUG) {
MyLog.w(CLS_NAME, "recognitionListenerAction: ON_ERROR");
}
listener.onError(SpeechRecognizer.ERROR_AUDIO);
break;
case ON_END_OF_SPEECH:
if (DEBUG) {
MyLog.i(CLS_NAME, "recognitionListenerAction: ON_END_OF_SPEECH");
}
listener.onEndOfSpeech();
break;
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/SaiyAudio.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.media.audiofx.AcousticEchoCanceler;
import android.media.audiofx.AutomaticGainControl;
import android.media.audiofx.NoiseSuppressor;
import android.os.Build;
import ai.saiy.android.utils.MyLog;
/**
* Wrapper around {@link AudioRecord} to set enhancers by default if they are available.
*
* Created by benrandall76@gmail.com on 12/02/2016.
*/
public class SaiyAudio extends AudioRecord {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = SaiyAudio.class.getSimpleName();
/**
* Class constructor.
* Though some invalid parameters will result in an {@link IllegalArgumentException} exception,
* other errors do not. Thus you should call {@link #getState()} immediately after construction
* to confirm that the object is usable.
*
* @param audioSource the recording source.
* See {@link MediaRecorder.AudioSource} for the recording source definitions.
* @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
* rate that is guaranteed to work on all devices, but other rates such as 22050,
* 16000, and 11025 may work on some devices.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_IN_MONO} and
* {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
* to work on all devices.
* @param audioFormat the format in which the audio data is to be returned.
* See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},
* and {@link AudioFormat#ENCODING_PCM_FLOAT}.
* @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
* to during the recording. New audio data can be read from this buffer in smaller chunks
* than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
* required buffer size for the successful creation of an AudioRecord instance. Using values
* smaller than getMinBufferSize() will result in an initialization failure.
* @param enhance if audio enhancers should be added
*/
public SaiyAudio(final int audioSource, final int sampleRateInHz, final int channelConfig,
final int audioFormat, final int bufferSizeInBytes, final boolean enhance)
throws IllegalArgumentException {
super(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);
if (enhance && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (DEBUG) {
MyLog.i(CLS_NAME, "attempting audio enhancements");
}
setEnhancers(getAudioSessionId());
}
}
/**
* Attempt to set enhancers available on modern devices.
*
* These are hardware dependent, not build version. Although the APIs weren't available to
* devices until API Level 16
*/
@SuppressWarnings("NewApi")
private void setEnhancers(final int sessionId) {
if (!DEBUG) {
NoiseSuppressor.create(sessionId);
AcousticEchoCanceler.create(sessionId);
AutomaticGainControl.create(sessionId);
} else {
if (NoiseSuppressor.create(sessionId) == null) {
MyLog.i(CLS_NAME, "NoiseSuppressor null");
} else {
MyLog.i(CLS_NAME, "NoiseSuppressor success");
}
if (AcousticEchoCanceler.create(sessionId) == null) {
MyLog.i(CLS_NAME, "AcousticEchoCanceler null");
} else {
MyLog.i(CLS_NAME, "AcousticEchoCanceler success");
}
if (AutomaticGainControl.create(sessionId) == null) {
MyLog.i(CLS_NAME, "AutomaticGainControl null");
} else {
MyLog.i(CLS_NAME, "AutomaticGainControl success");
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/SaiyAudioTrack.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import ai.saiy.android.tts.SaiyProgressListener;
import ai.saiy.android.utils.MyLog;
/**
* Wrapper class around the {@link AudioTrack} object to handle application specific eventualities
*
* Created by benrandall76@gmail.com on 28/04/2016.
*/
public class SaiyAudioTrack extends AudioTrack {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = SaiyAudioTrack.class.getSimpleName();
private static final int WAV_OFFSET = 44;
private static final int MAX_AUDIO_BUFFER_SIZE = 8192;
private static final int SAMPLE_RATE_HZ = 16000;
private static final int CHANNEL_OUT = AudioFormat.CHANNEL_OUT_MONO;
private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int MIN_BUFFER_SIZE = AudioTrack.getMinBufferSize(SAMPLE_RATE_HZ, CHANNEL_OUT, ENCODING);
private volatile SaiyProgressListener listener;
private final BlockingQueue> byteQueue = new LinkedBlockingQueue<>();
/**
* Class constructor.
*
* @param streamType the type of the audio stream. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
* {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
* @param sampleRateInHz the initial source sample rate expressed in Hz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT},
* {@link AudioFormat#ENCODING_PCM_8BIT},
* and {@link AudioFormat#ENCODING_PCM_FLOAT}.
* @param bufferSizeInBytes the total size (in bytes) of the internal buffer where audio data is
* read from for playback. This should be a multiple of the frame size in bytes.
* If the track's creation mode is {@link #MODE_STATIC},
* this is the maximum length sample, or audio clip, that can be played by this instance.
*
If the track's creation mode is {@link #MODE_STREAM},
* this should be the desired buffer size
* for the AudioTrack to satisfy the application's
* natural latency requirements.
* If bufferSizeInBytes is less than the
* minimum buffer size for the output sink, it is automatically increased to the minimum
* buffer size.
* The method {@link #getBufferSizeInFrames()} returns the
* actual size in frames of the native buffer created, which
* determines the frequency to write
* to the streaming AudioTrack to avoid under-run.
* @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}
* @throws java.lang.IllegalArgumentException
*/
public SaiyAudioTrack(final int streamType, final int sampleRateInHz, final int channelConfig,
final int audioFormat, final int bufferSizeInBytes, final int mode) throws IllegalArgumentException {
super(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, mode);
}
@Override
public int write(@NonNull final byte[] audioData, final int offsetInBytes, final int sizeInBytes) {
return super.write(audioData, offsetInBytes, sizeInBytes);
}
/**
* Stops playing the audio data.
* When used on an instance created in {@link #MODE_STREAM} mode, audio will stop playing
* after the last buffer that was written has been played. For an immediate stop, use
* {@link #pause()}, followed by {@link #flush()} to discard audio data that hasn't been played
* back yet.
*
* @throws IllegalStateException
*/
@Override
public void stop() throws IllegalStateException {
super.stop();
}
/**
* Handles the two circumstances of stop being called at the end of standard playback, or an
* interrupt call, where the queue needs to be cleared. In the case that the queue needs to be
* cleared, an {@link IllegalStateException} is likely to be thrown, but handled gracefully
* in {@link #enqueue(byte[], String)} and here.
*
* @param interrupt true if all pending audio should be stopped.
*/
public void stop(final boolean interrupt) {
if (interrupt) {
byteQueue.clear();
flush();
release();
}
try {
super.stop();
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "stop: IllegalStateException");
e.printStackTrace();
}
}
}
/**
* Listener to notify of audio callbacks
*
* @param listener the {@link SaiyProgressListener}
*/
public void setListener(@NonNull final SaiyProgressListener listener) {
this.listener = listener;
}
/**
* Add a byte[] of uncompressed audio to the queue to process. If the queue isn't currently
* processing any audio, it will be started.
*
* @param uncompressedBytes the uncompressed audio byte[]
* @param utteranceId the utterance id
*/
public void enqueue(@NonNull final byte[] uncompressedBytes, @NonNull final String utteranceId) {
if (DEBUG) {
MyLog.i(CLS_NAME, "enqueue: queue size: " + byteQueue.size());
}
synchronized (byteQueue) {
if (byteQueue.isEmpty()) {
byteQueue.add(new Pair<>(uncompressedBytes, utteranceId));
try {
process();
} catch (final NoSuchElementException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "enqueue: process: NoSuchElementException");
e.printStackTrace();
}
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "enqueue: process: IllegalStateException");
e.printStackTrace();
}
}
} else {
byteQueue.add(new Pair<>(uncompressedBytes, utteranceId));
}
}
}
/**
* Process any pending audio
*/
private synchronized void process() throws NoSuchElementException, IllegalStateException {
if (DEBUG) {
MyLog.i(CLS_NAME, "process");
}
while (!byteQueue.isEmpty()) {
if (DEBUG) {
MyLog.i(CLS_NAME, "processing: queue size: " + byteQueue.size());
}
play();
listener.onStart(byteQueue.element().second);
int offset = SaiyAudioTrack.WAV_OFFSET;
int bytesToWrite;
String utteranceId = null;
while (byteQueue.element() != null && offset < byteQueue.element().first.length) {
utteranceId = byteQueue.element().second;
bytesToWrite = Math.min(SaiyAudioTrack.MAX_AUDIO_BUFFER_SIZE,
byteQueue.element().first.length - offset);
write(byteQueue.element().first, offset, bytesToWrite);
offset += bytesToWrite;
}
byteQueue.remove();
if (byteQueue.isEmpty()) {
stop(false);
}
listener.onDone(utteranceId);
}
if (DEBUG) {
MyLog.i(CLS_NAME, "processing complete. Queue empty");
}
}
/**
* Static helper method to create a {@link SaiyAudioTrack} object, as the Constructor parameters
* will always be the same.
*
* @return a new {@link SaiyAudioTrack} object
*/
public static SaiyAudioTrack getSaiyAudioTrack() {
try {
return new SaiyAudioTrack(AudioManager.STREAM_MUSIC, SaiyAudioTrack.SAMPLE_RATE_HZ,
SaiyAudioTrack.CHANNEL_OUT, SaiyAudioTrack.ENCODING, SaiyAudioTrack.MIN_BUFFER_SIZE,
SaiyAudioTrack.MODE_STREAM);
} catch (final IllegalArgumentException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getSaiyAudioTrack: IllegalArgumentException");
e.printStackTrace();
}
return null;
}
}
/**
* Static helper method to create a {@link SaiyAudioTrack} object.
*
* @param stream the stream type
* @return a new {@link SaiyAudioTrack} object
*/
public static SaiyAudioTrack getSaiyAudioTrack(final int stream) {
try {
return new SaiyAudioTrack(stream, SaiyAudioTrack.SAMPLE_RATE_HZ,
SaiyAudioTrack.CHANNEL_OUT, SaiyAudioTrack.ENCODING, SaiyAudioTrack.MIN_BUFFER_SIZE,
SaiyAudioTrack.MODE_STREAM);
} catch (final IllegalArgumentException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getSaiyAudioTrack: IllegalArgumentException");
e.printStackTrace();
}
return null;
}
}
public boolean streamMatches(final int stream) {
return stream == super.getStreamType();
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/SaiyRecorder.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Looper;
import android.support.annotation.NonNull;
import ai.saiy.android.utils.MyLog;
/**
* Wrapper around the {@link AudioRecord class} to handle errors and setup.
*
* Note - currently only configured for uncompressed recordings.
*
* Created by benrandall76@gmail.com on 15/02/2016.
*/
public class SaiyRecorder {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = SaiyRecorder.class.getSimpleName();
private SaiyAudio saiyAudio;
// Will use in future compressed recordings
private final short nChannels = 1;
private final short bSamples = 16;
private final int TIMER_INTERVAL = 120;
private int framePeriod;
private final int audioSource;
private final int sampleRateInHz;
private final int channelConfig;
private final int audioFormat;
private final int bufferSizeInBytes;
private final boolean enhance;
/**
* Constructor
*
* Uses the most common application defaults
*/
public SaiyRecorder() {
this.audioSource = MediaRecorder.AudioSource.VOICE_RECOGNITION;
this.sampleRateInHz = 8000;
this.channelConfig = AudioFormat.CHANNEL_IN_MONO;
this.audioFormat = AudioFormat.ENCODING_PCM_16BIT;
this.bufferSizeInBytes = calculateBufferSize();
this.enhance = true;
}
/**
* Constructor
*
* @param audioSource the audio source
* @param sampleRateInHz the sampling rate in hertz
* @param channelConfig the channel configuration
* @param audioFormat the audio format
*/
public SaiyRecorder(final int audioSource, final int sampleRateInHz, final int channelConfig,
final int audioFormat, final boolean enhance) {
this.audioSource = audioSource;
this.sampleRateInHz = sampleRateInHz;
this.channelConfig = channelConfig;
this.audioFormat = audioFormat;
this.bufferSizeInBytes = calculateBufferSize();
this.enhance = enhance;
}
/**
* Constructor
*
* @param audioSource the audio source
* @param sampleRateInHz the sampling rate in hertz
* @param channelConfig the channel configuration
* @param audioFormat the audio format
* @param bufferSizeInBytes the buffer size in bytes
*/
public SaiyRecorder(final int audioSource, final int sampleRateInHz, final int channelConfig,
final int audioFormat, final int bufferSizeInBytes, final boolean enhance) {
this.audioSource = audioSource;
this.sampleRateInHz = sampleRateInHz;
this.channelConfig = channelConfig;
this.audioFormat = audioFormat;
this.bufferSizeInBytes = bufferSizeInBytes;
this.enhance = enhance;
}
/**
* Initialise the Voice Recorder
*
* @return The audio record initialisation state.
*/
public int initialise() {
int count = 0;
while (count < 4) {
count++;
saiyAudio = new SaiyAudio(audioSource, sampleRateInHz, channelConfig, audioFormat,
bufferSizeInBytes, enhance);
if (saiyAudio.getState() == AudioRecord.STATE_INITIALIZED) {
return AudioRecord.STATE_INITIALIZED;
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "SaiyAudio reinitialisation attempt ~ " + count);
}
if (Looper.myLooper() != null && Looper.myLooper() != Looper.getMainLooper()) {
// Give the audio object a small chance to sort itself out
try {
Thread.sleep(250);
} catch (InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "SaiyAudio InterruptedException");
e.printStackTrace();
}
}
}
}
}
if (DEBUG) {
MyLog.w(CLS_NAME, "SaiyAudio initialisation failed");
}
return AudioRecord.STATE_UNINITIALIZED;
}
/**
* Reads audio data from the audio hardware for recording into a byte array.
*
* @param buffer the array to which the recorded audio data is written.
* @return the number of bytes that were read. The number of bytes will not exceed sizeInBytes.
*/
public int read(@NonNull final byte[] buffer) {
return saiyAudio.read(buffer, 0, buffer.length);
}
/**
* Reads audio data from the audio hardware for recording into a short array.
*
* @param audioData short audio data
* @param offsetInShorts the offset to read from
* @param sizeInShorts size of the audio data
* @return size read
*/
public int read(short[] audioData, int offsetInShorts, int sizeInShorts) {
return saiyAudio.read(audioData, offsetInShorts, sizeInShorts);
}
/**
* Get the recording state from the audio session.
*
* @return the audio session state
*/
public int getRecordingState() {
return saiyAudio.getRecordingState();
}
public int getBufferSize() {
return bufferSizeInBytes;
}
/**
* Start the audio recording session.
*/
public int startRecording() {
try {
saiyAudio.startRecording();
if (saiyAudio.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
if (DEBUG) {
MyLog.i(CLS_NAME, "AudioRecord == RECORDSTATE_RECORDING");
}
return AudioRecord.RECORDSTATE_RECORDING;
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "AudioRecord != RECORDSTATE_RECORDING");
}
}
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "AudioRecord IllegalStateException");
e.printStackTrace();
}
} catch (NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "AudioRecord NullPointerException");
e.printStackTrace();
}
}
return AudioRecord.ERROR;
}
/**
* Shutdown the audio session
*
* @param from a String label to identify the source of the request. Useful for logging only.
*/
public void shutdown(@NonNull final String from) {
if (saiyAudio == null) {
return;
}
if (saiyAudio.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
try {
saiyAudio.stop();
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "saiyAudio.stop(): IllegalStateException ~ " + from);
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "saiyAudio.stop(): NullPointerException ~ " + from);
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "saiyAudio.stop(): Exception ~ " + from);
}
}
} else {
if (DEBUG) {
MyLog.v(CLS_NAME, "saiyAudio != AudioRecord.RECORDSTATE_RECORDING ~ " + from);
}
}
try {
saiyAudio.release();
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "saiyAudio.release(): IllegalStateException ~ " + from);
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "saiyAudio.release(): NullPointerException ~ " + from);
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "saiyAudio.release(): Exception ~ " + from);
}
}
if (DEBUG) {
MyLog.d(CLS_NAME, "saiyAudio set to NULL ~ " + from);
}
saiyAudio = null;
}
/**
* Calculate the buffer size.
*
* @return the calculated buffer size
*/
private int calculateBufferSize() {
framePeriod = sampleRateInHz * TIMER_INTERVAL / 1000;
int bufferSize = framePeriod * 2 * bSamples * nChannels / 8;
if (DEBUG) {
MyLog.i(CLS_NAME, "bufferSize: " + bufferSize);
}
final int minBuff = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
switch (minBuff) {
case AudioRecord.ERROR:
case AudioRecord.ERROR_BAD_VALUE:
if (DEBUG) {
MyLog.w(CLS_NAME, "AudioRecord.ERROR/ERROR_BAD_VALUE");
}
break;
default:
if (DEBUG) {
MyLog.i(CLS_NAME, "minBuff: " + minBuff);
}
if (bufferSize < minBuff) {
bufferSize = minBuff;
// Unused for now
framePeriod = bufferSize / (2 * bSamples * nChannels / 8);
}
break;
}
if (DEBUG) {
MyLog.i(CLS_NAME, "bufferSize returning: " + bufferSize);
}
return bufferSize;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/SaiySoundPool.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.support.annotation.NonNull;
import ai.saiy.android.R;
import ai.saiy.android.utils.MyLog;
/**
* Wrapper class around {@link SoundPool} to handle short sounds
*
* Created by benrandall76@gmail.com on 14/09/2016.
*/
public class SaiySoundPool implements SoundPool.OnLoadCompleteListener {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = SaiySoundPool.class.getSimpleName();
public static final int VOICE_RECOGNITION = 1;
private static final int MAX_STREAMS = 2;
private volatile SoundPool sp;
private int beepStart;
private int beepStop;
private boolean beepStopInitialised;
private boolean beepStartInitialised;
/**
* Set up the {@link SoundPool} object, adding the relevant sounds automatically and
* generating their id for future use.
*
* @param ctx the application context
* @param type the type of media the {@link SoundPool} will be used for
*/
public SaiySoundPool setUp(@NonNull final Context ctx, final int type) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
sp = new SoundPool.Builder().setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()).build();
} else {
//noinspection deprecation
sp = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
}
sp.setOnLoadCompleteListener(this);
switch (type) {
case VOICE_RECOGNITION:
new Thread() {
public void run() {
beepStart = sp.load(ctx, R.raw.beep_high, 1);
beepStop = sp.load(ctx, R.raw.beep_low, 1);
}
}.start();
break;
default:
break;
}
return this;
}
/**
* Play an already loaded sound
*
* @param soundId the id of the sound generated in {@link #setUp(Context, int)}
* @return non-zero stream id if successful
*/
public int play(final int soundId) {
if (sp == null) {
return 0;
}
return sp.play(soundId, 0.05f, 0.05f, 1, 0, 1f);
}
/**
* Release the {@link SoundPool} and resources
*/
public void release() {
sp.release();
sp = null;
}
public int getBeepStart() {
return beepStart;
}
public int getBeepStop() {
return beepStop;
}
public boolean isBeepStartInitialised() {
return beepStartInitialised;
}
public boolean isBeepStopInitialised() {
return beepStopInitialised;
}
/**
* Called when a sound has completed loading.
*
* @param soundPool SoundPool object from the load() method
* @param sampleId the sample ID of the sound loaded.
* @param status the status of the load operation (0 = success)
*/
@Override
public void onLoadComplete(final SoundPool soundPool, final int sampleId, final int status) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onLoadComplete: " + sampleId + " ~ " + status);
}
if (sampleId == beepStart) {
beepStartInitialised = true;
} else if (sampleId == beepStop) {
beepStopInitialised = true;
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/pause/PauseDetector.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2011-2016, Institute of Cybernetics at Tallinn University of Technology
*
* 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.
*/
package ai.saiy.android.audio.pause;
import ai.saiy.android.utils.MyLog;
/**
* Created by benrandall76@gmail.com on 15/02/2016.
*
* Class to detect when a user has stopped speaking adapted from the excellent library from the
* author below.
*
* @author Kaarel Kaljurand
*/
public class PauseDetector {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = PauseDetector.class.getSimpleName();
private final int PAUSE_THRESHOLD = 7;
public static final long DEFAULT_PAUSE_IGNORE_TIME = 4000;
private final int MAX_RECORDING_LENGTH = 120;
private long pauseCheck;
private final long pauseIgnoreTime;
private final short RESOLUTION_IN_BYTES = 2;
private final int mOneSec;
// TODO: use: mRecording.length instead
// TODO: synchronisation and atomics
private volatile int mRecordedLength = 0;
private final PauseListener pauseListener;
private volatile boolean hasDetected;
private final byte[] mRecording;
private volatile double mAvgEnergy = 0;
private final int maxSize;
/**
* Constructor for the PauseDetector
*
* @param pauseListener the listener connected to the recognition object
* @param sampleRateInHz the sampling rate in hertz
* @param nChannels the number of channels
*/
public PauseDetector(final PauseListener pauseListener, final int sampleRateInHz,
final int nChannels, final long pauseIgnoreTime) {
this.pauseListener = pauseListener;
this.pauseIgnoreTime = pauseIgnoreTime;
mOneSec = RESOLUTION_IN_BYTES * nChannels * sampleRateInHz;
mRecording = new byte[mOneSec * MAX_RECORDING_LENGTH];
hasDetected = false;
maxSize = mRecording.length;
}
/**
* Start the pause detection
*/
public void begin() {
pauseCheck = System.currentTimeMillis();
}
/**
* Add information from the buffer
*
* @param buffer the audio buffer
* @param bufferReadResult the previous read result
*/
public void addLength(final byte[] buffer, final int bufferReadResult) {
synchronized (this) {
if (!hasDetected) {
new Thread() {
public void run() {
mRecordedLength += buffer.length;
if (mRecordedLength <= maxSize) {
System.arraycopy(buffer, 0, mRecording, mRecordedLength, bufferReadResult);
} else {
hasDetected = true;
}
}
}.start();
}
}
}
/**
* Check if a pause has been detected
*/
public boolean hasDetected() {
return hasDetected;
}
/**
* Monitor the audio data
*/
public void monitor() {
synchronized (this) {
if (!hasDetected) {
new Thread() {
public void run() {
double pauseScore = getPauseScore();
if (DEBUG) {
MyLog.i(CLS_NAME, "Pause score: " + pauseScore);
}
if (System.currentTimeMillis() - pauseCheck < pauseIgnoreTime) {
if (DEBUG) {
MyLog.i(CLS_NAME, "PAUSE_IGNORE_TIME: too early");
}
} else {
if (pauseScore > PAUSE_THRESHOLD) {
hasDetected = true;
pauseListener.onPauseDetected();
}
}
}
}.start();
if (DEBUG) {
int running = 0;
for (final Thread thread : Thread.getAllStackTraces().keySet()) {
if (thread.getState() == Thread.State.RUNNABLE) {
running++;
}
}
MyLog.v(CLS_NAME, "Threads running: " + running);
}
}
}
}
/**
* In order to calculate if the user has stopped speaking we take the
* data from the last second of the recording, map it to a number
* and compare this number to the numbers obtained previously. We
* return a confidence score (0-INF) of a longer pause having occurred in the
* speech input.
*
* TODO: base the implementation on some well-known technique.
* TODO: behaves very differently depending on the {@link android.media.MediaRecorder.AudioSource}
*
* @return positive value which the caller can use to determine if there is a pause
*/
private double getPauseScore() {
long t2 = getRms(mRecordedLength, mOneSec);
if (t2 == 0) {
return 0;
}
double t = mAvgEnergy / t2;
mAvgEnergy = (2 * mAvgEnergy + t2) / 3;
return t;
}
private long getRms(int end, int span) {
int begin = end - span;
if (begin < 0) {
begin = 0;
}
if (0 != (begin % 2)) {
begin++;
}
long sum = 0;
for (int i = begin; i < end; i += 2) {
short curSample = getShort(mRecording[i], mRecording[i + 1]);
sum += curSample * curSample;
}
return sum;
}
private short getShort(byte argB1, byte argB2) {
return (short) (argB1 | (argB2 << 8));
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/audio/pause/PauseListener.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.audio.pause;
/**
* Created by benrandall76@gmail.com on 15/02/2016.
*/
public interface PauseListener {
void onPauseDetected();
}
================================================
FILE: app/src/main/java/ai/saiy/android/broadcast/BRBoot.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.broadcast;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import ai.saiy.android.service.helper.LocalRequest;
import ai.saiy.android.service.helper.SelfAwareHelper;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
import ai.saiy.android.utils.UtilsString;
/**
* Broadcast Receiver for Boot Completed.
*
* Created by benrandall76@gmail.com on 25/03/2016.
*/
public class BRBoot extends BroadcastReceiver {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = BRBoot.class.getSimpleName();
@Override
public void onReceive(final Context context, final Intent intent) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onReceive");
}
if (SPH.getSelfAwareEnabled(context.getApplicationContext())
&& SPH.getStartAtBoot(context.getApplicationContext())) {
final String action = getAction(intent);
if (UtilsString.notNaked(action) && action.equals(Intent.ACTION_BOOT_COMPLETED)
|| action.equals("android.intent.action.QUICKBOOT_POWERON")
|| action.equals("com.htc.intent.action.QUICKBOOT_POWERON")) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onReceive: starting service");
}
SelfAwareHelper.startSelfAwareIfRequired(context.getApplicationContext());
if (SPH.getHotwordBoot(context.getApplicationContext())) {
final LocalRequest request = new LocalRequest(context.getApplicationContext());
request.prepareDefault(LocalRequest.ACTION_START_HOTWORD, null);
request.execute();
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onReceive: start at boot hotword disabled by user");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "onReceive: action naked or unknown");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onReceive: start at boot disabled by user");
}
}
}
/**
* Get the Intent action, if there is one.
*
* @param intent received by the Broadcast
* @return the Intent Action or an empty string
*/
private String getAction(final Intent intent) {
if (intent != null && intent.getAction() != null) {
return intent.getAction();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "onReceive: unknown intent");
}
}
return "";
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/broadcast/BRRemote.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.broadcast;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Set;
import ai.saiy.android.api.helper.BlackListHelper;
import ai.saiy.android.api.request.Regex;
import ai.saiy.android.api.request.SaiyKeyphrase;
import ai.saiy.android.api.request.SaiyRequestParams;
import ai.saiy.android.applications.UtilsApplication;
import ai.saiy.android.command.helper.CC;
import ai.saiy.android.custom.CCC;
import ai.saiy.android.custom.CustomCommand;
import ai.saiy.android.custom.CustomCommandHelper;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.nlu.local.Resolve;
import ai.saiy.android.personality.PersonalityResponse;
import ai.saiy.android.service.helper.LocalRequest;
import ai.saiy.android.tts.SaiyTextToSpeech;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
import ai.saiy.android.utils.UtilsBundle;
import ai.saiy.android.utils.UtilsList;
import ai.saiy.android.utils.UtilsString;
/**
* Class to handle remote applications registering a keyphrase or hot word.
*
* Started from {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)}
* one of {@link Activity#RESULT_OK} or {@link Activity#RESULT_CANCELED} is returned to the
* requesting application.
*
* Created by benrandall76@gmail.com on 25/03/2016.
*/
public class BRRemote extends BroadcastReceiver {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = BRRemote.class.getSimpleName();
private static final String SAIY_INTENT_RECEIVER = "ai.saiy.android.SAIY_INTENT_RECEIVER";
@Override
public void onReceive(final Context context, final Intent intent) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onReceive");
}
if (intent == null) {
if (DEBUG) {
MyLog.w(CLS_NAME, " onHandleIntent: Intent null");
}
return;
}
final String action = intent.getAction();
if (DEBUG) {
examineIntent(intent);
}
if (!UtilsString.notNaked(action)) {
if (DEBUG) {
MyLog.w(CLS_NAME, " onHandleIntent: action null");
}
return;
}
if (!intent.getAction().equals(SaiyKeyphrase.SAIY_REQUEST_RECEIVER)) {
Log.e("Saiy Remote Request", "Incorrect ACTION: rejecting");
return;
}
switch (intent.getIntExtra(SaiyKeyphrase.REQUEST_TYPE, 0)) {
case SaiyKeyphrase.REQUEST_KEYPHRASE:
if (DEBUG) {
MyLog.i(CLS_NAME, "onHandleIntent: REQUEST_KEYPHRASE");
}
final String keyphrase = intent.getStringExtra(SaiyKeyphrase.SAIY_KEYPHRASE);
if (UtilsString.notNaked(keyphrase)) {
final String packageName = intent.getStringExtra(SaiyKeyphrase.REQUESTING_PACKAGE);
if (UtilsString.notNaked(packageName)) {
final BlackListHelper blackListHelper = new BlackListHelper();
if (!blackListHelper.isBlacklisted(context, packageName)) {
final Pair appPair = UtilsApplication.getAppNameFromPackage(
context.getApplicationContext(), packageName);
if (appPair.first && UtilsString.notNaked(appPair.second)) {
final Locale vrLocale = SPH.getVRLocale(context.getApplicationContext());
final SupportedLanguage sl = SupportedLanguage.getSupportedLanguage(
vrLocale);
final ArrayList voiceData = new ArrayList<>(1);
voiceData.add(keyphrase);
final float[] confidence = new float[1];
confidence[0] = 1f;
final ArrayList> resolvePair = new Resolve(
context.getApplicationContext(), voiceData, confidence, sl).resolve();
if (!UtilsList.notNaked(resolvePair)) {
final CustomCommand customCommand = new CustomCommand(CCC.CUSTOM_INTENT_SERVICE,
CC.COMMAND_USER_CUSTOM, keyphrase, SaiyRequestParams.SILENCE,
SaiyRequestParams.SILENCE,
SPH.getTTSLocale(context.getApplicationContext()).toString(),
vrLocale.toString(), LocalRequest.ACTION_SPEAK_ONLY);
final Regex regex = (Regex) intent.getSerializableExtra(
SaiyKeyphrase.KEYPHRASE_REGEX);
switch (regex) {
case MATCHES:
case STARTS_WITH:
case ENDS_WITH:
case CONTAINS:
customCommand.setRegex(regex);
break;
case CUSTOM:
customCommand.setRegex(regex);
customCommand.setRegularExpression(
intent.getStringExtra(SaiyKeyphrase.REGEX_CONTENT));
break;
}
final Intent remoteIntent = new Intent(SAIY_INTENT_RECEIVER);
remoteIntent.setPackage(packageName);
final Bundle bundle = intent.getExtras();
if (UtilsBundle.notNaked(bundle)) {
if (!UtilsBundle.isSuspicious(bundle)) {
if (DEBUG) {
examineIntent(intent);
}
remoteIntent.putExtras(bundle);
customCommand.setIntent(remoteIntent.toUri(0));
final Pair successPair = CustomCommandHelper.setCommand(
context.getApplicationContext(), customCommand, -1);
if (DEBUG) {
MyLog.i(CLS_NAME, "Custom command created: " + successPair.first);
}
final Bundle responseBundle = new Bundle();
final int responseCode = bundle.getInt(SaiyKeyphrase.SAIY_KEYPHRASE_ID, 0);
if (DEBUG) {
MyLog.i(CLS_NAME, "Custom command responseCode: " + responseCode);
}
responseBundle.putInt(SaiyKeyphrase.SAIY_KEYPHRASE_ID, responseCode);
final LocalRequest request = new LocalRequest(context.getApplicationContext());
request.setVRLocale(vrLocale);
request.setTTSLocale(SPH.getTTSLocale(context.getApplicationContext()));
request.setSupportedLanguage(sl);
request.setQueueType(SaiyTextToSpeech.QUEUE_ADD);
request.setAction(LocalRequest.ACTION_SPEAK_ONLY);
if (successPair.first) {
request.setUtterance(PersonalityResponse.getRemoteCommandRegisterSuccess(
context.getApplicationContext(), sl, appPair.second, keyphrase));
request.execute();
setResult(Activity.RESULT_OK, SaiyKeyphrase.class.getSimpleName(),
responseBundle);
} else {
request.setUtterance(PersonalityResponse.getErrorRemoteCommandRegister(
context.getApplicationContext(), sl, appPair.second));
request.execute();
setResult(Activity.RESULT_CANCELED, SaiyKeyphrase.class.getSimpleName(),
responseBundle);
}
} else {
Log.e("Saiy Remote Request", "Bundle rejected due to contents");
}
} else {
Log.e("Saiy Remote Request", "Request bundle missing contents: rejected");
}
} else {
Log.e("Saiy Remote Request", "Conflict with inbuilt command: rejected");
}
} else {
Log.e("Saiy Remote Request", "Application name undetectable: rejected");
}
} else {
Log.e("Saiy Remote Request", "Application blacklisted: rejected");
}
} else {
Log.e("Saiy Remote Request", "Package name missing: rejected");
}
} else {
Log.e("Saiy Remote Request", "Keyphrase missing: rejected");
}
break;
default:
Log.e("Saiy Remote Request", "Internal type error: rejected");
break;
}
}
/**
* For debugging the intent extras
*
* @param intent containing potential extras
*/
private void examineIntent(@NonNull final Intent intent) {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineIntent");
}
final Bundle bundle = intent.getExtras();
if (bundle != null) {
final Set keys = bundle.keySet();
for (final String key : keys) {
if (DEBUG) {
MyLog.v(CLS_NAME, "examineIntent: " + key + " ~ " + bundle.get(key));
}
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cache/speech/IAudioCompression.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cache.speech;
/**
* Interface for audio compression callbacks
*
* Created by benrandall76@gmail.com on 28/04/2016.
*/
public interface IAudioCompression {
void onCompressionCompleted(final byte[] compressedAudio);
}
================================================
FILE: app/src/main/java/ai/saiy/android/cache/speech/SpeechCachePrepare.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cache.speech;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Process;
import android.speech.tts.Voice;
import android.support.annotation.NonNull;
import ai.saiy.android.audio.AudioCompression;
import ai.saiy.android.database.DBSpeech;
/**
* Class to prepare an entry into {@link DBSpeech}. The method {@link #setUncompressedAudio(byte[])}
* is passed uncompressed audio data, which is subsequently compressed with a callback of completion
* coming from the implemented {@link IAudioCompression} interface.
*
* Created by benrandall76@gmail.com on 27/04/2016.
*/
public class SpeechCachePrepare implements IAudioCompression {
private final Context mContext;
private String engine;
private String utterance;
private String locale;
private Voice voice;
private volatile byte[] compressedAudio;
/**
* Constructor
*
* @param mContext the application context
*/
public SpeechCachePrepare(@NonNull final Context mContext) {
this.mContext = mContext;
}
/**
* Set the uncompressed audio
*
* @param uncompressedAudio byte[]
*/
public void setUncompressedAudio(@NonNull final byte[] uncompressedAudio) {
new Thread() {
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
AudioCompression.compressBytes(SpeechCachePrepare.this, uncompressedAudio);
}
}.start();
}
public byte[] getCompressedAudio() {
return compressedAudio;
}
public String getEngine() {
return engine;
}
public void setEngine(@NonNull final String engine) {
this.engine = engine;
}
public String getLocale() {
return locale;
}
public void setLocale(@NonNull final String locale) {
this.locale = locale;
}
public String getUtterance() {
return utterance;
}
public void setUtterance(@NonNull final String utterance) {
this.utterance = utterance;
}
public Voice getVoice() {
return voice;
}
public void setVoice(@NonNull final Voice voice) {
this.voice = voice;
}
@Override
public void onCompressionCompleted(final byte[] compressedAudio) {
this.compressedAudio = compressedAudio;
executeInsert();
}
/**
* Execute the insertion of the audio data into {@link DBSpeech}
*/
private void executeInsert() {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
final DBSpeech dbSpeech = new DBSpeech(mContext);
dbSpeech.insertRow(SpeechCachePrepare.this);
}
});
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cache/speech/SpeechCacheResult.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cache.speech;
/**
* Helper class to store the results of the speech cache
*
* Created by benrandall76@gmail.com on 27/04/2016.
*/
public class SpeechCacheResult {
private final byte[] compressedBytes;
private final long rowId;
private final boolean success;
public SpeechCacheResult(final byte[] compressedBytes, final long rowId, final boolean success) {
this.compressedBytes = compressedBytes;
this.rowId = rowId;
this.success = success;
}
public byte[] getCompressedBytes() {
return compressedBytes;
}
public long getRowId() {
return rowId;
}
public boolean isSuccess() {
return success;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/AnalysisResult.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal;
import android.support.annotation.NonNull;
/**
* Created by benrandall76@gmail.com on 14/08/2016.
*/
public class AnalysisResult {
private long analysisTime;
private String recordingId;
private String description;
public AnalysisResult() {
}
public AnalysisResult(@NonNull final String recordingId, final long analysisTime, @NonNull final String description) {
this.recordingId = recordingId;
this.analysisTime = analysisTime;
this.description = description;
}
public String getRecordingId() {
return recordingId;
}
public void setRecordingId(final String recordingId) {
this.recordingId = recordingId;
}
public long getAnalysisTime() {
return analysisTime;
}
public void setAnalysisTime(final long analysisTime) {
this.analysisTime = analysisTime;
}
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/AnalysisResultHelper.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Random;
import ai.saiy.android.R;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Analysis;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.AnalysisSummary;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Arousal;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.AudioQuality;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Composite;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Emotions;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Gender;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Group11;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Group21;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Group7;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Mood;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Primary;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Result;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Secondary;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Segment;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Summary;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Temper;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Valence;
import ai.saiy.android.localisation.SaiyResources;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.personality.PersonalityResponse;
import ai.saiy.android.ui.notification.NotificationHelper;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
import ai.saiy.android.utils.UtilsList;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 14/08/2016.
*/
public class AnalysisResultHelper {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = AnalysisResultHelper.class.getSimpleName();
private final Context mContext;
private final SupportedLanguage sl;
/**
* Constructor
*
* @param mContext the application context
* @param sl the {@link SupportedLanguage} object
*/
public AnalysisResultHelper(@NonNull final Context mContext, @NonNull final SupportedLanguage sl) {
this.mContext = mContext;
this.sl = sl;
}
/**
* Analyse the emotion response attempting to construct a verbose explanation of the interpretation to announce to the
* user.
*
* @param emotions the {@link Emotions} response object
*/
public void interpretAndStore(@Nullable final Emotions emotions) {
if (DEBUG) {
MyLog.i(CLS_NAME, "interpretAndStore");
}
if (emotions != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "interpretAndStore: getRecordingId: " + emotions.getRecordingId());
MyLog.i(CLS_NAME, "interpretAndStore: getStatus: " + emotions.getStatus());
}
if (emotions.getStatus().matches(Emotions.SUCCESS)) {
final Result result = emotions.getResult();
if (result != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "result: getDuration: " + result.getDuration());
MyLog.i(CLS_NAME, "result: getSessionStatus: " + result.getSessionStatus());
verboseEmotions(result);
}
final AnalysisResult resultHolder = new AnalysisResult();
resultHolder.setAnalysisTime(System.currentTimeMillis());
resultHolder.setRecordingId(emotions.getRecordingId());
resultHolder.setDescription(constructResponse(result));
if (UtilsString.notNaked(resultHolder.getDescription())) {
if (DEBUG) {
MyLog.i(CLS_NAME, "interpretAndStore: saving emotion analysis");
}
SPH.setEmotion(mContext, new GsonBuilder().disableHtmlEscaping().create().toJson(resultHolder));
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: resultHolder getDescription naked");
}
SPH.setEmotion(mContext, null);
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: result null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: status failure");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: emotions null");
}
}
NotificationHelper.createEmotionAnalysisNotification(mContext);
}
private String getIntro(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_temper_intro);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getStartDesc(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_temper_start_desc);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getConnector(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_temper_connector);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getGap(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_temper_gap);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getConnectorTwo(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_temper_connector_two);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getValenceIntro(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_valence_intro);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getConnectorThree(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_temper_connector_three);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getMoodConnector(@NonNull final SaiyResources sr) {
final String[] stringArray = sr.getStringArray(R.array.array_beyond_verbal_moods_connector);
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getModeOne(final String[] stringArray) {
return stringArray[new Random().nextInt(stringArray.length)];
}
/**
* Helper method to avoid duplication of a modal response
*
* @param stringArray the array of possible responses
* @return a random mode response
*/
private String getModeTwo(@NonNull final String[] stringArray) {
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getModeThree(final String[] stringArray) {
return stringArray[new Random().nextInt(stringArray.length)];
}
private String getValenceLevel(@NonNull final SaiyResources sr, @NonNull final String valenceMode) {
switch (valenceMode) {
case Valence.NEGATIVE:
return sr.getString(R.string.negative);
case Valence.NEUTRAL:
return sr.getString(R.string.neutral);
case Valence.POSITIVE:
return sr.getString(R.string.positive);
default:
return sr.getString(R.string.neutral);
}
}
private String getArousalLevel(@NonNull final SaiyResources sr, @NonNull final String arousalMode) {
switch (arousalMode) {
case Analysis.LOW:
return sr.getString(R.string.low);
case Analysis.MED:
return sr.getString(R.string.medium);
case Analysis.HIGH:
return sr.getString(R.string.high);
default:
return sr.getString(R.string.medium);
}
}
/**
* Method to construct a verbose interpretation of the emotion analysis.
*
* @param result the emotion {@link Result} object
* @return a constructed String that can be announced to the user.
*/
public String constructResponse(@Nullable final Result result) {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse");
}
String response = null;
if (result == null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: result null");
}
return response;
}
final AnalysisSummary analysisSummary = result.getAnalysisSummary();
if (analysisSummary == null) {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: analysisSummary null");
}
return response;
}
final Analysis analysisResult = analysisSummary.getAnalysisResult();
if (analysisResult == null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: analysisResult null");
}
return response;
}
final Temper temper = analysisResult.getTemper();
if (temper == null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: temper null");
}
return response;
}
final String temperMode = temper.getMode();
if (!UtilsString.notNaked(temperMode)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: temperMode null");
}
}
final SaiyResources sr = new SaiyResources(mContext, sl);
String mode;
String modeOne;
String modeTwo;
String modeThree;
String[] stringArray;
final String intro = getIntro(sr);
final String startDesc = getStartDesc(sr);
final String connector = getConnector(sr);
final String gap = getGap(sr);
final String connectorTwo = getConnectorTwo(sr);
final String valenceIntro = getValenceIntro(sr);
final String connectorThree = getConnectorThree(sr);
final String moodConnector = getMoodConnector(sr);
final String arousalIntro = sr.getString(R.string.beyond_verbal_arousal_intro);
String valenceLevel = null;
String arousalLevel = null;
String compositePrimaryPhrase = null;
String compositeSecondaryPhrase = null;
String group7PrimaryPhrase = null;
String group7SecondaryPhrase = null;
String group11PrimaryPhrase = null;
String group11SecondaryPhrase = null;
String group21PrimaryPhrase = null;
String group21SecondaryPhrase = null;
switch (temperMode) {
case Analysis.LOW:
mode = sr.getString(R.string.low);
stringArray = sr.getStringArray(R.array.array_beyond_verbal_synonyms_low_first);
modeOne = getModeOne(stringArray);
modeTwo = getModeTwo(stringArray);
while (modeTwo.matches(modeOne)) {
modeTwo = getModeTwo(stringArray);
}
modeThree = getModeThree(sr.getStringArray(R.array.array_beyond_verbal_synonyms_low_second));
break;
case Analysis.MED:
mode = sr.getString(R.string.medium);
stringArray = sr.getStringArray(R.array.array_beyond_verbal_synonyms_medium_first);
modeOne = getModeOne(stringArray);
modeTwo = getModeTwo(stringArray);
while (modeTwo.matches(modeOne)) {
modeTwo = getModeTwo(stringArray);
}
modeThree = getModeThree(sr.getStringArray(R.array.array_beyond_verbal_synonyms_medium_second));
break;
case Analysis.HIGH:
mode = sr.getString(R.string.high);
stringArray = sr.getStringArray(R.array.array_beyond_verbal_synonyms_high_first);
modeOne = getModeOne(stringArray);
modeTwo = getModeTwo(stringArray);
while (modeTwo.matches(modeOne)) {
modeTwo = getModeTwo(stringArray);
}
modeThree = getModeThree(sr.getStringArray(R.array.array_beyond_verbal_synonyms_high_second));
break;
default:
mode = sr.getString(R.string.low);
stringArray = sr.getStringArray(R.array.array_beyond_verbal_synonyms_low_first);
modeOne = getModeOne(stringArray);
modeTwo = getModeTwo(stringArray);
while (modeTwo.matches(modeOne)) {
modeTwo = getModeTwo(stringArray);
}
modeThree = getModeThree(sr.getStringArray(R.array.array_beyond_verbal_synonyms_low_second));
break;
}
final Valence valence = analysisResult.getValence();
if (valence != null) {
final String valenceMode = valence.getMode();
if (UtilsString.notNaked(valenceMode)) {
valenceLevel = getValenceLevel(sr, valenceMode);
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: valenceMode naked");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: valence null");
}
}
final Arousal arousal = analysisResult.getArousal();
if (arousal != null) {
final String arousalMode = arousal.getMode();
if (UtilsString.notNaked(arousalMode)) {
arousalLevel = getArousalLevel(sr, arousalMode);
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: arousalMode naked");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "constructResponse: arousal null");
}
}
final List segments = result.getSegments();
if (UtilsList.notNaked(segments)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: segments count: " + segments.size());
}
for (final Segment segment : segments) {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: segment: getDuration: " + segment.getDuration());
MyLog.i(CLS_NAME, "constructResponse: segment: getOffset: " + segment.getOffset());
}
final Analysis analysis = segment.getAnalysis();
if (analysis != null) {
final Mood mood = analysis.getMood();
if (mood != null) {
final Composite composite = mood.getComposite();
if (composite != null) {
final Primary compositePrimary = composite.getPrimary();
if (compositePrimary != null) {
compositePrimaryPhrase = compositePrimary.getPhrase();
if (!UtilsString.notNaked(compositePrimaryPhrase)) {
compositePrimaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood compositePrimary null");
}
}
final Secondary compositeSecondary = composite.getSecondary();
if (compositeSecondary != null) {
compositeSecondaryPhrase = compositeSecondary.getPhrase();
if (!UtilsString.notNaked(compositeSecondaryPhrase)) {
compositeSecondaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood compositeSecondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood composite null");
}
}
final Group7 group7 = mood.getGroup7();
if (group7 != null) {
final Primary group7Primary = group7.getPrimary();
if (group7Primary != null) {
group7PrimaryPhrase = group7Primary.getPhrase();
if (!UtilsString.notNaked(group7PrimaryPhrase)) {
group7PrimaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group7Primary null");
}
}
final Secondary group7Secondary = group7.getSecondary();
if (group7Secondary != null) {
group7SecondaryPhrase = group7Secondary.getPhrase();
if (!UtilsString.notNaked(group7SecondaryPhrase)) {
group7SecondaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group7Secondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group7 null");
}
}
final Group11 group11 = mood.getGroup11();
if (group11 != null) {
final Primary group11Primary = group11.getPrimary();
if (group11Primary != null) {
group11PrimaryPhrase = group11Primary.getPhrase();
if (!UtilsString.notNaked(group11PrimaryPhrase)) {
group11PrimaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group11Primary null");
}
}
final Secondary group11Secondary = group11.getSecondary();
if (group11Secondary != null) {
group11SecondaryPhrase = group11Secondary.getPhrase();
if (!UtilsString.notNaked(group11SecondaryPhrase)) {
group11SecondaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group11Secondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group11 null");
}
}
final Group21 group21 = mood.getGroup21();
if (group21 != null) {
final Primary group21Primary = group21.getPrimary();
if (group21Primary != null) {
group21PrimaryPhrase = group21Primary.getPhrase();
if (!UtilsString.notNaked(group21PrimaryPhrase)) {
group21PrimaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group21Primary null");
}
}
final Secondary group21Secondary = group21.getSecondary();
if (group21Secondary != null) {
group21SecondaryPhrase = group21Secondary.getPhrase();
if (!UtilsString.notNaked(group21SecondaryPhrase)) {
group21SecondaryPhrase = "";
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group21Secondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group21 null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood null");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: analysis null");
}
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: segments naked");
}
}
if (DEBUG) {
MyLog.v(CLS_NAME, "constructResponse: intro: " + intro);
MyLog.v(CLS_NAME, "constructResponse: mode: " + mode);
MyLog.v(CLS_NAME, "constructResponse: startDesc: " + startDesc);
MyLog.v(CLS_NAME, "constructResponse: connector: " + connector);
MyLog.v(CLS_NAME, "constructResponse: modeOne: " + modeOne);
MyLog.v(CLS_NAME, "constructResponse: gap: " + gap);
MyLog.v(CLS_NAME, "constructResponse: modeTwo: " + modeTwo);
MyLog.v(CLS_NAME, "constructResponse: connectorTwo: " + connectorTwo);
MyLog.v(CLS_NAME, "constructResponse: modeThree: " + modeThree);
MyLog.v(CLS_NAME, "constructResponse: arousalIntro: " + arousalIntro);
MyLog.v(CLS_NAME, "constructResponse: arousalLevel: " + arousalLevel);
MyLog.v(CLS_NAME, "constructResponse: valenceIntro: " + valenceIntro);
MyLog.v(CLS_NAME, "constructResponse: valenceLevel: " + valenceLevel);
MyLog.v(CLS_NAME, "constructResponse: connectorThree: " + connectorThree);
MyLog.v(CLS_NAME, "constructResponse: compositePrimaryPhrase: " + compositePrimaryPhrase);
MyLog.v(CLS_NAME, "constructResponse: compositeSecondaryPhrase: " + compositeSecondaryPhrase);
MyLog.v(CLS_NAME, "constructResponse: group7PrimaryPhrase: " + group7PrimaryPhrase);
MyLog.v(CLS_NAME, "constructResponse: group7SecondaryPhrase: " + group7SecondaryPhrase);
MyLog.v(CLS_NAME, "constructResponse: group11PrimaryPhrase: " + group11PrimaryPhrase);
MyLog.v(CLS_NAME, "constructResponse: group11SecondaryPhrase: " + group11SecondaryPhrase);
MyLog.v(CLS_NAME, "constructResponse: group21PrimaryPhrase: " + group21PrimaryPhrase);
MyLog.v(CLS_NAME, "constructResponse: group21SecondaryPhrase: " + group21SecondaryPhrase);
}
final StringBuilder builder = new StringBuilder();
builder.append(intro);
builder.append(" ");
builder.append(mode);
builder.append(", ");
builder.append(startDesc);
builder.append(" ");
builder.append(connector);
builder.append(" ");
builder.append(modeOne);
builder.append(", ");
builder.append(gap);
builder.append(" ");
builder.append(modeTwo);
builder.append(", ");
builder.append(connectorTwo);
builder.append(" ");
builder.append(modeThree);
builder.append(". ");
if (UtilsString.notNaked(arousalLevel)) {
builder.append(arousalIntro);
builder.append(", ");
builder.append(arousalLevel);
builder.append(". ");
}
if (UtilsString.notNaked(valenceLevel)) {
builder.append(valenceIntro);
builder.append(", ");
builder.append(valenceLevel);
builder.append(". ");
}
builder.append(connectorThree);
builder.append(", ");
if (UtilsString.notNaked(compositePrimaryPhrase)) {
builder.append(compositePrimaryPhrase);
builder.append(". ");
}
if (UtilsString.notNaked(compositeSecondaryPhrase)) {
builder.append(compositeSecondaryPhrase);
builder.append(". ");
}
if (UtilsString.notNaked(group7PrimaryPhrase)) {
builder.append(group7PrimaryPhrase);
builder.append(". ");
}
if (UtilsString.notNaked(group7SecondaryPhrase)) {
builder.append(group7SecondaryPhrase);
builder.append(". ");
}
builder.append(moodConnector);
builder.append(", ");
if (UtilsString.notNaked(group11PrimaryPhrase)) {
builder.append(group11PrimaryPhrase);
builder.append(". ");
}
if (UtilsString.notNaked(group11SecondaryPhrase)) {
builder.append(group11SecondaryPhrase);
builder.append(". ");
}
if (UtilsString.notNaked(group21PrimaryPhrase)) {
builder.append(group21PrimaryPhrase);
builder.append(". ");
}
if (UtilsString.notNaked(group21SecondaryPhrase)) {
builder.append(group21SecondaryPhrase);
builder.append(". ");
}
response = builder.toString().replaceAll("\\.+", ".").trim();
if (response.endsWith(".")) {
response = UtilsString.replaceLast(StringUtils.removeEnd(response, "."), ".",
sr.getString(R.string._and));
} else {
response = UtilsString.replaceLast(response, ".", sr.getString(R.string._and));
}
if (DEBUG) {
MyLog.i(CLS_NAME, "constructResponse: response: " + response);
}
sr.reset();
return response;
}
/**
* Verbose emotion analysis information for debugging only
*
* @param result the {@link Result} object
*/
private void verboseEmotions(@NonNull final Result result) {
if (DEBUG) {
MyLog.i(CLS_NAME, "verboseEmotions");
MyLog.i(CLS_NAME, "result: getDuration: " + result.getDuration());
MyLog.i(CLS_NAME, "result: getSessionStatus: " + result.getSessionStatus());
}
Temper temper;
Valence valence;
Gender gender;
Arousal arousal;
AudioQuality audioQuality;
final AnalysisSummary analysisSummary = result.getAnalysisSummary();
if (analysisSummary != null) {
final Analysis analysisResult = analysisSummary.getAnalysisResult();
if (analysisResult != null) {
temper = analysisResult.getTemper();
if (temper != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: temper: getGroup: " + temper.getGroup());
MyLog.i(CLS_NAME, "analysisResult: temper: getValue: " + temper.getValue());
MyLog.i(CLS_NAME, "analysisResult: temper: getMode: " + temper.getMode());
MyLog.i(CLS_NAME, "analysisResult: temper: getMean: " + temper.getMean());
MyLog.i(CLS_NAME, "analysisResult: temper: getScore: " + temper.getScore());
}
final Summary temperSummary = temper.getSummary();
if (temperSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: temperSummary: getMode: " + temperSummary.getMode());
MyLog.i(CLS_NAME, "analysisResult: temperSummary: getMean: " + temperSummary.getMean());
MyLog.i(CLS_NAME, "analysisResult: temperSummary: getModePct: " + temperSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: temperSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: temper null");
}
}
valence = analysisResult.getValence();
if (valence != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: valence: getGroup: " + valence.getGroup());
MyLog.i(CLS_NAME, "analysisResult: valence: getValue: " + valence.getValue());
MyLog.i(CLS_NAME, "analysisResult: valence: getMode: " + valence.getMode());
MyLog.i(CLS_NAME, "analysisResult: valence: getMean: " + valence.getMean());
MyLog.i(CLS_NAME, "analysisResult: valence: getScore: " + valence.getScore());
}
final Summary valenceSummary = valence.getSummary();
if (valenceSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: valenceSummary: getMode: " + valenceSummary.getMode());
MyLog.i(CLS_NAME, "analysisResult: valenceSummary: getMean: " + valenceSummary.getMean());
MyLog.i(CLS_NAME, "analysisResult: valenceSummary: getModePct: " + valenceSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: valenceSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: valence null");
}
}
gender = analysisResult.getGender();
if (gender != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: gender: getGroup: " + gender.getGroup());
MyLog.i(CLS_NAME, "analysisResult: gender: getValue: " + gender.getValue());
MyLog.i(CLS_NAME, "analysisResult: gender: getMode: " + gender.getMode());
MyLog.i(CLS_NAME, "analysisResult: gender: getMean: " + gender.getMean());
}
final Summary genderSummary = gender.getSummary();
if (genderSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: genderSummary: getMode: " + genderSummary.getMode());
MyLog.i(CLS_NAME, "analysisResult: genderSummary: getMean: " + genderSummary.getMean());
MyLog.i(CLS_NAME, "analysisResult: genderSummary: getModePct: " + genderSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: genderSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: gender null");
}
}
arousal = analysisResult.getArousal();
if (arousal != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: arousal: getGroup: " + arousal.getGroup());
MyLog.i(CLS_NAME, "analysisResult: arousal: getValue: " + arousal.getValue());
MyLog.i(CLS_NAME, "analysisResult: arousal: getMode: " + arousal.getMode());
MyLog.i(CLS_NAME, "analysisResult: arousal: getMean: " + arousal.getMean());
MyLog.i(CLS_NAME, "analysisResult: arousal: getScore: " + arousal.getScore());
}
final Summary arousalSummary = arousal.getSummary();
if (arousalSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: arousalSummary: getMode: " + arousalSummary.getMode());
MyLog.i(CLS_NAME, "analysisResult: arousalSummary: getMean: " + arousalSummary.getMean());
MyLog.i(CLS_NAME, "analysisResult: arousalSummary: getModePct: " + arousalSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: arousalSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: arousal null");
}
}
audioQuality = analysisResult.getAudioQuality();
if (audioQuality != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: audioQuality: getGroup: " + audioQuality.getGroup());
MyLog.i(CLS_NAME, "analysisResult: audioQuality: getValue: " + audioQuality.getValue());
MyLog.i(CLS_NAME, "analysisResult: audioQuality: getMode: " + audioQuality.getMode());
MyLog.i(CLS_NAME, "analysisResult: audioQuality: getMean: " + audioQuality.getMean());
}
final Summary audioQualitySummary = audioQuality.getSummary();
if (audioQualitySummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysisResult: audioQualitySummary: getMode: " + audioQualitySummary.getMode());
MyLog.i(CLS_NAME, "analysisResult: audioQualitySummary: getMean: " + audioQualitySummary.getMean());
MyLog.i(CLS_NAME, "analysisResult: audioQualitySummary: getModePct: " + audioQualitySummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: audioQualitySummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysisResult: audioQuality null");
}
}
final List segments = result.getSegments();
if (UtilsList.notNaked(segments)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "segments count: " + segments.size());
}
for (final Segment segment : segments) {
if (DEBUG) {
MyLog.i(CLS_NAME, "segment: getDuration: " + segment.getDuration());
MyLog.i(CLS_NAME, "segment: getOffset: " + segment.getOffset());
}
final Analysis analysis = segment.getAnalysis();
if (analysis != null) {
temper = analysis.getTemper();
if (temper != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: temper: getGroup: " + temper.getGroup());
MyLog.i(CLS_NAME, "analysis: temper: getValue: " + temper.getValue());
MyLog.i(CLS_NAME, "analysis: temper: getMode: " + temper.getMode());
MyLog.i(CLS_NAME, "analysis: temper: getMean: " + temper.getMean());
MyLog.i(CLS_NAME, "analysis: temper: getScore: " + temper.getScore());
}
final Summary temperSummary = temper.getSummary();
if (temperSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: temperSummary: getMode: " + temperSummary.getMode());
MyLog.i(CLS_NAME, "analysis: temperSummary: getMean: " + temperSummary.getMean());
MyLog.i(CLS_NAME, "analysis: temperSummary: getModePct: " + temperSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: temperSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: temper null");
}
}
valence = analysis.getValence();
if (valence != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: valence: getGroup: " + valence.getGroup());
MyLog.i(CLS_NAME, "analysis: valence: getValue: " + valence.getValue());
MyLog.i(CLS_NAME, "analysis: valence: getMode: " + valence.getMode());
MyLog.i(CLS_NAME, "analysis: valence: getMean: " + valence.getMean());
MyLog.i(CLS_NAME, "analysis: valence: getScore: " + valence.getScore());
}
final Summary valenceSummary = valence.getSummary();
if (valenceSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: valenceSummary: getMode: " + valenceSummary.getMode());
MyLog.i(CLS_NAME, "analysis: valenceSummary: getMean: " + valenceSummary.getMean());
MyLog.i(CLS_NAME, "analysis: valenceSummary: getModePct: " + valenceSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: valenceSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: valence null");
}
}
gender = analysis.getGender();
if (gender != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: gender: getGroup: " + gender.getGroup());
MyLog.i(CLS_NAME, "analysis: gender: getValue: " + gender.getValue());
MyLog.i(CLS_NAME, "analysis: gender: getMode: " + gender.getMode());
MyLog.i(CLS_NAME, "analysis: gender: getMean: " + gender.getMean());
}
final Summary genderSummary = gender.getSummary();
if (genderSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: genderSummary: getMode: " + genderSummary.getMode());
MyLog.i(CLS_NAME, "analysis: genderSummary: getMean: " + genderSummary.getMean());
MyLog.i(CLS_NAME, "analysis:genderSummary: getModePct: " + genderSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: genderSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: gender null");
}
}
arousal = analysis.getArousal();
if (arousal != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: arousal: getGroup: " + arousal.getGroup());
MyLog.i(CLS_NAME, "analysis: arousal: getValue: " + arousal.getValue());
MyLog.i(CLS_NAME, "analysis: arousal: getMode: " + arousal.getMode());
MyLog.i(CLS_NAME, "analysis: arousal: getMean: " + arousal.getMean());
MyLog.i(CLS_NAME, "analysis: arousal: getScore: " + arousal.getScore());
}
final Summary arousalSummary = arousal.getSummary();
if (arousalSummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: arousalSummary: getMode: " + arousalSummary.getMode());
MyLog.i(CLS_NAME, "analysis: arousalSummary: getMean: " + arousalSummary.getMean());
MyLog.i(CLS_NAME, "analysis: arousalSummary: getModePct: " + arousalSummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: arousalSummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: arousal null");
}
}
audioQuality = analysis.getAudioQuality();
if (audioQuality != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: audioQuality: getGroup: " + audioQuality.getGroup());
MyLog.i(CLS_NAME, "analysis: audioQuality: getValue: " + audioQuality.getValue());
MyLog.i(CLS_NAME, "analysis: audioQuality: getMode: " + audioQuality.getMode());
MyLog.i(CLS_NAME, "analysis: audioQuality: getMean: " + audioQuality.getMean());
}
final Summary audioQualitySummary = audioQuality.getSummary();
if (audioQualitySummary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "analysis: audioQualitySummary: getMode: " + audioQualitySummary.getMode());
MyLog.i(CLS_NAME, "analysis: audioQualitySummary: getMean: " + audioQualitySummary.getMean());
MyLog.i(CLS_NAME, "analysis: audioQualitySummary: getModePct: " + audioQualitySummary.getModePct());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: audioQualitySummary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "analysis: audioQuality null");
}
}
final Mood mood = analysis.getMood();
if (mood != null) {
final Composite composite = mood.getComposite();
if (composite != null) {
final Primary compositePrimary = composite.getPrimary();
if (compositePrimary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "compositePrimary: getPhrase: " + compositePrimary.getPhrase());
MyLog.i(CLS_NAME, "compositePrimary: getId: " + compositePrimary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood compositePrimary null");
}
}
final Secondary compositeSecondary = composite.getSecondary();
if (compositeSecondary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "compositeSecondary: getPhrase: " + compositeSecondary.getPhrase());
MyLog.i(CLS_NAME, "compositeSecondary: getId: " + compositeSecondary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood compositeSecondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood composite null");
}
}
final Group7 group7 = mood.getGroup7();
if (group7 != null) {
final Primary group7Primary = group7.getPrimary();
if (group7Primary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "group7Primary: getPhrase: " + group7Primary.getPhrase());
MyLog.i(CLS_NAME, "group7Primary: getId: " + group7Primary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group7Primary null");
}
}
final Secondary group7Secondary = group7.getSecondary();
if (group7Secondary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "group7Secondary: getPhrase: " + group7Secondary.getPhrase());
MyLog.i(CLS_NAME, "group7Secondary: getId: " + group7Secondary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group7Secondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group7 null");
}
}
final Group11 group11 = mood.getGroup11();
if (group11 != null) {
final Primary group11Primary = group11.getPrimary();
if (group11Primary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "group11Primary: getPhrase: " + group11Primary.getPhrase());
MyLog.i(CLS_NAME, "group11Primary: getId: " + group11Primary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group11Primary null");
}
}
final Secondary group11Secondary = group11.getSecondary();
if (group11Secondary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "group11Secondary: getPhrase: " + group11Secondary.getPhrase());
MyLog.i(CLS_NAME, "group11Secondary: getId: " + group11Secondary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group11Secondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group11 null");
}
}
final Group21 group21 = mood.getGroup21();
if (group21 != null) {
final Primary group21Primary = group21.getPrimary();
if (group21Primary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "group21Primary: getPhrase: " + group21Primary.getPhrase());
MyLog.i(CLS_NAME, "group21Primary: getId: " + group21Primary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group21Primary null");
}
}
final Secondary group21Secondary = group21.getSecondary();
if (group21Secondary != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "group21Secondary: getPhrase: " + group21Secondary.getPhrase());
MyLog.i(CLS_NAME, "group21Secondary: getId: " + group21Secondary.getId());
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group21Secondary null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood group21 null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: mood null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "segment: analysis null");
}
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: segments naked");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: analysisResult null");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "interpretAndStore: analysisSummary null");
}
}
}
/**
* Check if we have an emotion analysis result saved
*
* @param ctx the application context
* @return true if a {@link AnalysisResult} is stored
*/
public static boolean hasEmotion(@NonNull final Context ctx) {
return SPH.getEmotion(ctx) != null;
}
/**
* Get the {@link AnalysisResult} description we have stored
*
* @param ctx the application context
* @return the {@link AnalysisResult} description
*/
public static String getEmotionDescription(@NonNull final Context ctx, @NonNull final SupportedLanguage sl) {
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final AnalysisResult analysisResult;
if (hasEmotion(ctx)) {
try {
analysisResult = gson.fromJson(SPH.getEmotion(ctx), AnalysisResult.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "emotion: " + gson.toJson(analysisResult));
}
return analysisResult.getDescription();
} catch (final JsonSyntaxException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "emotion: JsonSyntaxException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "emotion: NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "emotion: Exception");
e.printStackTrace();
}
}
}
return PersonalityResponse.getBeyondVerbalErrorResponse(ctx, sl);
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/BeyondVerbal.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import ai.saiy.android.audio.RecognitionMic;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.audio.AudioConfig;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers.BVCredentials;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers.StartRequestBody;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers.StartResponse;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.http.BVStartRequest;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.http.BVStreamAudio;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.language.SupportedLanguageBV;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.user.MetaData;
import ai.saiy.android.configuration.BeyondVerbalConfiguration;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.personality.PersonalityResponse;
import ai.saiy.android.recognition.Recognition;
import ai.saiy.android.service.helper.LocalRequest;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 13/08/2016.
*/
public class BeyondVerbal {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = BeyondVerbal.class.getSimpleName();
public static final long FETCH_ANALYSIS_DELAY = 6000;
public static final long MINIMUM_AUDIO_TIME = 13000;
private final SupportedLanguage sl;
private final RecognitionMic mic;
private final Context mContext;
/**
* Constructor
*
* @param mContext the application context
*/
public BeyondVerbal(@NonNull final Context mContext, @NonNull final RecognitionMic mic,
@NonNull final SupportedLanguage sl) {
this.mContext = mContext;
this.mic = mic;
this.sl = sl;
}
public void stream() {
if (DEBUG) {
MyLog.i(CLS_NAME, "stream");
}
final String token = getToken();
if (UtilsString.notNaked(token)) {
final Pair startRequest = new BVStartRequest(mContext, token)
.getId(new StartRequestBody(AudioConfig.getDefault(), MetaData.getEmpty(),
SupportedLanguageBV.getSupportedLanguage(sl.getLocale())).prepare());
if (startRequest.first) {
if (startRequest.second.isSuccessful()) {
final String recordingId = startRequest.second.getRecordingId();
if (mic.isAvailable()) {
new BVStreamAudio(mic, sl, token, recordingId).stream();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "mic unavailable");
}
Recognition.setState(Recognition.State.IDLE);
onError();
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "startRequest.second.isSuccessful()");
}
onError();
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "startRequest.first");
}
onError();
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "token naked");
}
onError();
}
}
private void onError() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onError");
}
Recognition.setState(Recognition.State.IDLE);
final LocalRequest localRequest = new LocalRequest(mContext);
localRequest.setSupportedLanguage(sl);
localRequest.setAction(LocalRequest.ACTION_SPEAK_ONLY);
localRequest.setUtterance(PersonalityResponse.getBeyondVerbalServerErrorResponse(mContext, sl));
localRequest.setTTSLocale(SPH.getTTSLocale(mContext));
localRequest.setVRLocale(SPH.getVRLocale(mContext));
localRequest.execute();
}
private String getToken() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getID");
}
final Pair tokenPair = BVCredentials.refreshTokenIfRequired(mContext,
BeyondVerbalConfiguration.API_KEY);
if (tokenPair.first) {
return tokenPair.second.getAccessToken();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "failed to get token");
}
}
return null;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Analysis.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Analysis {
public static final String LOW = "low";
public static final String MED = "med";
public static final String HIGH = "high";
@SerializedName("Temper")
private final Temper temper;
@SerializedName("Valence")
private final Valence valence;
@SerializedName("Gender")
private final Gender gender;
@SerializedName("Mood")
private final Mood mood;
@SerializedName("Arousal")
private final Arousal arousal;
@SerializedName("AudioQuality")
private final AudioQuality audioQuality;
public Analysis(final Gender gender, final Temper temper, final Valence valence, final Mood mood,
final AudioQuality audioQuality, final Arousal arousal) {
this.gender = gender;
this.temper = temper;
this.valence = valence;
this.mood = mood;
this.audioQuality = audioQuality;
this.arousal = arousal;
}
public Arousal getArousal() {
return arousal;
}
public AudioQuality getAudioQuality() {
return audioQuality;
}
public Gender getGender() {
return gender;
}
public Mood getMood() {
return mood;
}
public Temper getTemper() {
return temper;
}
public Valence getValence() {
return valence;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/AnalysisSummary.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Created by benrandall76@gmail.com on 14/08/2016.
*/
public class AnalysisSummary {
@SerializedName("AnalysisResult")
private final Analysis analysisResult;
public AnalysisSummary(final Analysis analysisResult) {
this.analysisResult = analysisResult;
}
public Analysis getAnalysisResult() {
return analysisResult;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Arousal.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Arousal {
@SerializedName("Mode")
private final String mode;
@SerializedName("Mean")
private final double mean;
@SerializedName("Value")
private final double value;
@SerializedName("Score")
private final double score;
@SerializedName("Group")
private final String group;
@SerializedName("Summary")
private final Summary summary;
public Arousal(final String group, final double value, final Summary summary, final double mean, final String mode,
final double score) {
this.group = group;
this.value = value;
this.summary = summary;
this.mean = mean;
this.mode = mode;
this.score = score;
}
public double getScore() {
return score;
}
public double getMean() {
return mean;
}
public String getMode() {
return mode;
}
public String getGroup() {
return group;
}
public Summary getSummary() {
return summary;
}
public double getValue() {
return value;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/AudioQuality.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class AudioQuality {
public static final String BAD = "bad";
@SerializedName("Mode")
private final String mode;
@SerializedName("Mean")
private final double mean;
@SerializedName("Value")
private final double value;
@SerializedName("Group")
private final String group;
@SerializedName("Summary")
private final Summary summary;
public AudioQuality(final String group, final double value, final double mean, final String mode, final Summary summary) {
this.group = group;
this.value = value;
this.mean = mean;
this.mode = mode;
this.summary = summary;
}
public Summary getSummary() {
return summary;
}
public double getValue() {
return value;
}
public double getMean() {
return mean;
}
public String getMode() {
return mode;
}
public String getGroup() {
return group;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Composite.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Composite {
@SerializedName("Primary")
private final Primary primary;
@SerializedName("Secondary")
private final Secondary secondary;
public Composite(final Primary primary, final Secondary secondary) {
this.primary = primary;
this.secondary = secondary;
}
public Primary getPrimary() {
return primary;
}
public Secondary getSecondary() {
return secondary;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Emotions.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import android.support.annotation.NonNull;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 09/06/2016.
*/
public class Emotions {
public static final String SUCCESS = "success";
@SerializedName("result")
private final Result result;
@SerializedName("status")
private final String status;
@SerializedName("recordingId")
private String recordingId;
public Emotions(final String recordingId, final Result result, final String status) {
this.recordingId = recordingId;
this.result = result;
this.status = status;
}
public void setRecordingId(@NonNull final String recordingId) {
this.recordingId = recordingId;
}
public String getRecordingId() {
return recordingId;
}
public Result getResult() {
return result;
}
public String getStatus() {
return status;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Gender.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Gender {
public static final String MALE = "male";
public static final String FEMALE = "female";
@SerializedName("Mode")
private final String mode;
@SerializedName("Mean")
private final double mean;
@SerializedName("Value")
private final double value;
@SerializedName("Group")
private final String group;
@SerializedName("Summary")
private final Summary summary;
public Gender(final String group, final double value, final Summary summary, final double mean, final String mode) {
this.group = group;
this.value = value;
this.summary = summary;
this.mean = mean;
this.mode = mode;
}
public double getMean() {
return mean;
}
public String getMode() {
return mode;
}
public String getGroup() {
return group;
}
public Summary getSummary() {
return summary;
}
public double getValue() {
return value;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Group11.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Group11 {
@SerializedName("Primary")
private final Primary primary;
@SerializedName("Secondary")
private final Secondary secondary;
public Group11(final Primary primary, final Secondary secondary) {
this.primary = primary;
this.secondary = secondary;
}
public Primary getPrimary() {
return primary;
}
public Secondary getSecondary() {
return secondary;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Group21.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Group21 {
@SerializedName("Primary")
private final Primary primary;
@SerializedName("Secondary")
private final Secondary secondary;
public Group21(final Primary primary, final Secondary secondary) {
this.primary = primary;
this.secondary = secondary;
}
public Primary getPrimary() {
return primary;
}
public Secondary getSecondary() {
return secondary;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Group7.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Group7 {
@SerializedName("Primary")
private final Primary primary;
@SerializedName("Secondary")
private final Secondary secondary;
public Group7(final Primary primary, final Secondary secondary) {
this.primary = primary;
this.secondary = secondary;
}
public Primary getPrimary() {
return primary;
}
public Secondary getSecondary() {
return secondary;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Mood.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Mood {
@SerializedName("Group11")
private final Group11 group11;
@SerializedName("Group7")
private final Group7 group7;
@SerializedName("Group21")
private final Group21 group21;
@SerializedName("Composite")
private final Composite composite;
public Mood(final Composite composite, final Group11 group11, final Group7 group7, final Group21 group21) {
this.composite = composite;
this.group11 = group11;
this.group7 = group7;
this.group21 = group21;
}
public Composite getComposite() {
return composite;
}
public Group11 getGroup11() {
return group11;
}
public Group21 getGroup21() {
return group21;
}
public Group7 getGroup7() {
return group7;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Primary.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Primary {
@SerializedName("Id")
private final int id;
@SerializedName("Phrase")
private final String phrase;
public Primary(final int id, final String phrase) {
this.id = id;
this.phrase = phrase;
}
public int getId() {
return id;
}
public String getPhrase() {
return phrase;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Result.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
import java.util.List;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Result {
@SerializedName("analysisSegments")
private final List segments;
@SerializedName("analysisSummary")
private final AnalysisSummary analysisSummary;
@SerializedName("duration")
private final double duration;
@SerializedName("sessionStatus")
private final String sessionStatus;
public Result(final double duration, final List segments, final String sessionStatus,
final AnalysisSummary analysisSummary) {
this.duration = duration;
this.segments = segments;
this.sessionStatus = sessionStatus;
this.analysisSummary = analysisSummary;
}
public AnalysisSummary getAnalysisSummary() {
return analysisSummary;
}
public double getDuration() {
return duration;
}
public List getSegments() {
return segments;
}
public String getSessionStatus() {
return sessionStatus;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Secondary.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Secondary {
@SerializedName("Id")
private final int id;
@SerializedName("Phrase")
private final String phrase;
public Secondary(final int id, final String phrase) {
this.id = id;
this.phrase = phrase;
}
public int getId() {
return id;
}
public String getPhrase() {
return phrase;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Segment.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Segment {
@SerializedName("duration")
private final double duration;
@SerializedName("offset")
private final double offset;
@SerializedName("analysis")
private final Analysis analysis;
public Segment(final Analysis analysis, final double duration, final double offset) {
this.analysis = analysis;
this.duration = duration;
this.offset = offset;
}
public Analysis getAnalysis() {
return analysis;
}
public double getDuration() {
return duration;
}
public double getOffset() {
return offset;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Summary.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Summary {
@SerializedName("Mode")
private final String mode;
@SerializedName("ModePct")
private final int modePct;
@SerializedName("Mean")
private final double mean;
public Summary(final double mean, final String mode, final int modePct) {
this.mean = mean;
this.mode = mode;
this.modePct = modePct;
}
public double getMean() {
return mean;
}
public String getMode() {
return mode;
}
public int getModePct() {
return modePct;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Temper.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Temper {
@SerializedName("Mode")
private final String mode;
@SerializedName("Mean")
private final double mean;
@SerializedName("Value")
private final double value;
@SerializedName("Score")
private final double score;
@SerializedName("Group")
private final String group;
@SerializedName("Summary")
private final Summary summary;
public Temper(final String group, final double value, final Summary summary, final double mean, final String mode,
final double score) {
this.group = group;
this.value = value;
this.summary = summary;
this.mean = mean;
this.mode = mode;
this.score = score;
}
public double getScore() {
return score;
}
public double getMean() {
return mean;
}
public String getMode() {
return mode;
}
public String getGroup() {
return group;
}
public Summary getSummary() {
return summary;
}
public double getValue() {
return value;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/analysis/Valence.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis;
import com.google.gson.annotations.SerializedName;
/**
* Helper class to serialise the JSON response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class Valence {
public static final String POSITIVE = "positive";
public static final String NEUTRAL = "neutral";
public static final String NEGATIVE = "negative";
@SerializedName("Mode")
private final String mode;
@SerializedName("Mean")
private final double mean;
@SerializedName("Value")
private final double value;
@SerializedName("Score")
private final double score;
@SerializedName("Group")
private final String group;
@SerializedName("Summary")
private final Summary summary;
public Valence(final String group, final double value, final Summary summary, final double mean, final String mode,
final double score) {
this.group = group;
this.value = value;
this.summary = summary;
this.mean = mean;
this.mode = mode;
this.score = score;
}
public double getScore() {
return score;
}
public double getMean() {
return mean;
}
public String getMode() {
return mode;
}
public String getGroup() {
return group;
}
public Summary getSummary() {
return summary;
}
public double getValue() {
return value;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/audio/AudioConfig.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.audio;
import android.support.annotation.NonNull;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Class to set the audio configuration details, that will be sent with API requests.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class AudioConfig {
private static final String TYPE = "type";
private static final String CHANNELS = "channels";
private static final String SAMPLE_RATE = "sample_rate";
private static final String BITS_PER_SAMPLE = "bits_per_sample";
private static final String AUTO_DETECT = "auto_detect";
private AudioType type;
private int channels;
private int sampleRate;
private int bitsPerSample;
private boolean autoDetect;
/**
* Default constructor
*/
public AudioConfig() {
}
/**
* Constructor
*
* @param type the audio type, one of {@link AudioType#PCM} or {@link AudioType#WAV}
* @param bitsPerSample the bits per sample
* @param channels number of channels
* @param sampleRate the sampling rate
* @param autoDetect ???
*/
public AudioConfig(@NonNull final AudioType type, final int bitsPerSample,
final int channels, final int sampleRate, final boolean autoDetect) {
this.autoDetect = autoDetect;
this.bitsPerSample = bitsPerSample;
this.channels = channels;
this.sampleRate = sampleRate;
this.type = type;
}
public static AudioConfig getDefault() {
return new AudioConfig(AudioType.PCM, 16, 1, 8000, true);
}
/**
* Method to prepare the configuration details in a JSON format that the API will accept.
*
* @return a JSON formatted representation of the audio configuration
*/
public JSONObject getConfigJson() {
final JSONObject object = new JSONObject();
try {
object.put(TYPE, getType());
object.put(CHANNELS, getChannels());
object.put(SAMPLE_RATE, getSampleRate());
object.put(BITS_PER_SAMPLE, getBitsPerSample());
object.put(AUTO_DETECT, isAutoDetect());
} catch (JSONException e) {
e.printStackTrace();
}
return object;
}
public int getBitsPerSample() {
return bitsPerSample;
}
public int getSampleRate() {
return sampleRate;
}
public boolean isAutoDetect() {
return autoDetect;
}
public void setAutoDetect(final boolean autoDetect) {
this.autoDetect = autoDetect;
}
public void setBitsPerSample(final int bitsPerSample) {
this.bitsPerSample = bitsPerSample;
}
public int getChannels() {
return channels;
}
public void setChannels(final int channels) {
this.channels = channels;
}
public void setSampleRate(final int sampleRate) {
this.sampleRate = sampleRate;
}
public AudioType getType() {
return type;
}
public void setType(@NonNull final AudioType type) {
this.type = type;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/audio/AudioType.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.audio;
/**
* Class to ensure that a supported audio file type is chosen prior to being sent to the API.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public enum AudioType {
WAV("wav"),
PCM("pcm");
private final String type;
/**
* Enum Constructor
*
* @param type the audio file type in a format supported by the API
*/
AudioType(final String type) {
this.type = type;
}
public String getType() {
return type;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/containers/BVCredentials.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Pair;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.http.BVAuthRequest;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
/**
* Class to serialise the credential information from Beyond Verbal
*
* Created by benrandall76@gmail.com on 09/06/2016.
*/
public class BVCredentials {
private transient static final boolean DEBUG = MyLog.DEBUG;
private transient static final String CLS_NAME = BVCredentials.class.getSimpleName();
@SerializedName("access_token")
private final String accessToken;
@SerializedName("token_type")
private final String tokenType;
@SerializedName("expires_in")
private final long expiresIn;
@SerializedName("expiry_time")
private long expiryTime;
public BVCredentials(final String accessToken, final String tokenType, final long expiresIn,
final long expiryTime) {
this.accessToken = accessToken;
this.tokenType = tokenType;
this.expiresIn = expiresIn;
this.expiryTime = expiryTime;
}
/**
* Method to check if the token is valid
*
* @param bvCredentials the {@link BVCredentials} containing the most recent token credentials.
* @return true if the token is valid, false otherwise.
*/
public static boolean isTokenValid(@Nullable final BVCredentials bvCredentials) {
return bvCredentials != null && System.currentTimeMillis() < bvCredentials.getExpiryTime();
}
/**
* Convenience method to check if the most recent access token remains valid and automatically
* request one if required.
*
* @param ctx the application context
* @param apiKey the API key
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link BVCredentials} object, containing the token credentials. If the request was unsuccessful,
* the second parameter may be null.
*/
public static Pair refreshTokenIfRequired(@NonNull final Context ctx,
@NonNull final String apiKey) {
final BVCredentials bvCredentials = getLastToken(ctx);
if (isTokenValid(bvCredentials)) {
return new Pair<>(true, bvCredentials);
} else {
return new BVAuthRequest(ctx.getApplicationContext(), apiKey).getToken();
}
}
/**
* Method to get the most recent access token stored in the user {@link SPH )
* shared preferences.
*
* @param ctx the application context
* @return the most recent {@link BVCredentials} object
*/
private static BVCredentials getLastToken(@NonNull final Context ctx) {
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final BVCredentials bvCredentials;
try {
bvCredentials = gson.fromJson(SPH.getBeyondVerbalCredentials(ctx), BVCredentials.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "getLastToken: " + gson.toJson(bvCredentials));
}
return bvCredentials;
} catch (final JsonSyntaxException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getLastToken: JsonSyntaxException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getLastToken: NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getLastToken: Exception");
e.printStackTrace();
}
}
return null;
}
public String getAccessToken() {
return accessToken;
}
public long getExpiresIn() {
return expiresIn;
}
public String getTokenType() {
return tokenType;
}
public long getExpiryTime() {
return expiryTime;
}
public void setExpiryTime(final long expiryTime) {
this.expiryTime = (System.currentTimeMillis() + (expiryTime * 1000));
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/containers/StartRequestBody.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.audio.AudioConfig;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.language.SupportedLanguageBV;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.user.MetaData;
import ai.saiy.android.utils.MyLog;
/**
* Utility class to format the request body parameters into a format the API will accept.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class StartRequestBody {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = StartRequestBody.class.getSimpleName();
private static final String DATA_FORMAT = "dataFormat";
private static final String META_DATA = "metadata";
private static final String DISPLAY_LANGUAGE = "displayLang";
private final AudioConfig config;
private final MetaData metaData;
private final SupportedLanguageBV sl;
/**
* Constructor
*
* @param config the {@link AudioConfig}
* @param metaData the {@link MetaData}. Optional, so can be null
* @param sl the {@link SupportedLanguageBV}. Optional, so can be null
*/
public StartRequestBody(@NonNull final AudioConfig config, @Nullable final MetaData metaData,
@Nullable final SupportedLanguageBV sl) {
this.config = config;
this.metaData = metaData;
this.sl = sl;
}
/**
* Method to format the body parameters into an accepted JSON format
*
* @return an {@link JSONObject} containing the body parameters
*/
public JSONObject prepare() {
if (DEBUG) {
MyLog.i(CLS_NAME, "prepare");
}
final JSONObject object = new JSONObject();
try {
object.put(DATA_FORMAT, config.getConfigJson());
if (metaData != null) {
object.put(META_DATA, metaData.getMetaJSON());
}
if (sl != null) {
object.put(DISPLAY_LANGUAGE, sl.getServerFormat());
}
} catch (final JSONException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "prepare JSONException");
e.printStackTrace();
}
}
return object;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/containers/StartResponse.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers;
import com.google.gson.annotations.SerializedName;
/**
* Class to serialise the response from Beyond Verbal
*
* Created by benrandall76@gmail.com on 09/06/2016.
*/
public class StartResponse {
private static final String SUCCESS = "success";
private static final String FAILURE = "failure";
@SerializedName("status")
private final String status;
@SerializedName("recordingId")
private final String recordingId;
@SerializedName("reason")
private final String reason;
public StartResponse(final String reason, final String status, final String recordingId) {
this.reason = reason;
this.status = status;
this.recordingId = recordingId;
}
public boolean isSuccessful() {
return getStatus().matches(SUCCESS);
}
public String getReason() {
return reason;
}
public String getRecordingId() {
return recordingId;
}
public String getStatus() {
return status;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/http/BVAuthRequest.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.http;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers.BVCredentials;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
/**
* Class to get an initial access token, which will be valid for a short period of time. This
* request must be made before emotion analysis or any other request.
*
* Rather than using this class directly, it is better to
* call {@link BVCredentials#refreshTokenIfRequired(Context, String)} which will invoke this class if
* the current access token has expired.
*
* The token request is always synchronous, as it is an 'on-demand' requirement.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class BVAuthRequest {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = BVAuthRequest.class.getSimpleName();
private static final String AUTH_URL = "https://token.beyondverbal.com/token";
private static final String GRANT_TYPE = "grant_type";
private static final String CLIENT_CREDENTIALS = "client_credentials";
private static final String API_KEY = "apiKey";
private static final String CONTENT_TYPE = "Content-Type";
private static final String HEADER_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";
private static final String ENCODING = "UTF-8";
private static final String CHARSET = "Accept-Charset";
private static final long THREAD_TIMEOUT = 7L;
private final Context mContext;
private final String apiKey;
/**
* Constructor
*
* @param mContext the application context
* @param apiKey the api key
*/
public BVAuthRequest(@NonNull final Context mContext, @NonNull final String apiKey) {
this.apiKey = apiKey;
this.mContext = mContext.getApplicationContext();
}
/**
* Method to get a temporary access token.
*
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link BVCredentials} object, containing the token credentials. If the request was unsuccessful,
* the second parameter may be null.
*/
public Pair getToken() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getID");
}
final RequestFuture future = RequestFuture.newFuture();
final RequestQueue queue = Volley.newRequestQueue(mContext);
queue.start();
final StringRequest request = new StringRequest(Request.Method.POST, AUTH_URL, future,
new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
BVAuthRequest.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(CONTENT_TYPE, HEADER_CONTENT_TYPE);
return params;
}
@Override
protected Map getParams() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(API_KEY, apiKey);
params.put(GRANT_TYPE, CLIENT_CREDENTIALS);
return params;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
String response = null;
try {
response = future.get(THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getID: InterruptedException");
e.printStackTrace();
}
} catch (final ExecutionException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getID: ExecutionException");
e.printStackTrace();
}
} catch (final TimeoutException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getID: TimeoutException");
e.printStackTrace();
}
} finally {
queue.stop();
}
if (response != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "response: " + response);
}
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final BVCredentials authResponse = gson.fromJson(response, BVCredentials.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: getAccessToken: " + authResponse.getAccessToken());
MyLog.i(CLS_NAME, "onResponse: getTokenType: " + authResponse.getTokenType());
MyLog.i(CLS_NAME, "onResponse: getExpiresIn: " + authResponse.getExpiresIn());
}
authResponse.setExpiryTime(authResponse.getExpiresIn());
SPH.setBeyondVerbalCredentials(mContext, gson.toJson(authResponse));
return new Pair<>(true, authResponse);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return new Pair<>(false, null);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/http/BVEmotionAnalysis.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.http;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.volley.AuthFailureError;
import com.android.volley.Cache;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Network;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.StringRequest;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.nuance.dragon.toolkit.oem.api.json.JSONObject;
import org.json.JSONException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.AnalysisResultHelper;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.analysis.Emotions;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers.BVCredentials;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsVolley;
/**
* Class to get an initial access token, which will be valid for a short period of time. This
* request must be made before emotion analysis or any other request.
*
* Rather than using this class directly, it is better to
* call {@link BVCredentials#refreshTokenIfRequired(Context, String)} which will invoke this class if
* the current access token has expired.
*
* The token request is always synchronous, as it is an 'on-demand' requirement.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class BVEmotionAnalysis {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = BVEmotionAnalysis.class.getSimpleName();
private static final String ANALYSIS_URL = "https://apiv4.beyondverbal.com/v4/recording/";
private static final String FROM_MS = "/analysis?fromMs=";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER_ = "Bearer ";
private static final String ENCODING = "UTF-8";
private static final String CHARSET = "Accept-Charset";
private static final long THREAD_TIMEOUT = 7L;
private final Context mContext;
private final SupportedLanguage sl;
private final String token;
/**
* Constructor
*
* @param mContext the application context
* @param sl the {@link SupportedLanguage} object
* @param token the access token
*/
public BVEmotionAnalysis(@NonNull final Context mContext, @NonNull final SupportedLanguage sl,
@NonNull final String token) {
this.token = token;
this.mContext = mContext;
this.sl = sl;
}
/**
* Method to get a temporary access token.
*
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link BVCredentials} object, containing the token credentials. If the request was unsuccessful,
* the second parameter may be null.
*/
public Pair getAnalysis(@NonNull final String recordingId, final int offset) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getAnalysis");
}
final RequestFuture future = RequestFuture.newFuture();
final Cache cache = UtilsVolley.getCache(mContext);
final Network network = new BasicNetwork(new HurlStack());
final RequestQueue queue = new RequestQueue(cache, network);
queue.start();
final String url = ANALYSIS_URL + recordingId + FROM_MS + String.valueOf(offset);
final StringRequest request = new StringRequest(Request.Method.GET, url, future,
new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
BVEmotionAnalysis.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(AUTHORIZATION, BEARER_ + token);
return params;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
String response = null;
try {
response = future.get(THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "execute: InterruptedException");
e.printStackTrace();
}
} catch (final ExecutionException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "execute: ExecutionException");
e.printStackTrace();
}
} catch (final TimeoutException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "execute: TimeoutException");
e.printStackTrace();
}
} finally {
queue.stop();
}
if (response != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: " + response);
try {
final JSONObject object = new JSONObject(response);
MyLog.i(CLS_NAME, "object: " + object.toString(4));
} catch (final JSONException e) {
e.printStackTrace();
}
}
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final Emotions emotions = gson.fromJson(response, Emotions.class);
emotions.setRecordingId(recordingId);
new AnalysisResultHelper(mContext, sl).interpretAndStore(emotions);
return new Pair<>(true, emotions);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "onResponse: failed");
}
return new Pair<>(false, null);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/http/BVSendFile.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.http;
import android.net.ParseException;
import android.support.annotation.NonNull;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.net.UnknownHostException;
import javax.net.ssl.HttpsURLConnection;
import ai.saiy.android.utils.Constants;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsString;
/**
* Class to send a file of audio data to the BV API
*
* Created by benrandall76@gmail.com on 09/06/2016.
*/
public class BVSendFile {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = BVSendFile.class.getSimpleName();
private static final String RECORDING_URL = "https://apiv4.beyondverbal.com/v4/recording/";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER_ = "Bearer ";
private static final String CONTENT_TYPE = "Content-Type";
private static final String HEADER_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";
private HttpsURLConnection urlConnection;
private OutputStream outputStream;
private FileInputStream fileInputStream;
private final String token;
private final String recordingId;
private final File file;
public BVSendFile(@NonNull final String token, @NonNull final String recordingId,
@NonNull final File file) {
this.token = token;
this.recordingId = recordingId;
this.file = file;
}
public void stream() {
if (DEBUG) {
MyLog.i(CLS_NAME, "stream");
}
try {
urlConnection = (HttpsURLConnection) new URL(RECORDING_URL + recordingId).openConnection();
urlConnection.setRequestMethod(Constants.HTTP_POST);
urlConnection.setRequestProperty(CONTENT_TYPE, HEADER_CONTENT_TYPE);
urlConnection.setRequestProperty(AUTHORIZATION, BEARER_ + token);
urlConnection.setUseCaches(false);
urlConnection.setDoOutput(true);
urlConnection.connect();
outputStream = urlConnection.getOutputStream();
fileInputStream = new FileInputStream(file);
final byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1) {
if (DEBUG) {
MyLog.v(CLS_NAME, "writing bytes: " + len);
}
outputStream.write(buffer, 0, len);
}
if (DEBUG) {
MyLog.i(CLS_NAME, "Requesting Response code");
}
final int responseCode = urlConnection.getResponseCode();
if (DEBUG) {
MyLog.i(CLS_NAME, "responseCode: " + responseCode);
}
if (responseCode != HttpsURLConnection.HTTP_OK) {
if (DEBUG) {
MyLog.e(CLS_NAME, "ErrorStream: "
+ UtilsString.streamToString(urlConnection.getErrorStream()));
}
}
} catch (final ParseException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "ParseException");
e.printStackTrace();
}
} catch (final UnknownHostException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnknownHostException");
e.printStackTrace();
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "IOException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
} finally {
closeConnection();
}
}
private void closeConnection() {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
if (urlConnection != null) {
try {
urlConnection.disconnect();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/http/BVStartRequest.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.http;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.containers.StartResponse;
import ai.saiy.android.utils.MyLog;
/**
* Class to get a recording identifier that will be used for emotion analysis request. This identifier
* should be stored for future use, as it can be used to retrieve meta-data regarding the recording
* in the future.
*
* This is a synchronous request, as the identifier will be required immediately.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class BVStartRequest {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = BVStartRequest.class.getSimpleName();
private static final String START_URL = "https://apiv4.beyondverbal.com/v4/recording/start";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER_ = "Bearer ";
private static final String ENCODING = "UTF-8";
private static final String CHARSET = "Accept-Charset";
private static final long THREAD_TIMEOUT = 7L;
private final Context mContext;
private final String token;
/**
* Constructor
*
* @param mContext the application context
* @param token the access token
*/
public BVStartRequest(@NonNull final Context mContext, @NonNull final String token) {
this.token = token;
this.mContext = mContext.getApplicationContext();
}
/**
* Method to get a recording identifier.
*
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link StartResponse} object, containing the recording id.
*
* If the request was unsuccessful, the second parameter may be null.
*/
public Pair getId(@NonNull final JSONObject body) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getId");
}
final RequestFuture future = RequestFuture.newFuture();
final RequestQueue queue = Volley.newRequestQueue(mContext);
queue.start();
final JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.POST, START_URL,
body, future, new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
BVStartRequest.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(AUTHORIZATION, BEARER_ + token);
return params;
}
};
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(jsonObjReq);
JSONObject response = null;
try {
response = future.get(THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "execute: InterruptedException");
e.printStackTrace();
}
} catch (ExecutionException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "execute: ExecutionException");
e.printStackTrace();
}
} catch (TimeoutException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "execute: TimeoutException");
e.printStackTrace();
}
} finally {
queue.stop();
}
if (response != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "response: " + response);
}
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final StartResponse startResponse = gson.fromJson(response.toString(), StartResponse.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: getStatus: " + startResponse.getStatus());
}
if (startResponse.isSuccessful()) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: getRecordingId: " + startResponse.getRecordingId());
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: getReason: " + startResponse.getReason());
}
}
return new Pair<>(true, startResponse);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return new Pair<>(false, null);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/http/BVStreamAudio.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.http;
import android.net.ParseException;
import android.os.Process;
import android.speech.SpeechRecognizer;
import android.support.annotation.NonNull;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.HttpsURLConnection;
import ai.saiy.android.R;
import ai.saiy.android.audio.IMic;
import ai.saiy.android.audio.RecognitionMic;
import ai.saiy.android.cognitive.emotion.provider.beyondverbal.BeyondVerbal;
import ai.saiy.android.cognitive.identity.provider.microsoft.Speaker;
import ai.saiy.android.localisation.SaiyResourcesHelper;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.personality.PersonalityResponse;
import ai.saiy.android.recognition.Recognition;
import ai.saiy.android.service.helper.LocalRequest;
import ai.saiy.android.utils.Constants;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
import ai.saiy.android.utils.UtilsString;
/**
* Class to stream raw audio to the BV API
*
* Created by benrandall76@gmail.com on 10/06/2016.
*/
public class BVStreamAudio implements IMic {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = BVStreamAudio.class.getSimpleName();
private static final String RECORDING_URL = "https://apiv4.beyondverbal.com/v4/recording/";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER_ = "Bearer ";
private static final String CONTENT_TYPE = "Content-Type";
private static final String HEADER_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";
private static final String TRANSFER_ENCODING = "Transfer-Encoding";
private static final String CHUNKED = "chunked";
private static final String CONTENT_TYPE_AUDIO_PARAMS = "audio/l16; rate=8000";
private volatile HttpsURLConnection urlConnection;
private volatile OutputStream outputStream;
private volatile int retryCount;
private final String token;
private final String recordingId;
private final RecognitionMic mic;
private final SupportedLanguage sl;
/**
* Constructor
*
* @param mic the initialised {@link RecognitionMic} object
* @param sl the {@link SupportedLanguage} object
* @param token the Beyond Verbal access token
* @param recordingId the Beyond Verbal recording id.
*/
public BVStreamAudio(@NonNull final RecognitionMic mic, @NonNull final SupportedLanguage sl,
@NonNull final String token,
@NonNull final String recordingId) {
this.mic = mic;
this.token = token;
this.recordingId = recordingId;
this.sl = sl;
this.mic.setMicListener(this);
}
/**
* Start streaming the audio to the BV servers
*/
public void stream() {
if (DEBUG) {
MyLog.i(CLS_NAME, "stream");
}
mic.startRecording();
final Thread httpThread = new Thread() {
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);
try {
urlConnection = (HttpsURLConnection) new URL(RECORDING_URL + recordingId).openConnection();
urlConnection.setAllowUserInteraction(false);
urlConnection.setInstanceFollowRedirects(true);
urlConnection.setRequestMethod(Constants.HTTP_POST);
urlConnection.setRequestProperty(CONTENT_TYPE, HEADER_CONTENT_TYPE);
urlConnection.setRequestProperty(AUTHORIZATION, BEARER_ + token);
urlConnection.setUseCaches(false);
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty(TRANSFER_ENCODING, CHUNKED);
urlConnection.setChunkedStreamingMode(0);
urlConnection.setRequestProperty(CONTENT_TYPE, CONTENT_TYPE_AUDIO_PARAMS);
urlConnection.connect();
outputStream = urlConnection.getOutputStream();
synchronized (mic.getLock()) {
while (mic.isRecording()) {
try {
if (DEBUG) {
MyLog.i(CLS_NAME, "mic lock waiting");
}
mic.getLock().wait();
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "InterruptedException");
e.printStackTrace();
}
break;
}
}
}
if (DEBUG) {
MyLog.i(CLS_NAME, "requesting response");
}
final int responseCode = urlConnection.getResponseCode();
if (DEBUG) {
MyLog.d(CLS_NAME, "responseCode: " + responseCode);
}
if (responseCode != HttpsURLConnection.HTTP_OK) {
if (DEBUG) {
MyLog.e(CLS_NAME, "audioThread ErrorStream: "
+ UtilsString.streamToString(urlConnection.getErrorStream()));
}
onError(SpeechRecognizer.ERROR_NETWORK);
} else {
if (DEBUG) {
MyLog.d(CLS_NAME, "response: HTTP_OK. Setting timer");
}
if (mic.isInterrupted()) {
onError(Speaker.ERROR_USER_CANCELLED);
} else {
mic.getRecognitionListener().onComplete();
proceedAndNotify();
}
}
} catch (final MalformedURLException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "MalformedURLException");
e.printStackTrace();
}
onError(SpeechRecognizer.ERROR_NETWORK);
} catch (final ParseException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "ParseException");
e.printStackTrace();
}
onError(SpeechRecognizer.ERROR_NETWORK);
} catch (final UnknownHostException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnknownHostException");
e.printStackTrace();
}
onError(SpeechRecognizer.ERROR_NETWORK);
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IOException");
e.printStackTrace();
}
onError(SpeechRecognizer.ERROR_NETWORK);
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IllegalStateException");
e.printStackTrace();
}
onError(SpeechRecognizer.ERROR_NETWORK);
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "NullPointerException");
e.printStackTrace();
}
onError(SpeechRecognizer.ERROR_NETWORK);
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
onError(SpeechRecognizer.ERROR_NETWORK);
} finally {
closeConnection();
mic.stopRecording();
}
}
};
httpThread.start();
}
@Override
public void onBufferReceived(final int bufferReadResult, final byte[] buffer) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onBufferReceived");
}
try {
for (int i = 0; i < bufferReadResult; i++) {
if (outputStream != null) {
outputStream.write(buffer[i]);
}
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "onBufferReceived: IOException");
e.printStackTrace();
}
}
}
private void proceedAndNotify() {
if (retryCount < 2) {
if (retryCount == 0) {
final LocalRequest localRequest = new LocalRequest(mic.getContext());
localRequest.setSupportedLanguage(sl);
localRequest.setAction(LocalRequest.ACTION_SPEAK_ONLY);
localRequest.setTTSLocale(SPH.getTTSLocale(mic.getContext()));
localRequest.setVRLocale(SPH.getVRLocale(mic.getContext()));
localRequest.setUtterance(PersonalityResponse.getBVAnalysisCompleteResponse(mic.getContext(), sl));
localRequest.execute();
}
retryCount++;
final TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if (DEBUG) {
MyLog.i(CLS_NAME, "timerTask: fetching analysis");
}
if (!new BVEmotionAnalysis(mic.getContext(), sl, token).getAnalysis(recordingId, 0)
.first) {
proceedAndNotify();
}
}
};
new Timer().schedule(timerTask, BeyondVerbal.FETCH_ANALYSIS_DELAY);
}
}
@Override
public void onError(int error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onError");
}
mic.getRecognitionListener().onComplete();
Recognition.setState(Recognition.State.IDLE);
if (mic.isInterrupted()) {
error = Speaker.ERROR_USER_CANCELLED;
}
final LocalRequest localRequest = new LocalRequest(mic.getContext());
localRequest.setSupportedLanguage(sl);
localRequest.setAction(LocalRequest.ACTION_SPEAK_ONLY);
localRequest.setTTSLocale(SPH.getTTSLocale(mic.getContext()));
localRequest.setVRLocale(SPH.getVRLocale(mic.getContext()));
switch (error) {
case Speaker.ERROR_USER_CANCELLED:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_USER_CANCELLED");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.cancelled));
break;
case Speaker.ERROR_NETWORK:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_NETWORK");
}
localRequest.setUtterance(PersonalityResponse.getNoNetwork(mic.getContext(), sl));
break;
case Speaker.ERROR_AUDIO:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_AUDIO");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
case Speaker.ERROR_FILE:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_FILE");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
default:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError default");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
}
localRequest.execute();
}
@Override
public void onPauseDetected() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onPauseDetected");
}
}
@Override
public void onRecordingStarted() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingStarted");
}
}
@Override
public void onRecordingEnded() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingEnded");
}
Recognition.setState(Recognition.State.IDLE);
}
@Override
public void onFileWriteComplete(final boolean success) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingEnded");
}
}
private void closeConnection() {
if (DEBUG) {
MyLog.i(CLS_NAME, "closeConnection");
}
if (outputStream != null) {
try {
outputStream.close();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
if (urlConnection != null) {
try {
urlConnection.disconnect();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/language/SupportedLanguageBV.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.language;
import android.support.annotation.NonNull;
import java.util.Locale;
/**
* Utility class to query and access supported languages of the Beyond Verbal API
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public enum SupportedLanguageBV {
ENGLISH_US("en-us", "eng_USA", "English", "United States", Locale.US);
private final String languageCountry;
private final String languageCountryISO;
private final String language;
private final String country;
private final Locale locale;
SupportedLanguageBV(final String country, final String languageCountry, final String languageCountryISO,
final String language, final Locale locale) {
this.country = country;
this.languageCountry = languageCountry;
this.languageCountryISO = languageCountryISO;
this.language = language;
this.locale = locale;
}
public static boolean isSupported(@NonNull final Locale loc) {
for (final SupportedLanguageBV sl : getSupportedLanguages()) {
if (sl.getLocale().equals(loc)) {
return true;
}
}
return false;
}
public static SupportedLanguageBV getSupportedLanguage(@NonNull final Locale loc) {
for (final SupportedLanguageBV sl : getSupportedLanguages()) {
if (sl.getLocale().equals(loc)) {
return sl;
}
}
return ENGLISH_US;
}
public static SupportedLanguageBV[] getSupportedLanguages() {
return SupportedLanguageBV.values();
}
public String getCountry() {
return country;
}
public String getLanguage() {
return language;
}
public String getLanguageCountry() {
return languageCountry;
}
public String getServerFormat() {
return languageCountry;
}
public String getLanguageCountryISO() {
return languageCountryISO;
}
public Locale getLocale() {
return locale;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/emotion/provider/beyondverbal/user/MetaData.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.emotion.provider.beyondverbal.user;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Class to set additional optional information regarding the user. Such parameters will have future
* uses, such as direct and social media sharing.
*
* In the case of encrypted emotion analysis, the unique device id will need to be set.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class MetaData {
private static final String EMAIL = "email";
private static final String PHONE = "phone";
private static final String DEVICE_ID = "deviceId";
private static final String FACEBOOK_ID = "facebookId";
private static final String TWITTER_ID = "twitterId";
private static final String DEFAULT_DEVICE_ID = "default_device_id";
private static final String DEFAULT_DEVICE_PHONE = "default_phone";
private static final String DEFAULT_DEVICE_EMAIL = "default_email";
private static final String DEFAULT_DEVICE_FACEBOOK_ID = "default_facebook_id";
private static final String DEFAULT_DEVICE_TWITTER_HANDLE = "default_twitter_id";
private String email;
private String phone;
private String deviceId;
private String facebookId;
private String twitterId;
/**
* Default Constructor
*/
public MetaData() {
}
/**
* Constructor
*
* @param deviceId the device unique id
* @param email the user's email address
* @param facebookId the user's facebook url
* @param phone the user's phone number
* @param twitterId the user's Twitter handle
*/
public MetaData(@Nullable final String deviceId, @Nullable final String email,
@Nullable final String facebookId, @Nullable final String phone,
@Nullable final String twitterId) {
this.deviceId = deviceId;
this.email = email;
this.facebookId = facebookId;
this.phone = phone;
this.twitterId = twitterId;
}
public static MetaData getEmpty() {
return new MetaData(DEFAULT_DEVICE_ID, DEFAULT_DEVICE_EMAIL,
DEFAULT_DEVICE_FACEBOOK_ID, DEFAULT_DEVICE_PHONE, DEFAULT_DEVICE_TWITTER_HANDLE);
}
/**
* Method to prepare the meta data in a JSON format that the API will accept.
*
* @return a JSON formatted representation of the meta data
*/
public JSONObject getMetaJSON() {
final JSONObject object = new JSONObject();
try {
object.put(EMAIL, getEmail());
object.put(PHONE, getPhone());
object.put(DEVICE_ID, getDeviceId());
object.put(FACEBOOK_ID, getFacebookId());
object.put(TWITTER_ID, getTwitterId());
} catch (JSONException e) {
e.printStackTrace();
}
return object;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(@NonNull final String deviceId) {
this.deviceId = deviceId;
}
public String getEmail() {
return email;
}
public void setEmail(@NonNull final String email) {
this.email = email;
}
public String getFacebookId() {
return facebookId;
}
public void setFacebookId(@NonNull final String facebookId) {
this.facebookId = facebookId;
}
public String getPhone() {
return phone;
}
public void setPhone(@NonNull final String phone) {
this.phone = phone;
}
public String getTwitterId() {
return twitterId;
}
public void setTwitterId(@NonNull final String twitterId) {
this.twitterId = twitterId;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/Speaker.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft;
import android.support.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 16/09/2016.
*/
public class Speaker {
public static final String EXTRA_IDENTIFY_OUTCOME = "extra_identify_outcome";
public static final String EXTRA_IDENTITY_OUTCOME = "extra_identity_outcome";
public static final int ERROR_USER_CANCELLED = 1;
public static final int ERROR_AUDIO = 2;
public static final int ERROR_NETWORK = 3;
public static final int ERROR_FILE = 4;
public enum Confidence {
HIGH,
NORMAL,
LOW,
UNDEFINED,
ERROR;
/**
* Get the {@link Confidence} from the string equivalent
*
* @param level the string level
* @return the equivalent {@link Confidence}
*/
public static Confidence getConfidence(@Nullable final String level) {
if (UtilsString.notNaked(level)) {
if (StringUtils.containsIgnoreCase(HIGH.name(), level)) {
return HIGH;
} else if (StringUtils.containsIgnoreCase(NORMAL.name(), level)) {
return NORMAL;
} else if (StringUtils.containsIgnoreCase(LOW.name(), level)) {
return LOW;
}
}
return UNDEFINED;
}
}
public enum Status {
NOTSTARTED,
RUNNING,
FAILED,
SUCCEEDED,
UNDEFINED;
/**
* Get the {@link Status} from the string equivalent
*
* @param status the string status
* @return the equivalent {@link Status}
*/
public static Status getStatus(@Nullable final String status) {
if (UtilsString.notNaked(status)) {
if (StringUtils.containsIgnoreCase(NOTSTARTED.name(), status)) {
return NOTSTARTED;
} else if (StringUtils.containsIgnoreCase(RUNNING.name(), status)) {
return RUNNING;
} else if (StringUtils.containsIgnoreCase(FAILED.name(), status)) {
return FAILED;
} else if (StringUtils.containsIgnoreCase(SUCCEEDED.name(), status)) {
return SUCCEEDED;
}
}
return UNDEFINED;
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/SpeakerEnrollment.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft;
import android.os.Process;
import android.support.annotation.NonNull;
import ai.saiy.android.R;
import ai.saiy.android.audio.IMic;
import ai.saiy.android.audio.RecognitionMic;
import ai.saiy.android.cognitive.identity.provider.microsoft.http.CreateIDEnrollment;
import ai.saiy.android.localisation.SaiyResourcesHelper;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.personality.PersonalityResponse;
import ai.saiy.android.recognition.Recognition;
import ai.saiy.android.service.helper.LocalRequest;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
/**
* Created by benrandall76@gmail.com on 07/09/2016.
*/
public class SpeakerEnrollment implements IMic {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = SpeakerEnrollment.class.getSimpleName();
public static final long MINIMUM_AUDIO_TIME = 15000;
private final SupportedLanguage sl;
private final RecognitionMic mic;
private final String apiKey;
private final String profileId;
private final boolean shortAudio;
/**
* Constructor
*
* @param mic the initialised {@link RecognitionMic} object
* @param sl the {@link SupportedLanguage}
* @param apiKey the OCP APIM key
* @param profileId of the user
* @param shortAudio true if the capture length will be below the recommended threshold, false otherwise
*/
public SpeakerEnrollment(@NonNull final RecognitionMic mic,
@NonNull final SupportedLanguage sl, @NonNull final String apiKey,
@NonNull final String profileId, final boolean shortAudio) {
this.mic = mic;
this.sl = sl;
this.apiKey = apiKey;
this.profileId = profileId;
this.shortAudio = shortAudio;
this.mic.setMicListener(this);
}
/**
* Start recording the enrollment audio
*/
public void record() {
if (DEBUG) {
MyLog.i(CLS_NAME, "record");
}
if (mic.isAvailable()) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);
mic.startRecording();
synchronized (mic.getLock()) {
if (DEBUG) {
MyLog.i(CLS_NAME, "mic locked");
}
while (mic.isRecording()) {
if (DEBUG) {
MyLog.i(CLS_NAME, "mic isRecording");
}
try {
if (DEBUG) {
MyLog.i(CLS_NAME, "mic lock waiting");
}
mic.getLock().wait();
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "InterruptedException");
e.printStackTrace();
}
break;
}
}
}
if (DEBUG) {
MyLog.i(CLS_NAME, "record lock released");
}
if (mic.isInterrupted()) {
onError(Speaker.ERROR_USER_CANCELLED);
} else {
mic.getRecognitionListener().onComplete();
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "mic unavailable");
}
onError(Speaker.ERROR_AUDIO);
}
}
@Override
public void onError(int error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onError");
}
mic.getRecognitionListener().onComplete();
Recognition.setState(Recognition.State.IDLE);
if (mic.isInterrupted()) {
error = Speaker.ERROR_USER_CANCELLED;
}
final LocalRequest localRequest = new LocalRequest(mic.getContext());
localRequest.setSupportedLanguage(sl);
localRequest.setAction(LocalRequest.ACTION_SPEAK_ONLY);
localRequest.setTTSLocale(SPH.getTTSLocale(mic.getContext()));
localRequest.setVRLocale(SPH.getVRLocale(mic.getContext()));
switch (error) {
case Speaker.ERROR_USER_CANCELLED:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_USER_CANCELLED");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.cancelled));
break;
case Speaker.ERROR_NETWORK:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_NETWORK");
}
localRequest.setUtterance(PersonalityResponse.getNoNetwork(mic.getContext(), sl));
break;
case Speaker.ERROR_AUDIO:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_AUDIO");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
case Speaker.ERROR_FILE:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_FILE");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
default:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError default");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
}
localRequest.execute();
}
@Override
public void onBufferReceived(final int bufferReadResult, final byte[] buffer) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onBufferReceived");
}
}
@Override
public void onPauseDetected() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onPauseDetected");
}
}
@Override
public void onRecordingStarted() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingStarted");
}
}
@Override
public void onRecordingEnded() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingEnded");
}
Recognition.setState(Recognition.State.IDLE);
}
@Override
public void onFileWriteComplete(final boolean success) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onFileWriteComplete: " + success);
}
if (!mic.isInterrupted()) {
if (success) {
final LocalRequest localRequest = new LocalRequest(mic.getContext());
localRequest.setSupportedLanguage(sl);
localRequest.setAction(LocalRequest.ACTION_SPEAK_ONLY);
localRequest.setTTSLocale(SPH.getTTSLocale(mic.getContext()));
localRequest.setVRLocale(SPH.getVRLocale(mic.getContext()));
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.vocal_notify_enroll));
localRequest.execute();
new CreateIDEnrollment(mic, apiKey, profileId, shortAudio, mic.getFile()).stream();
} else {
onError(Speaker.ERROR_FILE);
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onFileWriteComplete: mic interrupted");
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/SpeakerIdentification.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft;
import android.os.Process;
import android.support.annotation.NonNull;
import ai.saiy.android.R;
import ai.saiy.android.audio.IMic;
import ai.saiy.android.audio.RecognitionMic;
import ai.saiy.android.cognitive.identity.provider.microsoft.http.ValidateID;
import ai.saiy.android.localisation.SaiyResourcesHelper;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.personality.PersonalityResponse;
import ai.saiy.android.recognition.Recognition;
import ai.saiy.android.service.helper.LocalRequest;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
/**
* Created by benrandall76@gmail.com on 07/09/2016.
*/
public class SpeakerIdentification implements IMic {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = SpeakerIdentification.class.getSimpleName();
public static final long MINIMUM_AUDIO_TIME = 3000;
private final SupportedLanguage sl;
private final RecognitionMic mic;
private final String apiKey;
private final String profileId;
private final boolean shortAudio;
/**
* Constructor
*
* @param mic the initialised {@link RecognitionMic} object
* @param sl the {@link SupportedLanguage}
* @param apiKey the OCP APIM key
* @param profileId of the user
* @param shortAudio true if the capture length will be below the recommended threshold, false otherwise
*/
public SpeakerIdentification(@NonNull final RecognitionMic mic,
@NonNull final SupportedLanguage sl, @NonNull final String apiKey,
@NonNull final String profileId, final boolean shortAudio) {
this.mic = mic;
this.sl = sl;
this.apiKey = apiKey;
this.profileId = profileId;
this.shortAudio = shortAudio;
this.mic.setMicListener(this);
}
/**
* Start recording the enrollment file
*/
public void record() {
if (DEBUG) {
MyLog.i(CLS_NAME, "record");
}
if (mic.isAvailable()) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);
mic.startRecording();
synchronized (mic.getLock()) {
while (mic.isRecording()) {
try {
if (DEBUG) {
MyLog.i(CLS_NAME, "mic lock waiting");
}
mic.getLock().wait();
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "InterruptedException");
e.printStackTrace();
}
break;
}
}
}
if (DEBUG) {
MyLog.i(CLS_NAME, "record lock released");
}
mic.getRecognitionListener().onComplete();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "mic unavailable");
}
onError(Speaker.ERROR_AUDIO);
}
}
@Override
public void onError(int error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onError");
}
mic.getRecognitionListener().onComplete();
Recognition.setState(Recognition.State.IDLE);
if (mic.isInterrupted()) {
error = Speaker.ERROR_USER_CANCELLED;
}
final LocalRequest localRequest = new LocalRequest(mic.getContext());
localRequest.setSupportedLanguage(sl);
localRequest.setAction(LocalRequest.ACTION_SPEAK_ONLY);
localRequest.setTTSLocale(SPH.getTTSLocale(mic.getContext()));
localRequest.setVRLocale(SPH.getVRLocale(mic.getContext()));
switch (error) {
case Speaker.ERROR_USER_CANCELLED:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_USER_CANCELLED");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.cancelled));
break;
case Speaker.ERROR_NETWORK:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_NETWORK");
}
localRequest.setUtterance(PersonalityResponse.getNoNetwork(mic.getContext(), sl));
break;
case Speaker.ERROR_AUDIO:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_AUDIO");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
case Speaker.ERROR_FILE:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError ERROR_FILE");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
default:
if (DEBUG) {
MyLog.w(CLS_NAME, "onError default");
}
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.error_audio));
break;
}
localRequest.execute();
}
@Override
public void onBufferReceived(final int bufferReadResult, final byte[] buffer) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onBufferReceived");
}
}
@Override
public void onPauseDetected() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onPauseDetected");
}
}
@Override
public void onRecordingStarted() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingStarted");
}
}
@Override
public void onRecordingEnded() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingEnded");
}
Recognition.setState(Recognition.State.IDLE);
}
@Override
public void onFileWriteComplete(final boolean success) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onFileWriteComplete: " + success);
}
if (!mic.isInterrupted()) {
if (success) {
final LocalRequest localRequest = new LocalRequest(mic.getContext());
localRequest.setSupportedLanguage(sl);
localRequest.setAction(LocalRequest.ACTION_SPEAK_ONLY);
localRequest.setTTSLocale(SPH.getTTSLocale(mic.getContext()));
localRequest.setVRLocale(SPH.getVRLocale(mic.getContext()));
localRequest.setUtterance(SaiyResourcesHelper.getStringResource(mic.getContext(), sl,
R.string.vocal_notify_verify));
localRequest.execute();
new ValidateID(mic, sl, apiKey, profileId, shortAudio, mic.getFile()).stream();
} else {
onError(Speaker.ERROR_FILE);
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onFileWriteComplete: mic interrupted");
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/containers/EnrollmentID.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.containers;
import com.google.gson.annotations.SerializedName;
/**
* Created by benrandall76@gmail.com on 07/09/2016.
*/
public class EnrollmentID {
@SerializedName("identificationProfileId")
private final String id;
public EnrollmentID(final String id) {
this.id = id;
}
public String getId() {
return id;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/containers/OperationStatus.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.containers;
import com.google.gson.annotations.SerializedName;
/**
* Created by benrandall76@gmail.com on 15/09/2016.
*/
public class OperationStatus {
@SerializedName("status")
private String status;
@SerializedName("createdDateTime")
private String created;
@SerializedName("lastActionDateTime")
private String lastAction;
@SerializedName("message")
private String message;
@SerializedName("processingResult")
private ProcessingResult processingResult;
public OperationStatus(final String created, final String lastAction, final String message,
final ProcessingResult processingResult, final String status) {
this.created = created;
this.lastAction = lastAction;
this.message = message;
this.processingResult = processingResult;
this.status = status;
}
public String getCreated() {
return created;
}
public void setCreated(final String created) {
this.created = created;
}
public String getLastAction() {
return lastAction;
}
public void setLastAction(final String lastAction) {
this.lastAction = lastAction;
}
public String getMessage() {
return message;
}
public void setMessage(final String message) {
this.message = message;
}
public ProcessingResult getProcessingResult() {
return processingResult;
}
public void setProcessingResult(final ProcessingResult processingResult) {
this.processingResult = processingResult;
}
public String getStatus() {
return status;
}
public void setStatus(final String status) {
this.status = status;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/containers/ProcessingResult.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.containers;
import com.google.gson.annotations.SerializedName;
/**
* Created by benrandall76@gmail.com on 15/09/2016.
*/
public class ProcessingResult {
@SerializedName("enrollmentStatus")
private String enrollmentStatus;
@SerializedName("speechTime")
private double speechTime;
@SerializedName("enrollmentSpeechTime")
private double enrollmentSpeechTime;
@SerializedName("remainingEnrollmentSpeechTime")
private double remainingSpeechTime;
@SerializedName("identifiedProfileId")
private String profileId;
@SerializedName("confidence")
private String confidence;
public ProcessingResult(final String confidence, final double enrollmentSpeechTime,
final String enrollmentStatus, final String profileId,
final double remainingSpeechTime, final double speechTime) {
this.confidence = confidence;
this.enrollmentSpeechTime = enrollmentSpeechTime;
this.enrollmentStatus = enrollmentStatus;
this.profileId = profileId;
this.remainingSpeechTime = remainingSpeechTime;
this.speechTime = speechTime;
}
public String getConfidence() {
return confidence;
}
public void setConfidence(final String confidence) {
this.confidence = confidence;
}
public double getEnrollmentSpeechTime() {
return enrollmentSpeechTime;
}
public void setEnrollmentSpeechTime(final double enrollmentSpeechTime) {
this.enrollmentSpeechTime = enrollmentSpeechTime;
}
public String getEnrollmentStatus() {
return enrollmentStatus;
}
public void setEnrollmentStatus(final String enrollmentStatus) {
this.enrollmentStatus = enrollmentStatus;
}
public String getProfileId() {
return profileId;
}
public void setProfileId(final String profileId) {
this.profileId = profileId;
}
public double getRemainingSpeechTime() {
return remainingSpeechTime;
}
public void setRemainingSpeechTime(final double remainingSpeechTime) {
this.remainingSpeechTime = remainingSpeechTime;
}
public double getSpeechTime() {
return speechTime;
}
public void setSpeechTime(final double speechTime) {
this.speechTime = speechTime;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/containers/ProfileItem.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.containers;
import android.support.annotation.NonNull;
import com.google.gson.annotations.SerializedName;
/**
* Created by benrandall76@gmail.com on 07/09/2016.
*/
public class ProfileItem {
@SerializedName("identificationProfileId")
private final String id;
@SerializedName("locale")
private String locale;
@SerializedName("createdDateTime")
private String created;
@SerializedName("lastActionDateTime")
private String lastAction;
@SerializedName("enrollmentStatus")
private String status;
@SerializedName("enrollmentSpeechTime")
private double speechTime;
@SerializedName("remainingEnrollmentSpeechTime")
private double remainingSpeechTime;
public ProfileItem(final String created, final String id, final String locale, final String lastAction,
final String status, final double speechTime, final double remainingSpeechTime) {
this.created = created;
this.id = id;
this.locale = locale;
this.lastAction = lastAction;
this.status = status;
this.speechTime = speechTime;
this.remainingSpeechTime = remainingSpeechTime;
}
public ProfileItem(@NonNull final String id) {
this.id = id;
}
public String getCreated() {
return created;
}
public String getId() {
return id;
}
public String getLastAction() {
return lastAction;
}
public String getLocale() {
return locale;
}
public double getRemainingSpeechTime() {
return remainingSpeechTime;
}
public double getSpeechTime() {
return speechTime;
}
public String getStatus() {
return status;
}
public void setCreated(final String created) {
this.created = created;
}
public void setLastAction(final String lastAction) {
this.lastAction = lastAction;
}
public void setLocale(final String locale) {
this.locale = locale;
}
public void setRemainingSpeechTime(final double remainingSpeechTime) {
this.remainingSpeechTime = remainingSpeechTime;
}
public void setSpeechTime(final double speechTime) {
this.speechTime = speechTime;
}
public void setStatus(final String status) {
this.status = status;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/containers/ProfileList.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.containers;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 07/09/2016.
*/
public class ProfileList {
private final List items;
public ProfileList(final List items) {
this.items = items;
}
public List getItems() {
return items;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/http/CreateIDEnrollment.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.http;
import android.net.ParseException;
import android.os.Process;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.HttpsURLConnection;
import ai.saiy.android.audio.RecognitionMic;
import ai.saiy.android.cognitive.identity.provider.microsoft.Speaker;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.OperationStatus;
import ai.saiy.android.processing.Condition;
import ai.saiy.android.ui.notification.NotificationHelper;
import ai.saiy.android.user.SaiyAccountHelper;
import ai.saiy.android.utils.Constants;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 15/09/2016.
*/
public class CreateIDEnrollment {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = CreateIDEnrollment.class.getSimpleName();
private static final String ENROLLMENT_URL = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles/";
private static final String ENROLLMENT_URL_EXTRA = "/enroll?shortAudio=";
private static final String OCP_SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key";
private static final String CONTENT_TYPE = "Content-Type";
private static final String CONTENT_TYPE_AUDIO_PARAMS = "audio/l16; rate=16000";
private static final int HTTP_ACCEPTED = 202;
private static final String OPERATION_LOCATION = "Operation-Location";
private static final long FETCH_DELAY = 6000;
private static final long FETCH_DELAY_EXTENDED = 12000;
private volatile HttpsURLConnection urlConnection;
private volatile OutputStream outputStream;
private FileInputStream fileInputStream;
private volatile boolean retry = true;
private final String apiKey;
private final String profileId;
private final RecognitionMic mic;
private final boolean shortAudio;
private final File file;
/**
* Constructor
*
* @param mic the initialised {@link RecognitionMic} object
* @param apiKey the api key
* @param profileId of the user.
*/
public CreateIDEnrollment(@NonNull final RecognitionMic mic, @NonNull final String apiKey,
@NonNull final String profileId, final boolean shortAudio,
@NonNull final File file) {
this.mic = mic;
this.apiKey = apiKey;
this.profileId = profileId;
this.shortAudio = shortAudio;
this.file = file;
}
/**
* Start streaming the Microsoft enrollment servers
*/
public void stream() {
if (DEBUG) {
MyLog.i(CLS_NAME, "stream");
}
final Thread httpThread = new Thread() {
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);
try {
final String url = ENROLLMENT_URL + URLEncoder.encode(profileId, Constants.ENCODING_UTF8)
+ ENROLLMENT_URL_EXTRA + String.valueOf(shortAudio);
urlConnection = (HttpsURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod(Constants.HTTP_POST);
urlConnection.setRequestProperty(OCP_SUBSCRIPTION_KEY_HEADER, apiKey);
urlConnection.setRequestProperty(CONTENT_TYPE, CONTENT_TYPE_AUDIO_PARAMS);
urlConnection.setUseCaches(false);
urlConnection.setDoOutput(true);
urlConnection.connect();
outputStream = urlConnection.getOutputStream();
fileInputStream = new FileInputStream(file);
final byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
if (DEBUG) {
MyLog.i(CLS_NAME, "requesting response");
}
final int responseCode = urlConnection.getResponseCode();
if (DEBUG) {
MyLog.d(CLS_NAME, "responseCode: " + responseCode);
}
if (responseCode != HTTP_ACCEPTED) {
if (DEBUG) {
MyLog.e(CLS_NAME, "ErrorStream: "
+ UtilsString.streamToString(urlConnection.getErrorStream()));
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} else {
if (DEBUG) {
MyLog.d(CLS_NAME, "response: HTTP_ACCEPTED");
}
final String statusUrl = urlConnection.getHeaderField(OPERATION_LOCATION);
if (UtilsString.notNaked(statusUrl)) {
if (DEBUG) {
MyLog.d(CLS_NAME, "response statusUrl: " + statusUrl);
}
final TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if (DEBUG) {
MyLog.i(CLS_NAME, "timerTask: fetching operation status");
}
checkResult(new FetchIDOperation(mic.getContext(), apiKey, statusUrl)
.getStatus(), statusUrl);
}
};
new Timer().schedule(timerTask, FETCH_DELAY);
} else {
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
}
}
} catch (final MalformedURLException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "MalformedURLException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final UnsupportedEncodingException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnsupportedEncodingException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final ParseException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "ParseException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final UnknownHostException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnknownHostException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IOException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IllegalStateException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "NullPointerException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} finally {
closeConnection();
}
}
};
httpThread.start();
}
/**
* Check the result of the enrollment request
*
* @param statusPair the {@link Pair} with the first parameter denoting success and the
* second the {@link OperationStatus} object
* @param statusUrl url to recheck the status if required
*/
private void checkResult(@NonNull final Pair statusPair,
@NonNull final String statusUrl) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkResult");
}
if (statusPair.first && statusPair.second != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "statusPair: have data");
}
switch (Speaker.Status.getStatus(statusPair.second.getStatus())) {
case SUCCEEDED:
final boolean success = SaiyAccountHelper.updateEnrollmentStatus(
mic.getContext(), statusPair.second, profileId);
if (DEBUG) {
MyLog.i(CLS_NAME, "account updated: " + success);
}
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTITY, success, null);
break;
case FAILED:
case UNDEFINED:
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTITY, false, null);
break;
case NOTSTARTED:
case RUNNING:
if (retry) {
retry = false;
final TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if (DEBUG) {
MyLog.i(CLS_NAME, "timerTask: fetching operation status");
}
checkResult(new FetchIDOperation(mic.getContext(), apiKey, statusUrl)
.getStatus(), statusUrl);
}
};
new Timer().schedule(timerTask, FETCH_DELAY_EXTENDED);
} else {
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTITY, false, null);
}
break;
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "statusPair false");
}
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTITY, false, null);
}
}
private void closeConnection() {
if (DEBUG) {
MyLog.i(CLS_NAME, "closeConnection");
}
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
if (urlConnection != null) {
try {
urlConnection.disconnect();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/http/CreateIDProfile.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.http;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.EnrollmentID;
import ai.saiy.android.utils.MyLog;
/**
* Class to get an initial enrollment id, which will be used to enroll and validate the user.
*
* This request is always synchronous, as it is an 'on-demand' requirement.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class CreateIDProfile {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = CreateIDProfile.class.getSimpleName();
private static final String CREATE_URL = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles";
private static final String OCP_SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key";
private static final String ENCODING = "UTF-8";
private static final String CHARSET = "Accept-Charset";
private static final String JSON_HEADER_ACCEPT = "accept";
private static final String JSON_HEADER_VALUE_ACCEPT = "application/json";
private static final String LOCALE_PARAM = "locale";
private static final long THREAD_TIMEOUT = 7L;
private final Context mContext;
private final String apiKey;
private final Locale locale;
/**
* Constructor
*
* @param mContext the application context
* @param apiKey the api key
* @param locale the locale of the user. Currently unused as only en-us is supported
*/
public CreateIDProfile(@NonNull final Context mContext, @NonNull final String apiKey,
@NonNull final Locale locale) {
this.apiKey = apiKey;
this.mContext = mContext.getApplicationContext();
this.locale = locale;
}
/**
* Method to get an enrollment id.
*
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link EnrollmentID} object, containing the id. If the request was unsuccessful,
* the second parameter may be null.
*/
public Pair getID() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getID");
}
final long then = System.nanoTime();
final JSONObject object = new JSONObject();
try {
object.put(LOCALE_PARAM, "en-us");
} catch (final JSONException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getID: JSONException");
e.printStackTrace();
}
}
final RequestFuture future = RequestFuture.newFuture();
final RequestQueue queue = Volley.newRequestQueue(mContext);
queue.start();
final JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.POST, CREATE_URL, object, future,
new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
CreateIDProfile.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(JSON_HEADER_ACCEPT, JSON_HEADER_VALUE_ACCEPT);
params.put(OCP_SUBSCRIPTION_KEY_HEADER, apiKey);
return params;
}
};
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(jsonObjReq);
JSONObject response = null;
try {
response = future.get(THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getID: InterruptedException");
e.printStackTrace();
}
} catch (final ExecutionException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getID: ExecutionException");
e.printStackTrace();
}
} catch (final TimeoutException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getID: TimeoutException");
e.printStackTrace();
}
} finally {
queue.stop();
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
if (response != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "response: " + response.toString());
}
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final EnrollmentID enrollmentID = gson.fromJson(response.toString(), EnrollmentID.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: getId: " + enrollmentID.getId());
}
return new Pair<>(true, enrollmentID);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return new Pair<>(false, null);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/http/DeleteIDProfile.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.http;
import android.content.Context;
import android.support.annotation.NonNull;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import ai.saiy.android.utils.Constants;
import ai.saiy.android.utils.MyLog;
/**
* Created by benrandall76@gmail.com on 07/09/2016.
*/
public class DeleteIDProfile {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = DeleteIDProfile.class.getSimpleName();
private static final String DELETE_URL = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles/";
private static final String OCP_SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key";
private static final String CHARSET = "Accept-Charset";
private final Context mContext;
private final String apiKey;
private final String profileId;
/**
* Constructor
*
* @param mContext the application context
* @param apiKey the api key
* @param profileId to delete
*/
public DeleteIDProfile(@NonNull final Context mContext, @NonNull final String apiKey,
@NonNull final String profileId) {
this.apiKey = apiKey;
this.mContext = mContext.getApplicationContext();
this.profileId = profileId;
}
public void delete() {
final long then = System.nanoTime();
String url = null;
try {
url = DELETE_URL + URLEncoder.encode(profileId, Constants.ENCODING_UTF8);
} catch (final UnsupportedEncodingException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "delete: UnsupportedEncodingException");
e.printStackTrace();
}
}
final RequestQueue queue = Volley.newRequestQueue(mContext);
queue.start();
final StringRequest stringRequest = new StringRequest(Request.Method.DELETE, url,
new Response.Listener() {
@Override
public void onResponse(final String response) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: success");
}
queue.stop();
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
DeleteIDProfile.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, Constants.ENCODING_UTF8);
params.put(OCP_SUBSCRIPTION_KEY_HEADER, apiKey);
return params;
}
};
stringRequest.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(stringRequest);
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/http/FetchIDOperation.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.http;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.OperationStatus;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.ProcessingResult;
import ai.saiy.android.utils.MyLog;
/**
* Class to check the enrollment status of the user, post receipt of audio.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class FetchIDOperation {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = FetchIDOperation.class.getSimpleName();
private static final String OCP_SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key";
private static final String ENCODING = "UTF-8";
private static final String CHARSET = "Accept-Charset";
private static final String JSON_HEADER_ACCEPT = "accept";
private static final String JSON_HEADER_VALUE_ACCEPT = "application/json";
private static final long THREAD_TIMEOUT = 7L;
private final Context mContext;
private final String apiKey;
private final String url;
/**
* Constructor
*
* @param mContext the application context
* @param apiKey the api key
* @param url of the operation status.
*/
public FetchIDOperation(@NonNull final Context mContext, @NonNull final String apiKey,
@NonNull final String url) {
this.apiKey = apiKey;
this.mContext = mContext.getApplicationContext();
this.url = url;
}
/**
* Method to get an enrollment id.
*
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link OperationStatus} object. If the request was unsuccessful,
* the second parameter may be null.
*/
public Pair getStatus() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getStatus");
}
final long then = System.nanoTime();
final RequestFuture future = RequestFuture.newFuture();
final RequestQueue queue = Volley.newRequestQueue(mContext);
queue.start();
final JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, url, null, future,
new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
FetchIDOperation.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(JSON_HEADER_ACCEPT, JSON_HEADER_VALUE_ACCEPT);
params.put(OCP_SUBSCRIPTION_KEY_HEADER, apiKey);
return params;
}
};
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(jsonObjReq);
JSONObject response = null;
try {
response = future.get(THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getStatus: InterruptedException");
e.printStackTrace();
}
} catch (final ExecutionException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getStatus: ExecutionException");
e.printStackTrace();
}
} catch (final TimeoutException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getStatus: TimeoutException");
e.printStackTrace();
}
} finally {
queue.stop();
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
if (response != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "response: " + response.toString());
}
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final OperationStatus operationStatus = gson.fromJson(response.toString(), OperationStatus.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "response: getStatus: " + operationStatus.getStatus());
MyLog.i(CLS_NAME, "response: getCreated: " + operationStatus.getCreated());
MyLog.i(CLS_NAME, "response: getLastAction: " + operationStatus.getLastAction());
MyLog.i(CLS_NAME, "response: getMessage: " + operationStatus.getMessage());
final ProcessingResult processingResult = operationStatus.getProcessingResult();
if (processingResult != null) {
MyLog.i(CLS_NAME, "response: getEnrollmentStatus: " + processingResult.getEnrollmentStatus());
MyLog.i(CLS_NAME, "response: getConfidence: " + processingResult.getConfidence());
MyLog.i(CLS_NAME, "response: getProfileId: " + processingResult.getProfileId());
MyLog.i(CLS_NAME, "response: getEnrollmentSpeechTime: " + processingResult.getEnrollmentSpeechTime());
MyLog.i(CLS_NAME, "response: getRemainingSpeechTime: " + processingResult.getRemainingSpeechTime());
MyLog.i(CLS_NAME, "response: getSpeechTime: " + processingResult.getSpeechTime());
} else {
MyLog.i(CLS_NAME, "response: processingResult: null");
}
}
return new Pair<>(true, operationStatus);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return new Pair<>(false, null);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/http/FetchIDProfile.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.http;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.ProfileItem;
import ai.saiy.android.utils.MyLog;
/**
* Class to get an initial enrollment id, which will be used to enroll and validate the user.
*
* This request is always synchronous, as it is an 'on-demand' requirement.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class FetchIDProfile {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = CreateIDProfile.class.getSimpleName();
private static final String FETCH_URL = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles/";
private static final String OCP_SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key";
private static final String ENCODING = "UTF-8";
private static final String CHARSET = "Accept-Charset";
private static final String JSON_HEADER_ACCEPT = "accept";
private static final String JSON_HEADER_VALUE_ACCEPT = "application/json";
private static final long THREAD_TIMEOUT = 7L;
private final Context mContext;
private final String apiKey;
private final String profileId;
/**
* Constructor
*
* @param mContext the application context
* @param apiKey the api key
* @param profileId the profile id of the user.
*/
public FetchIDProfile(@NonNull final Context mContext, @NonNull final String apiKey,
@NonNull final String profileId) {
this.apiKey = apiKey;
this.mContext = mContext.getApplicationContext();
this.profileId = profileId;
}
/**
* Method to get an enrollment id.
*
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link ProfileItem} object. If the request was unsuccessful,
* the second parameter may be null.
*/
public Pair getProfile() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getProfile");
}
final long then = System.nanoTime();
String url = null;
try {
url = FETCH_URL + URLEncoder.encode(profileId, ENCODING);
} catch (final UnsupportedEncodingException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProfile: UnsupportedEncodingException");
e.printStackTrace();
}
}
final RequestFuture future = RequestFuture.newFuture();
final RequestQueue queue = Volley.newRequestQueue(mContext);
queue.start();
final JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, url, null, future,
new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
FetchIDProfile.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(JSON_HEADER_ACCEPT, JSON_HEADER_VALUE_ACCEPT);
params.put(OCP_SUBSCRIPTION_KEY_HEADER, apiKey);
return params;
}
};
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(jsonObjReq);
JSONObject response = null;
try {
response = future.get(THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProfile: InterruptedException");
e.printStackTrace();
}
} catch (final ExecutionException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProfile: ExecutionException");
e.printStackTrace();
}
} catch (final TimeoutException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProfile: TimeoutException");
e.printStackTrace();
}
} finally {
queue.stop();
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
if (response != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "response: " + response.toString());
}
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final ProfileItem profileItem = gson.fromJson(response.toString(), ProfileItem.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "response: getId: " + profileItem.getId());
}
return new Pair<>(true, profileItem);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return new Pair<>(false, null);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/http/ListIDProfiles.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.http;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.ProfileList;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.ProfileItem;
import ai.saiy.android.utils.MyLog;
/**
* Class to get a list of enrollment ids.
*
* This request is always synchronous, as it is an 'on-demand' requirement.
*
* Created by benrandall76@gmail.com on 08/06/2016.
*/
public class ListIDProfiles {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = ListIDProfiles.class.getSimpleName();
private static final String LIST_URL = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles";
private static final String OCP_SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key";
private static final String ENCODING = "UTF-8";
private static final String CHARSET = "Accept-Charset";
/**
* The initial volley request can be inexplicably slow
*/
private static final int OTT_MULTIPLIER = 20;
private static final long THREAD_TIMEOUT = 20L;
private final Context mContext;
private final String apiKey;
/**
* Constructor
*
* @param mContext the application context
* @param apiKey the api key
*/
public ListIDProfiles(@NonNull final Context mContext, @NonNull final String apiKey) {
this.apiKey = apiKey;
this.mContext = mContext.getApplicationContext();
}
/**
* Method to get all enrollment profile information.
*
* @return an {@link Pair} of which the first parameter will denote success and the second an
* {@link ProfileList} object, containing the list of profiles. If the request was unsuccessful,
* the second parameter may be null.
*/
public Pair getProfiles() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getProfiles");
}
final long then = System.nanoTime();
final RequestFuture future = RequestFuture.newFuture();
final RequestQueue queue = Volley.newRequestQueue(mContext);
queue.start();
final StringRequest request = new StringRequest(Request.Method.GET, LIST_URL, future,
new Response.ErrorListener() {
@Override
public void onErrorResponse(final VolleyError error) {
if (DEBUG) {
MyLog.w(CLS_NAME, "onErrorResponse: " + error.toString());
ListIDProfiles.this.verboseError(error);
}
queue.stop();
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
final Map params = new HashMap<>();
params.put(CHARSET, ENCODING);
params.put(OCP_SUBSCRIPTION_KEY_HEADER, apiKey);
return params;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * OTT_MULTIPLIER,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
String response = null;
try {
response = future.get(THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProfiles: InterruptedException");
e.printStackTrace();
}
} catch (final ExecutionException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProfiles: ExecutionException");
e.printStackTrace();
}
} catch (final TimeoutException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "getProfiles: TimeoutException");
e.printStackTrace();
}
} finally {
queue.stop();
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
if (response != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "response: " + response);
}
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final Type type = new TypeToken>() {
}.getType();
final ProfileList profileList = new ProfileList(gson.>fromJson(response,type));
if (DEBUG) {
MyLog.i(CLS_NAME, "onResponse: profileList size: " + profileList.getItems().size());
}
return new Pair<>(true, profileList);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return new Pair<>(false, null);
}
}
/**
* Used for debugging only to view verbose error information
*
* @param error the {@link VolleyError}
*/
private void verboseError(@NonNull final VolleyError error) {
final NetworkResponse response = error.networkResponse;
if (response != null && error instanceof ServerError) {
try {
final String result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
MyLog.i(CLS_NAME, "result: " + result);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/identity/provider/microsoft/http/ValidateID.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.identity.provider.microsoft.http;
import android.net.ParseException;
import android.os.Process;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.HttpsURLConnection;
import ai.saiy.android.audio.IMic;
import ai.saiy.android.audio.RecognitionMic;
import ai.saiy.android.cognitive.identity.provider.microsoft.Speaker;
import ai.saiy.android.cognitive.identity.provider.microsoft.containers.OperationStatus;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.processing.Condition;
import ai.saiy.android.ui.notification.NotificationHelper;
import ai.saiy.android.utils.Constants;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 15/09/2016.
*/
public class ValidateID implements IMic {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = ValidateID.class.getSimpleName();
private static final String IDENTIFICATION_URL = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identify?identificationProfileIds=";
private static final String IDENTIFICATION_URL_EXTRA = "&shortAudio=";
private static final String OCP_SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key";
private static final String CONTENT_TYPE = "Content-Type";
private static final String CONTENT_TYPE_AUDIO_PARAMS = "audio/l16; rate=16000";
private static final int HTTP_ACCEPTED = 202;
private static final String OPERATION_LOCATION = "Operation-Location";
private static final long FETCH_DELAY = 6000;
private static final long FETCH_DELAY_EXTENDED = 12000;
private volatile HttpsURLConnection urlConnection;
private volatile OutputStream outputStream;
private FileInputStream fileInputStream;
private volatile boolean retry = true;
private final String apiKey;
private final String profileId;
private final RecognitionMic mic;
private final SupportedLanguage sl;
private final boolean shortAudio;
private final File file;
/**
* Constructor
*
* @param mic the initialised {@link RecognitionMic} object
* @param sl the {@link SupportedLanguage} object
* @param apiKey the api key
* @param profileId of the user.
*/
public ValidateID(@NonNull final RecognitionMic mic, @NonNull final SupportedLanguage sl,
@NonNull final String apiKey,
@NonNull final String profileId, final boolean shortAudio,
@NonNull final File file) {
this.mic = mic;
this.apiKey = apiKey;
this.profileId = profileId;
this.sl = sl;
this.shortAudio = shortAudio;
this.file = file;
}
/**
* Start streaming the Microsoft enrollment servers
*/
public void stream() {
if (DEBUG) {
MyLog.i(CLS_NAME, "stream");
}
final Thread httpThread = new Thread() {
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);
try {
final String url = IDENTIFICATION_URL + URLEncoder.encode(profileId, Constants.ENCODING_UTF8)
+ IDENTIFICATION_URL_EXTRA + String.valueOf(shortAudio);
urlConnection = (HttpsURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod(Constants.HTTP_POST);
urlConnection.setRequestProperty(OCP_SUBSCRIPTION_KEY_HEADER, apiKey);
urlConnection.setRequestProperty(CONTENT_TYPE, CONTENT_TYPE_AUDIO_PARAMS);
urlConnection.setUseCaches(false);
urlConnection.setDoOutput(true);
urlConnection.connect();
outputStream = urlConnection.getOutputStream();
fileInputStream = new FileInputStream(file);
final byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
if (DEBUG) {
MyLog.i(CLS_NAME, "requesting response");
}
final int responseCode = urlConnection.getResponseCode();
if (DEBUG) {
MyLog.d(CLS_NAME, "responseCode: " + responseCode);
}
if (responseCode != HTTP_ACCEPTED) {
if (DEBUG) {
MyLog.e(CLS_NAME, "ErrorStream: "
+ UtilsString.streamToString(urlConnection.getErrorStream()));
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} else {
if (DEBUG) {
MyLog.d(CLS_NAME, "response: HTTP_ACCEPTED");
}
final String statusUrl = urlConnection.getHeaderField(OPERATION_LOCATION);
if (UtilsString.notNaked(statusUrl)) {
if (DEBUG) {
MyLog.d(CLS_NAME, "response statusUrl: " + statusUrl);
}
final TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if (DEBUG) {
MyLog.i(CLS_NAME, "timerTask: fetching operation status");
}
checkResult(new FetchIDOperation(mic.getContext(), apiKey, statusUrl)
.getStatus(), statusUrl);
}
};
new Timer().schedule(timerTask, FETCH_DELAY);
} else {
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
}
}
} catch (final MalformedURLException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "MalformedURLException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final UnsupportedEncodingException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnsupportedEncodingException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final ParseException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "ParseException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final UnknownHostException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnknownHostException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IOException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IllegalStateException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "NullPointerException");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
mic.getMicListener().onError(Speaker.ERROR_NETWORK);
} finally {
closeConnection();
}
}
};
httpThread.start();
}
/**
* Check the result of the validation request
*
* @param statusPair the {@link Pair} with the first parameter denoting success and the
* second the {@link OperationStatus} object
* @param statusUrl url to recheck the status if required
*/
private void checkResult(@NonNull final Pair statusPair,
@NonNull final String statusUrl) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkResult");
}
if (statusPair.first && statusPair.second != null && statusPair.second.getProcessingResult() != null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "checkResult: have data");
}
switch (Speaker.Status.getStatus(statusPair.second.getStatus())) {
case SUCCEEDED:
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTIFY, true, Speaker.Confidence.getConfidence(
statusPair.second.getProcessingResult().getConfidence()));
break;
case FAILED:
case UNDEFINED:
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTIFY, true, Speaker.Confidence.ERROR);
break;
case NOTSTARTED:
case RUNNING:
if (retry) {
retry = false;
final TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if (DEBUG) {
MyLog.i(CLS_NAME, "timerTask: fetching operation status");
}
checkResult(new FetchIDOperation(mic.getContext(), apiKey, statusUrl)
.getStatus(), statusUrl);
}
};
new Timer().schedule(timerTask, FETCH_DELAY_EXTENDED);
} else {
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTIFY, true, Speaker.Confidence.ERROR);
}
break;
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "statusPair false");
}
NotificationHelper.createIdentificationNotification(mic.getContext(),
Condition.CONDITION_IDENTIFY, true, Speaker.Confidence.ERROR);
}
}
@Override
public void onBufferReceived(final int bufferReadResult, final byte[] buffer) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onBufferReceived");
}
}
@Override
public void onError(final int error) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onError");
}
}
@Override
public void onPauseDetected() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onPauseDetected");
}
}
@Override
public void onRecordingStarted() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingStarted");
}
}
@Override
public void onRecordingEnded() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onRecordingEnded");
}
}
@Override
public void onFileWriteComplete(final boolean success) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onFileWriteComplete: " + success);
}
}
private void closeConnection() {
if (DEBUG) {
MyLog.i(CLS_NAME, "closeConnection");
}
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
if (urlConnection != null) {
try {
urlConnection.disconnect();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/WolframAlphaCognitive.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha;
import android.net.ParseException;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.resolve.ResolveWolframAlpha;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.resolve.WolframAlphaRequest;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.resolve.WolframAlphaResponse;
import ai.saiy.android.configuration.WolframConfiguration;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
public class WolframAlphaCognitive {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = WolframAlphaCognitive.class.getSimpleName();
private static final String REINTERPRET = "&reinterpret=true";
private static final String FORMAT = "&format=plaintext";
private static final String PARSE_TIMEOUT = "&parsetimeout=10";
private static final String SCAN_TIMEOUT = "&scantimeout=2";
private static final String FORMAT_TIMEOUT = "&formattimeout=5";
private static final String APP_ID = "&appid=";
private static final String IGNORE_CASE = "&ignorecase=true";
private static final String ENCODING = "utf-8";
private static final String POD_INDEX = "&podindex=1,2,3";
private static final String LAT_LONG = "&latlong=";
// TODO - need to contact W|A
private static final String SIGNATURE = "sig";
private String response;
private HttpURLConnection urlConnection;
/**
* Perform a synchronous validation request to Wolfram Alpha
*
* @param text the text to be queried
* @return true if the request passed validation, false otherwise
*/
public boolean validate(@NonNull final String text) {
if (DEBUG) {
MyLog.i(CLS_NAME, "validate");
}
final long then = System.nanoTime();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
try {
final String urlString = WolframConfiguration.VALIDATE_URL + URLEncoder.encode(text, ENCODING)
+ APP_ID + WolframConfiguration.WOLFRAM_APP_ID + FORMAT + IGNORE_CASE;
if (DEBUG) {
MyLog.i(CLS_NAME, "url:" + urlString);
}
urlConnection = (HttpURLConnection) new URL(urlString).openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoInput(true);
urlConnection.connect();
final int responseCode = urlConnection.getResponseCode();
if (DEBUG) {
MyLog.d(CLS_NAME, "responseCode: " + responseCode);
}
if (responseCode != HttpURLConnection.HTTP_OK) {
if (DEBUG) {
MyLog.e(CLS_NAME, "error stream: "
+ UtilsString.streamToString(urlConnection.getErrorStream()));
}
} else {
response = UtilsString.streamToString(urlConnection.getInputStream());
if (DEBUG) {
MyLog.d(CLS_NAME, "response: " + response);
}
}
} catch (final MalformedURLException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "MalformedURLException");
e.printStackTrace();
}
} catch (final UnsupportedEncodingException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnsupportedEncodingException");
e.printStackTrace();
}
} catch (final ParseException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "ParseException");
e.printStackTrace();
}
} catch (final UnknownHostException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnknownHostException");
e.printStackTrace();
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IOException");
e.printStackTrace();
}
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IllegalStateException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
} finally {
closeConnection();
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
if (UtilsString.notNaked(response)) {
return new ResolveWolframAlpha().validate(response);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return false;
}
}
/**
* Perform a synchronous Wolfram Alpha request
*
* @param request the {@link WolframAlphaRequest} object
* @return a {@link Pair} with the first parameter donating success and the second the result
*/
public Pair execute(@NonNull final WolframAlphaRequest request) {
if (DEBUG) {
MyLog.i(CLS_NAME, "execute");
}
final long then = System.nanoTime();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
try {
final String urlString = WolframConfiguration.QUERY_URL
+ URLEncoder.encode(request.getQuery(), ENCODING) + APP_ID
+ WolframConfiguration.WOLFRAM_APP_ID + FORMAT + PARSE_TIMEOUT + SCAN_TIMEOUT
+ FORMAT_TIMEOUT + REINTERPRET + IGNORE_CASE;
if (DEBUG) {
MyLog.i(CLS_NAME, "url:" + urlString);
}
urlConnection = (HttpURLConnection) new URL(urlString).openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoInput(true);
urlConnection.connect();
final int responseCode = urlConnection.getResponseCode();
if (DEBUG) {
MyLog.d(CLS_NAME, "responseCode: " + responseCode);
}
if (responseCode != HttpURLConnection.HTTP_OK) {
if (DEBUG) {
MyLog.e(CLS_NAME, "error stream: "
+ UtilsString.streamToString(urlConnection.getErrorStream()));
}
} else {
response = UtilsString.streamToString(urlConnection.getInputStream());
if (DEBUG) {
MyLog.i(CLS_NAME, "response: " + response);
}
}
} catch (final MalformedURLException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "MalformedURLException");
e.printStackTrace();
}
} catch (final UnsupportedEncodingException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnsupportedEncodingException");
e.printStackTrace();
}
} catch (final ParseException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "ParseException");
e.printStackTrace();
}
} catch (final UnknownHostException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "UnknownHostException");
e.printStackTrace();
}
} catch (final IOException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IOException");
e.printStackTrace();
}
} catch (final IllegalStateException e) {
if (DEBUG) {
MyLog.e(CLS_NAME, "IllegalStateException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
} finally {
closeConnection();
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
if (UtilsString.notNaked(response)) {
return new ResolveWolframAlpha().resolve(request, response);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "response: failed");
}
return new Pair<>(false, null);
}
}
private void closeConnection() {
if (urlConnection != null) {
try {
urlConnection.disconnect();
} catch (final Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Alternative.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Text;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "alternative")
public class Alternative {
@Attribute(name = "score", required = false)
private double score;
@Attribute(name = "level", required = false)
private String level;
private String text;
public Alternative() {
}
public Alternative(@Attribute(name = "level", required = false) final String level,
@Attribute(name = "score", required = false) final double score,
@Text final String text) {
this.level = level;
this.score = score;
this.text = text;
}
public String getLevel() {
return level;
}
public double getScore() {
return score;
}
@Text
public String getText() {
return text;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Assumption.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "assumption")
public class Assumption {
@Attribute(name = "type")
private String type;
@Attribute(name = "word", required = false)
private String word;
@Attribute(name = "template", required = false)
private String template;
@Attribute(name = "count")
private long count;
@Attribute(name = "desc", required = false)
private String desc;
@Attribute(name = "current", required = false)
private long current;
@ElementList(inline = true, name = "value")
private List values;
public Assumption() {
}
public Assumption(@Attribute(name = "count") final long count,
@Attribute(name = "template", required = false) final String template,
@Attribute(name = "type") final String type,
@ElementList(inline = true, name = "value") final List values,
@Attribute(name = "word", required = false) final String word,
@Attribute(name = "current", required = false) final long current,
@Attribute(name = "desc", required = false) final String desc) {
this.count = count;
this.template = template;
this.type = type;
this.values = values;
this.word = word;
this.current = current;
this.desc = desc;
}
public long getCurrent() {
return current;
}
public String getDesc() {
return desc;
}
public long getCount() {
return count;
}
public String getTemplate() {
return template;
}
public String getType() {
return type;
}
public List getValues() {
return values;
}
public String getWord() {
return word;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Assumptions.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "assumptions")
public class Assumptions {
@Attribute(name = "count")
private long count;
@ElementList(inline = true, name = "assumption")
private List assumptions;
public Assumptions() {
}
public Assumptions(@ElementList(inline = true, name = "assumption") final List assumptions,
@Attribute(name = "count") final long count) {
this.assumptions = assumptions;
this.count = count;
}
public long getCount() {
return count;
}
public List getAssumptions() {
return assumptions;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Definition.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "definition")
public class Definition {
@Attribute(name = "word", required = false)
private String word;
@Attribute(name = "desc", required = false)
private String desc;
public Definition() {
}
public Definition(@Attribute(name = "desc", required = false) final String desc,
@Attribute(name = "word", required = false) final String word) {
this.desc = desc;
this.word = word;
}
public String getDesc() {
return desc;
}
public String getWord() {
return word;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Definitions.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "definitions")
public class Definitions {
@Attribute(name = "count")
private long count;
@ElementList(inline = true, name = "definition")
private List definitions;
public Definitions() {
}
public Definitions(@Attribute(name = "count") final long count,
@ElementList(inline = true, name = "definition") final List definitions) {
this.count = count;
this.definitions = definitions;
}
public long getCount() {
return count;
}
public List getDefinitions() {
return definitions;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Info.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "info")
public class Info {
@Element(name = "link", required = false)
private Link link;
@Element(name = "units", required = false)
private Units units;
public Info() {
}
public Info(@Element(name = "link", required = false) final Link link,
@Element(name = "units", required = false) final Units units) {
this.link = link;
this.units = units;
}
public boolean hasUnits() {
return units != null;
}
public Units getUnits() {
return units;
}
public boolean hasLink() {
return link != null;
}
public Link getLink() {
return link;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Infos.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "infos")
public class Infos {
@Attribute(name = "count")
private long count;
@ElementList(inline = true, name = "info")
private List info;
public Infos() {
}
public Infos(@Attribute(name = "count") final long count,
@ElementList(inline = true, name = "info") final List info) {
this.count = count;
this.info = info;
}
public long getCount() {
return count;
}
public List getInfo() {
return info;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Link.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "link")
public class Link {
@Attribute(name = "url")
private String url;
@Attribute(name = "text")
private String text;
public Link() {
}
public Link(@Attribute(name = "text") final String text,
@Attribute(name = "url") final String url) {
this.text = text;
this.url = url;
}
public String getText() {
return text;
}
public String getUrl() {
return url;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Pod.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
import ai.saiy.android.utils.UtilsList;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "pod")
public class Pod {
public static final String ID_INPUT = "Input";
public static final String ID_RESULT = "Result";
@Attribute(name = "title")
private String title;
@Attribute(name = "scanner")
private String scanner;
@Attribute(name = "id")
private String id;
@Attribute(name = "position")
private long position;
@Attribute(name = "primary", required = false)
private boolean primary;
@Attribute(name = "error")
private boolean error;
@Attribute(name = "numsubpods")
private long numsubpods;
@ElementList(inline = true, name = "subpods")
private List subpods;
@Element(name = "states", required = false)
private States states;
@Element(name = "infos", required = false)
private Infos infos;
@Element(name = "definitions", required = false)
private Definitions definitions;
public Pod() {
}
public Pod(@Attribute(name = "error") final boolean error,
@Attribute(name = "primary") final boolean primary,
@Attribute(name = "title") final String title,
@Attribute(name = "scanner") final String scanner,
@Attribute(name = "id") final String id,
@Attribute(name = "position") final long position,
@Attribute(name = "numsubpods") final long numsubpods,
@ElementList(inline = true, name = "subpods") final List subpods,
@Element(name = "states") final States states,
@Element(name = "infos") final Infos infos,
@Element(name = "definitions", required = false) final Definitions definitions) {
this.error = error;
this.title = title;
this.scanner = scanner;
this.id = id;
this.position = position;
this.numsubpods = numsubpods;
this.subpods = subpods;
this.primary = primary;
this.states = states;
this.infos = infos;
this.definitions = definitions;
}
public boolean hasDefinitions() {
return definitions != null;
}
public Definitions getDefinitions() {
return definitions;
}
public boolean hasInfos() {
return infos != null;
}
public Infos getInfos() {
return infos;
}
public boolean hasStates() {
return states != null;
}
public States getStates() {
return states;
}
public boolean isPrimary() {
return primary;
}
public boolean isError() {
return error;
}
public String getId() {
return id;
}
public long getNumsubpods() {
return numsubpods;
}
public long getPosition() {
return position;
}
public String getScanner() {
return scanner;
}
public boolean hasSubPods() {
return UtilsList.notNaked(subpods);
}
public List getSubPods() {
return subpods;
}
public String getTitle() {
return title;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/QueryResult.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
import ai.saiy.android.utils.UtilsList;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "queryresult") //, strict = false
public class QueryResult {
@Attribute(name = "success")
private boolean success;
@Attribute(name = "error")
private boolean error;
@Attribute(name = "nodata", required = false)
private boolean noData;
@Attribute(name = "numpods")
private int numpods;
@Attribute(name = "datatypes")
private String datatypes;
@Attribute(name = "timedout")
private String timedout;
@Attribute(name = "timedoutpods")
private String timedoutpods;
@Attribute(name = "timing")
private double timing;
@Attribute(name = "parsetimedout")
private boolean parsetimedout;
@Attribute(name = "parsetiming")
private double parsetiming;
@Attribute(name = "recalculate")
private String recalculate;
@Attribute(name = "id")
private String id;
@Attribute(name = "host")
private String host;
@Attribute(name = "server")
private int server;
@Attribute(name = "related")
private String related;
@Attribute(name = "version")
private double version;
@ElementList(inline = true, name = "pod")
private List pods;
@Element(name = "assumptions", required = false)
private Assumptions assumptions;
@Element(name = "sources", required = false)
private Sources sources;
@Element(name = "warnings", required = false)
private Warnings warnings;
public QueryResult() {
}
public QueryResult(@Attribute(name = "datatypes") final String datatypes,
@Attribute(name = "success") final boolean success,
@Attribute(name = "nodata") final boolean noData,
@Attribute(name = "error") final boolean error,
@Attribute(name = "numpods") final int numpods,
@Attribute(name = "timedout") final String timedout,
@Attribute(name = "timedoutpods") final String timedoutpods,
@Attribute(name = "timing") final double timing,
@Attribute(name = "parsetimedout") final boolean parsetimedout,
@Attribute(name = "parsetiming") final double parsetiming,
@Attribute(name = "recalculate") final String recalculate,
@Attribute(name = "id") final String id,
@Attribute(name = "host") final String host,
@Attribute(name = "server") final int server,
@Attribute(name = "related") final String related,
@Attribute(name = "version") final double version,
@ElementList(inline = true, name = "pod") final List pods,
@Element(name = "assumptions") final Assumptions assumptions,
@Element(name = "sources", required = false) final Sources sources,
@Element(name = "warnings", required = false) final Warnings warnings) {
this.datatypes = datatypes;
this.success = success;
this.noData = noData;
this.error = error;
this.numpods = numpods;
this.timedout = timedout;
this.timedoutpods = timedoutpods;
this.timing = timing;
this.parsetimedout = parsetimedout;
this.parsetiming = parsetiming;
this.recalculate = recalculate;
this.id = id;
this.host = host;
this.server = server;
this.related = related;
this.version = version;
this.pods = pods;
this.assumptions = assumptions;
this.sources = sources;
this.warnings = warnings;
}
public boolean noData() {
return noData;
}
public Warnings getWarnings() {
return warnings;
}
public Sources getSources() {
return sources;
}
public boolean hasSources() {
return sources != null;
}
public Assumptions getAssumptions() {
return assumptions;
}
public boolean hasAssumptions() {
return assumptions != null;
}
public String getDatatypes() {
return datatypes;
}
public boolean isError() {
return error;
}
public String getHost() {
return host;
}
public String getId() {
return id;
}
public int getNumpods() {
return numpods;
}
public boolean isParsetimedout() {
return parsetimedout;
}
public double getParsetiming() {
return parsetiming;
}
public boolean hasPods() {
return UtilsList.notNaked(pods);
}
public List getPods() {
return pods;
}
public String getRecalculate() {
return recalculate;
}
public String getRelated() {
return related;
}
public int getServer() {
return server;
}
public boolean isSuccess() {
return success;
}
public String getTimedout() {
return timedout;
}
public String getTimedoutpods() {
return timedoutpods;
}
public double getTiming() {
return timing;
}
public double getVersion() {
return version;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Reinterpret.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "reinterpret")
public class Reinterpret {
@Attribute(name = "text", required = false)
private String text;
@Attribute(name = "new", required = false)
private String replaced;
@Attribute(name = "score", required = false)
private double score;
@Attribute(name = "level", required = false)
private String level;
@ElementList(inline = true, name = "alternative", required = false)
private List alternatives;
public Reinterpret() {
}
public Reinterpret(@Attribute(name = "level", required = false) final String level,
@Attribute(name = "new", required = false) final String replaced,
@Attribute(name = "score", required = false) final double score,
@Attribute(name = "text", required = false) final String text,
@ElementList(inline = true, name = "alternative", required = false)
final List alternatives) {
this.level = level;
this.replaced = replaced;
this.score = score;
this.text = text;
this.alternatives = alternatives;
}
public boolean haveAlternatives() {
return alternatives != null;
}
public List getAlternatives() {
return alternatives;
}
public String getLevel() {
return level;
}
public String getReplaced() {
return replaced;
}
public double getScore() {
return score;
}
public String getText() {
return text;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Source.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "source")
public class Source {
@Attribute(name = "url")
private String url;
@Attribute(name = "text")
private String text;
public Source() {
}
public Source(@Attribute(name = "text") final String text,
@Attribute(name = "url") final String url) {
this.text = text;
this.url = url;
}
public String getText() {
return text;
}
public String getUrl() {
return url;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Sources.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "sources")
public class Sources {
@ElementList(inline = true, name = "source")
private List sources;
@Attribute(name = "count")
private long count;
public Sources() {
}
public Sources(@Attribute(name = "count") final long count,
@ElementList(inline = true, name = "source") final List sources) {
this.count = count;
this.sources = sources;
}
public long getCount() {
return count;
}
public List getSources() {
return sources;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/SpellCheck.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "spellcheck")
public class SpellCheck {
@Attribute(name = "word")
private String word;
@Attribute(name = "suggestion")
private String suggestion;
@Attribute(name = "text")
private String text;
public SpellCheck() {
}
public SpellCheck(@Attribute(name = "suggestion") final String suggestion,
@Attribute(name = "text") final String text,
@Attribute(name = "word") final String word) {
this.suggestion = suggestion;
this.text = text;
this.word = word;
}
public String getSuggestion() {
return suggestion;
}
public String getText() {
return text;
}
public String getWord() {
return word;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/State.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "state")
public class State {
@Attribute(name = "name")
private String name;
@Attribute(name = "input")
private String input;
public State() {
}
public State(@Attribute(name = "name") final String input,
@Attribute(name = "input") final String name) {
this.input = input;
this.name = name;
}
public String getInput() {
return input;
}
public String getName() {
return name;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/StateList.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "statelist")
public class StateList {
@Attribute(name = "count")
private long count;
@Attribute(name = "value", required = false)
private String value;
@Attribute(name = "delimiters", required = false)
private String delimiters;
@ElementList(inline = true, name = "state")
private List state;
public StateList() {
}
public StateList(@Attribute(name = "count") final long count,
@Attribute(name = "delimiters", required = false) final String delimiters,
@ElementList(inline = true, name = "state") final List state,
@Attribute(name = "value", required = false) final String value) {
this.count = count;
this.delimiters = delimiters;
this.state = state;
this.value = value;
}
public long getCount() {
return count;
}
public String getDelimiters() {
return delimiters;
}
public List getState() {
return state;
}
public String getValue() {
return value;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/States.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "states")
public class States {
@Attribute(name = "count")
private long count;
@Element(name = "statelist", required = false)
private StateList stateList;
@ElementList(inline = true, name = "state")
private List state;
public States() {
}
public States(@Attribute(name = "count") final long count,
@ElementList(inline = true, name = "state") final List state,
@Element(name = "statelist", required = false) final StateList stateList) {
this.count = count;
this.state = state;
this.stateList = stateList;
}
public boolean hasStateList() {
return stateList != null;
}
public StateList getStateList() {
return stateList;
}
public long getCount() {
return count;
}
public List getState() {
return state;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/SubPod.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "subpod")
public class SubPod {
public static final String DATA_UNAVAILABLE = "(data not available)";
@Attribute(name = "title")
private String title;
@Element(name = "plaintext", required = false)
private String plaintext;
@Element(name = "imagesource", required = false)
private String imagesource;
public SubPod() {
}
public SubPod(@Element(name = "plaintext") final String plaintext,
@Attribute(name = "title") final String title,
@Element(name = "imagesource", required = false) final String imagesource) {
this.plaintext = plaintext;
this.title = title;
this.imagesource = imagesource;
}
public String getImagesource() {
return imagesource;
}
public String getPlaintext() {
return plaintext;
}
public String getTitle() {
return title;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Unit.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "unit")
public class Unit {
@Attribute(name = "short")
private String shortUnit;
@Attribute(name = "long")
private String longUnit;
public Unit() {
}
public Unit(@Attribute(name = "long") final String longUnit,
@Attribute(name = "short") final String shortUnit) {
this.longUnit = longUnit;
this.shortUnit = shortUnit;
}
public String getLongUnit() {
return longUnit;
}
public String getShortUnit() {
return shortUnit;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Units.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.List;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "units")
public class Units {
@Attribute(name = "count")
private long count;
@ElementList(inline = true, name = "unit")
private List unit;
public Units() {
}
public Units(@Attribute(name = "count") final long count,
@ElementList(inline = true, name = "unit") final List unit) {
this.count = count;
this.unit = unit;
}
public long getCount() {
return count;
}
public List getUnit() {
return unit;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/ValidateQueryResult.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 09/08/2016.
*/
@Root(name = "validatequeryresult", strict = false)
public class ValidateQueryResult {
@Attribute(name = "success")
private final boolean success;
@Attribute(name = "error")
private boolean error;
public ValidateQueryResult(@Attribute(name = "error") final boolean error,
@Attribute(name = "success") final boolean success) {
this.error = error;
this.success = success;
}
public boolean isError() {
return error;
}
public boolean isSuccess() {
return success;
}
public boolean passedValidation() {
return success && !error;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Value.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 06/08/2016.
*/
@Root(name = "value")
public class Value {
@Attribute(name = "name")
private String name;
@Attribute(name = "desc")
private String desc;
@Attribute(name = "input")
private String input;
@Attribute(name = "word", required = false)
private String word;
@Attribute(name = "valid", required = false)
private boolean valid;
public Value() {
}
public Value(@Attribute(name = "input") final String input,
@Attribute(name = "name") final String name,
@Attribute(name = "desc") final String desc,
@Attribute(name = "word") final String word,
@Attribute(name = "valid", required = false) final boolean valid) {
this.input = input;
this.name = name;
this.desc = desc;
this.word = word;
this.valid = valid;
}
public boolean isValid() {
return valid;
}
public String getWord() {
return word;
}
public String getDesc() {
return desc;
}
public String getInput() {
return input;
}
public String getName() {
return name;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/parse/Warnings.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
/**
* Created by benrandall76@gmail.com on 07/08/2016.
*/
@Root(name = "warnings")
public class Warnings {
@Attribute(name = "count")
private long count;
@Element(name = "reinterpret", required = false)
private Reinterpret reinterpret;
@Element(name = "spellcheck", required = false)
private SpellCheck spellcheck;
public Warnings() {
}
public Warnings(@Attribute(name = "count") final long count,
@Element(name = "reinterpret", required = false) final Reinterpret reinterpret,
@Element(name = "spellcheck", required = false) final SpellCheck spellcheck) {
this.count = count;
this.reinterpret = reinterpret;
this.spellcheck = spellcheck;
}
public boolean hasSpellCheck() {
return spellcheck != null;
}
public SpellCheck getSpellcheck() {
return spellcheck;
}
public long getCount() {
return count;
}
public boolean hasReinterpret() {
return reinterpret != null;
}
public Reinterpret getReinterpret() {
return reinterpret;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/resolve/ResolveWolframAlpha.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.resolve;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Pair;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import java.util.List;
import java.util.regex.Pattern;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.Assumption;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.Assumptions;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.Pod;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.QueryResult;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.Source;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.Sources;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.State;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.States;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.SubPod;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.ValidateQueryResult;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.Value;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsList;
import ai.saiy.android.utils.UtilsString;
/**
* Created by benrandall76@gmail.com on 08/08/2016.
*/
public class ResolveWolframAlpha {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = ResolveWolframAlpha.class.getSimpleName();
private static final String DELIMITER = "\\s\\|";
private List podList;
private WolframAlphaResponse wolframAlphaResponse = null;
public boolean validate(@NonNull final String question) {
if (DEBUG) {
MyLog.i(CLS_NAME, "validate");
}
final Serializer serializer = new Persister();
try {
final ValidateQueryResult result = serializer.read(ValidateQueryResult.class, question, false);
return result.passedValidation();
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
}
return false;
}
public Pair resolve(@NonNull final WolframAlphaRequest request,
@NonNull final String xmlResponse) {
if (DEBUG) {
MyLog.i(CLS_NAME, "resolve");
}
final Serializer serializer = new Persister();
try {
final QueryResult queryResult = serializer.read(QueryResult.class, xmlResponse, false);
if (isSuccess(queryResult)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "result: " + queryResult.getId());
MyLog.i(CLS_NAME, "result: " + queryResult.getDatatypes());
MyLog.i(CLS_NAME, "result: " + queryResult.getHost());
MyLog.i(CLS_NAME, "result: " + queryResult.getRecalculate());
MyLog.i(CLS_NAME, "result: " + queryResult.getTimedout());
MyLog.i(CLS_NAME, "result: " + queryResult.getNumpods());
MyLog.i(CLS_NAME, "result: " + queryResult.getRelated());
MyLog.i(CLS_NAME, "result: " + queryResult.getTimedoutpods());
MyLog.i(CLS_NAME, "result: " + queryResult.isParsetimedout());
MyLog.i(CLS_NAME, "result: " + queryResult.getVersion());
MyLog.i(CLS_NAME, "result: " + queryResult.getTiming());
MyLog.i(CLS_NAME, "result: " + queryResult.getServer());
}
if (canResolveResponse(queryResult)) {
for (final Pod pod : podList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "pod: " + pod.getId());
MyLog.i(CLS_NAME, "pod: " + pod.getScanner());
MyLog.i(CLS_NAME, "pod: " + pod.getTitle());
MyLog.i(CLS_NAME, "pod: " + pod.getNumsubpods());
MyLog.i(CLS_NAME, "pod: " + pod.getPosition());
}
final List subPodList = pod.getSubPods();
for (final SubPod subPod : subPodList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "subPod: " + subPod.getTitle());
MyLog.i(CLS_NAME, "subPod: " + subPod.getPlaintext());
}
}
if (pod.hasStates()) {
final States states = pod.getStates();
final List stateList = states.getState();
if (DEBUG) {
MyLog.i(CLS_NAME, "states: " + states.getCount());
}
for (final State state : stateList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "state: " + state.getInput());
MyLog.i(CLS_NAME, "state: " + state.getName());
}
}
} else {
MyLog.i(CLS_NAME, "pod: no states");
}
}
if (queryResult.hasAssumptions()) {
final Assumptions assumptions = queryResult.getAssumptions();
if (DEBUG) {
MyLog.i(CLS_NAME, "assumptions: " + assumptions.getCount());
}
final List assumptionList = assumptions.getAssumptions();
for (final Assumption assumption : assumptionList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "assumption: " + assumption.getCount());
MyLog.i(CLS_NAME, "assumption: " + assumption.getTemplate());
MyLog.i(CLS_NAME, "assumption: " + assumption.getType());
MyLog.i(CLS_NAME, "assumption: " + assumption.getWord());
}
final List valueList = assumption.getValues();
for (final Value value : valueList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "value: " + value.getDesc());
MyLog.i(CLS_NAME, "value: " + value.getInput());
MyLog.i(CLS_NAME, "value: " + value.getName());
}
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "result: no assumptions");
}
}
if (queryResult.hasSources()) {
final Sources sources = queryResult.getSources();
if (DEBUG) {
MyLog.i(CLS_NAME, "sources: " + sources.getCount());
}
final List sourceList = sources.getSources();
for (final Source source : sourceList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "source: " + source.getText());
MyLog.i(CLS_NAME, "source: " + source.getUrl());
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "result: no sources");
}
}
resolveResponse(podList);
wolframAlphaResponse.setQueryResult(queryResult);
wolframAlphaResponse.setQuestion(request.getQuery());
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "result: can't resolve response");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "result: isSuccess: false");
}
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "Exception");
e.printStackTrace();
}
}
return new Pair<>(wolframAlphaResponse != null, wolframAlphaResponse);
}
private boolean isSuccess(@Nullable final QueryResult result) {
return result != null && result.isSuccess() && !result.isError() && !result.noData();
}
private boolean canResolveResponse(@NonNull final QueryResult result) {
if (DEBUG) {
MyLog.i(CLS_NAME, "canResolveResponse");
}
if (result.hasPods()) {
podList = result.getPods();
if (haveInputPod(podList)) {
if (haveResultPod(podList)) {
return true;
} else {
if (DEBUG) {
MyLog.d(CLS_NAME, "canResolveResponse: no result pod");
}
}
} else {
if (DEBUG) {
MyLog.d(CLS_NAME, "canResolveResponse: no input pod");
}
}
} else {
if (DEBUG) {
MyLog.d(CLS_NAME, "canResolveResponse: no pods");
}
}
return result.hasPods() && haveInputPod(result.getPods()) && haveResultPod(result.getPods());
}
private boolean haveResultPod(@NonNull final List podList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "haveResultPod");
}
String id;
for (final Pod pod : podList) {
id = pod.getId();
if (UtilsString.notNaked(id) && id.matches(Pod.ID_RESULT)
&& haveSubPodPlainText(pod)) {
return true;
}
}
return false;
}
private boolean haveInputPod(@NonNull final List podList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "haveInputPod");
}
String id;
for (final Pod pod : podList) {
id = pod.getId();
if (UtilsString.notNaked(id) && Pod.ID_INPUT.matches(Pattern.quote(id))
&& haveSubPodPlainText(pod)) {
return true;
}
}
return false;
}
private String getInputText(@NonNull final List podList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getInputText");
}
String id;
for (final Pod pod : podList) {
id = pod.getId();
if (Pod.ID_INPUT.matches(Pattern.quote(id))) {
return pod.getSubPods().get(0).getPlaintext();
}
}
return null;
}
private String getResultText(@NonNull final List podList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getResultText");
}
String id;
for (final Pod pod : podList) {
id = pod.getId();
if (Pod.ID_RESULT.matches(Pattern.quote(id))) {
return pod.getSubPods().get(0).getPlaintext();
}
}
return null;
}
private boolean haveSubPodPlainText(@NonNull final Pod pod) {
if (DEBUG) {
MyLog.i(CLS_NAME, "haveSubPodPlainText");
}
final List subPodList = pod.getSubPods();
return UtilsList.notNaked(subPodList) && UtilsString.notNaked(subPodList.get(0).getPlaintext())
&& !SubPod.DATA_UNAVAILABLE.matches(Pattern.quote(subPodList.get(0).getPlaintext()));
}
private void resolveResponse(@NonNull final List podList) {
if (DEBUG) {
MyLog.i(CLS_NAME, "resolveResponse");
}
wolframAlphaResponse = new WolframAlphaResponse();
final String input = getInputText(podList);
final String result = getResultText(podList);
if (DEBUG) {
MyLog.i(CLS_NAME, "input: " + input);
MyLog.i(CLS_NAME, "result: " + result);
}
wolframAlphaResponse.setInterpretation(formatString(input));
wolframAlphaResponse.setResult(formatString(result));
}
private String formatString(@NonNull final String toFormat) {
return toFormat.replaceAll(DELIMITER, ",").replaceAll("\\s+", " ").trim() + ". ";
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/resolve/WolframAlphaRequest.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.resolve;
import android.support.annotation.NonNull;
/**
* Class to package up a Wolfram Alpha request
*
* Created by benrandall76@gmail.com on 09/08/2016.
*/
public class WolframAlphaRequest {
public enum Type {
GENERAL,
MATHEMATICA,
AUDIO,
IMAGERY
}
private String query;
private Type type;
private boolean autoShow;
public WolframAlphaRequest() {
}
/**
* Constructor
*
* @param type one of {@link Type}
* @param query the string of the query to request
* @param autoShow whether or not to show the results once they are complete
*/
public WolframAlphaRequest(@NonNull final Type type, @NonNull final String query,
final boolean autoShow) {
this.autoShow = autoShow;
this.query = query;
this.type = type;
}
public boolean isAutoShow() {
return autoShow;
}
public void setAutoShow(final boolean autoShow) {
this.autoShow = autoShow;
}
public String getQuery() {
return query;
}
public void setQuery(@NonNull final String query) {
this.query = query;
}
public Type getType() {
return type;
}
public void setType(@NonNull final Type type) {
this.type = type;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/knowledge/provider/wolframalpha/resolve/WolframAlphaResponse.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.knowledge.provider.wolframalpha.resolve;
import android.support.annotation.NonNull;
import ai.saiy.android.cognitive.knowledge.provider.wolframalpha.parse.QueryResult;
/**
* Class to package a response from Wolfram Alpha
*
* Created by benrandall76@gmail.com on 09/08/2016.
*/
public class WolframAlphaResponse {
private QueryResult queryResult;
private String question;
private String interpretation;
private String result;
public WolframAlphaResponse() {
}
/**
* Constructor
*
* @param queryResult a {@link QueryResult} object containing the full response
* @param question the question that was asked
* @param interpretation the interpretation of the question by Wolfram Alpha
* @param result the results supplied by Wolfram Alpha
*/
public WolframAlphaResponse(@NonNull final QueryResult queryResult, @NonNull final String question,
@NonNull final String interpretation, @NonNull final String result) {
this.interpretation = interpretation;
this.queryResult = queryResult;
this.question = question;
this.result = result;
}
public void setInterpretation(final String interpretation) {
this.interpretation = interpretation;
}
public void setQueryResult(final QueryResult queryResult) {
this.queryResult = queryResult;
}
public void setQuestion(final String question) {
this.question = question;
}
public void setResult(final String result) {
this.result = result;
}
public String getInterpretation() {
return interpretation;
}
public QueryResult getQueryResult() {
return queryResult;
}
public String getQuestion() {
return question;
}
public String getResult() {
return result;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/motion/provider/google/Motion.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.motion.provider.google;
/**
* Created by benrandall76@gmail.com on 05/07/2016.
*/
public class Motion {
protected transient static final int CONFIDENCE_THRESHOLD = 75;
private final int type;
private final int confidence;
private final long time;
public Motion(final int type, final int confidence, final long time) {
this.confidence = confidence;
this.type = type;
this.time = time;
}
public int getConfidence() {
return confidence;
}
public int getType() {
return type;
}
public long getTime() {
return time;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/motion/provider/google/MotionHelper.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.motion.provider.google;
import android.content.Context;
import android.support.annotation.NonNull;
import com.google.android.gms.location.DetectedActivity;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import ai.saiy.android.service.helper.LocalRequest;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
/**
* Helper class to store the most recent ActivityRecognition event in the user's shared preferences
*
* Created by benrandall76@gmail.com on 15/08/2016.
*/
public class MotionHelper {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = MotionHelper.class.getSimpleName();
/**
* Check if we have a recent {@link Motion} object stored
*
* @param ctx the application context
* @return true if a {@link Motion} is stored
*/
public static boolean haveMotion(@NonNull final Context ctx) {
return SPH.getMotion(ctx) != null;
}
/**
* Store the {@link Motion} object in the shared preferences
*
* @param ctx the application context
* @param motion {@link Motion} object
*/
public static void setMotion(@NonNull final Context ctx, @NonNull final Motion motion) {
final String gsonString = new GsonBuilder().disableHtmlEscaping().create().toJson(motion);
if (DEBUG) {
MyLog.i(CLS_NAME, "setMotion: gsonString: " + gsonString);
}
SPH.setMotion(ctx, gsonString);
reactMotion(ctx, motion);
}
/**
* Check if we need to react to the detected ActivityRecognition type.
*
* @param ctx the application context
* @param motion the detection {@link Motion} object
*/
private static void reactMotion(@NonNull final Context ctx, @NonNull final Motion motion) {
if (DEBUG) {
MyLog.i(CLS_NAME, "reactMotion");
}
switch (motion.getType()) {
case DetectedActivity.WALKING:
break;
case DetectedActivity.IN_VEHICLE:
if (DEBUG) {
MyLog.i(CLS_NAME, "reactMotion: IN_VEHICLE");
}
if (SPH.getHotwordDriving(ctx)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "reactMotion: IN_VEHICLE: enabled");
}
final LocalRequest request = new LocalRequest(ctx);
request.prepareDefault(LocalRequest.ACTION_START_HOTWORD, null);
request.execute();
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "reactMotion: IN_VEHICLE: disabled");
}
}
break;
case DetectedActivity.ON_BICYCLE:
break;
case DetectedActivity.ON_FOOT:
break;
case DetectedActivity.RUNNING:
break;
case DetectedActivity.STILL:
break;
case DetectedActivity.TILTING:
break;
case DetectedActivity.UNKNOWN:
break;
default:
break;
}
}
/**
* Get the {@link Motion} we have stored
*
* @param ctx the application context
* @return the {@link Motion} object or a created one if none was present
*/
public static Motion getMotion(@NonNull final Context ctx) {
final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
final Motion motion;
if (haveMotion(ctx)) {
try {
motion = gson.fromJson(SPH.getMemory(ctx), Motion.class);
if (DEBUG) {
MyLog.i(CLS_NAME, "motion: " + gson.toJson(motion));
}
return motion;
} catch (final JsonSyntaxException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "motion: JsonSyntaxException");
e.printStackTrace();
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "motion: NullPointerException");
e.printStackTrace();
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "motion: Exception");
e.printStackTrace();
}
}
}
return getUnknown();
}
/**
* No {@link Motion} has been stored, so return an empty {@link Motion} object that can be identified as unknown.
*
* @return a constructed {@link Motion} object
*/
private static Motion getUnknown() {
return new Motion(DetectedActivity.UNKNOWN, 0, 0L);
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/motion/provider/google/MotionIntentService.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.motion.provider.google;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
import java.util.Set;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
/**
* Class to handle callbacks from the ActivityRecognition API.
*
* If the confidence scores are high, the activity is stored, so we can understand the context of what the
* user is doing elsewhere in the application.
*
* Created by benrandall76@gmail.com on 05/07/2016.
*/
public class MotionIntentService extends IntentService {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = MotionIntentService.class.getSimpleName();
protected static final long UPDATE_INTERVAL = 600000L;
protected static final int REQUEST_CODE = 55;
private long then;
/**
* Constructor
*/
public MotionIntentService() {
super(MotionIntentService.class.getSimpleName());
}
@Override
public void onCreate() {
if (DEBUG) {
MyLog.i(CLS_NAME, "onCreate");
}
then = System.nanoTime();
super.onCreate();
}
@Override
protected void onHandleIntent(final Intent intent) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onHandleIntent");
}
if (SPH.getMotionEnabled(getApplicationContext())) {
if (intent != null) {
if (DEBUG) {
examineIntent(intent);
}
if (ActivityRecognitionResult.hasResult(intent)) {
final Motion motion = extractMotion(intent);
if (motion != null) {
MotionHelper.setMotion(getApplicationContext(), motion);
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onHandleIntent: motion null: ignoring");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onHandleIntent: no ActivityRecognition results");
}
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "onHandleIntent: intent: null");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "onHandleIntent: user has switched off. Don't store.");
}
}
}
/**
* Store the most recent user activity for use elsewhere in the application.
*
* @param intent which should contain an {@link ActivityRecognitionResult}
* @return a {@link Motion} object
*/
private Motion extractMotion(final Intent intent) {
if (DEBUG) {
MyLog.i(CLS_NAME, "extractMotion");
}
final ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
if (result != null) {
final DetectedActivity detectedActivity = result.getMostProbableActivity();
if (detectedActivity != null) {
if (DEBUG) {
logActivity(detectedActivity);
}
final int confidence = detectedActivity.getConfidence();
if (confidence > Motion.CONFIDENCE_THRESHOLD) {
switch (detectedActivity.getType()) {
case DetectedActivity.WALKING:
case DetectedActivity.IN_VEHICLE:
case DetectedActivity.ON_BICYCLE:
case DetectedActivity.ON_FOOT:
case DetectedActivity.RUNNING:
case DetectedActivity.STILL:
return new Motion(detectedActivity.getType(), confidence, result.getTime());
case DetectedActivity.TILTING:
case DetectedActivity.UNKNOWN:
default:
break;
}
} else {
if (DEBUG) {
MyLog.v(CLS_NAME, "detectedActivity: ignoring low confidence");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "detectedActivity: null");
}
}
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "detectedActivity: ActivityRecognitionResult: null");
}
}
return null;
}
/**
* Logging only
*
* @param detectedActivity the {@link DetectedActivity}
*/
private void logActivity(final DetectedActivity detectedActivity) {
MyLog.v(CLS_NAME, "detectedActivity: confidence: " + detectedActivity.getConfidence());
switch (detectedActivity.getType()) {
case DetectedActivity.WALKING:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.WALKING");
break;
case DetectedActivity.IN_VEHICLE:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.IN_VEHICLE");
break;
case DetectedActivity.ON_BICYCLE:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.ON_BICYCLE");
break;
case DetectedActivity.ON_FOOT:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.ON_FOOT");
break;
case DetectedActivity.RUNNING:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.RUNNING");
break;
case DetectedActivity.STILL:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.STILL");
break;
case DetectedActivity.TILTING:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.TILTING");
break;
case DetectedActivity.UNKNOWN:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.UNKNOWN");
break;
default:
MyLog.i(CLS_NAME, "detectedActivity: DetectedActivity.default");
break;
}
}
/**
* For debugging the intent extras
*
* @param intent containing potential extras
*/
private void examineIntent(@NonNull final Intent intent) {
if (DEBUG) {
MyLog.i(CLS_NAME, "examineIntent");
}
final Bundle bundle = intent.getExtras();
if (bundle != null) {
final Set keys = bundle.keySet();
for (final String key : keys) {
if (DEBUG) {
MyLog.v(CLS_NAME, "examineIntent: " + key + " ~ " + bundle.get(key));
}
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (DEBUG) {
MyLog.i(CLS_NAME, "onDestroy");
MyLog.getElapsed(CLS_NAME, then);
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/cognitive/motion/provider/google/MotionRecognition.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.cognitive.motion.provider.google;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.ActivityRecognition;
import com.google.android.gms.security.ProviderInstaller;
import ai.saiy.android.utils.MyLog;
/**
* Class to setup the ActivityRecognition API and register the {@link MotionIntentService} for callbacks.
*
* This must only survive as long as {@link ai.saiy.android.service.SelfAware} is running, so must
* be destroyed as per its lifecycle.
*
* Created by benrandall76@gmail.com on 06/07/2016.
*/
public class MotionRecognition implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
ResultCallback {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = MotionRecognition.class.getSimpleName();
private PendingIntent pendingIntent;
private GoogleApiClient activityClient;
/**
* Prepare the Activity Recognition API for use.
*
* @param ctx the application context
*/
public void prepare(@NonNull final Context ctx) {
final GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
final int connectionResult = apiAvailability.isGooglePlayServicesAvailable(ctx);
switch (connectionResult) {
case ConnectionResult.SUCCESS:
activityClient = new GoogleApiClient.Builder(ctx).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).addApi(ActivityRecognition.API).build();
pendingIntent = PendingIntent.getService(ctx, MotionIntentService.REQUEST_CODE,
new Intent(ctx, MotionIntentService.class),
PendingIntent.FLAG_UPDATE_CURRENT);
try {
ProviderInstaller.installIfNeededAsync(ctx, new ProviderInstaller.ProviderInstallListener() {
@Override
public void onProviderInstalled() {
if (DEBUG) {
MyLog.i(CLS_NAME, "prepare: play services onProviderInstalled");
}
}
@Override
public void onProviderInstallFailed(final int errorCode, final Intent intent) {
if (DEBUG) {
MyLog.w(CLS_NAME, "prepare: play services onProviderInstallFailed");
}
if (apiAvailability.isUserResolvableError(errorCode)) {
if (DEBUG) {
MyLog.w(CLS_NAME, "prepare: play services onProviderInstallFailed");
}
apiAvailability.showErrorNotification(ctx, errorCode);
} else {
// TODO - unrecoverable
}
}
});
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "prepare: play services unavailable");
e.printStackTrace();
}
}
break;
default:
if (DEBUG) {
MyLog.w(CLS_NAME, "prepare: play services unavailable");
}
apiAvailability.showErrorNotification(ctx, connectionResult);
break;
}
}
/**
* Connect to receive activity recognition callbacks
*/
public void connect() {
if (activityClient != null) {
activityClient.connect();
}
}
@Override
public void onConnected(@Nullable final Bundle bundle) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onConnected");
}
if (activityClient != null && pendingIntent != null) {
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(activityClient,
MotionIntentService.UPDATE_INTERVAL, pendingIntent).setResultCallback(this);
}
}
@Override
public void onConnectionSuspended(final int i) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onConnectionSuspended");
}
}
@Override
public void onConnectionFailed(@NonNull final ConnectionResult connectionResult) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onConnectionFailed");
}
}
@Override
public void onResult(@NonNull final Status status) {
if (DEBUG) {
MyLog.i(CLS_NAME, "onResult");
}
}
/**
* Cancel any future callbacks and disconnect the client.
*/
public void destroy() {
if (activityClient != null && pendingIntent != null) {
try {
ActivityRecognition.ActivityRecognitionApi.removeActivityUpdates(activityClient, pendingIntent);
activityClient.disconnect();
} catch (final Exception e) {
if (DEBUG) {
MyLog.i(CLS_NAME, "destroy: Exception");
e.printStackTrace();
}
}
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/command/battery/Battery.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.command.battery;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import ai.saiy.android.command.helper.CC;
import ai.saiy.android.localisation.SaiyResources;
import ai.saiy.android.localisation.SupportedLanguage;
/**
* Helper class to direct any voice data to the correct localisation to resolve the command.
*
* Performance is key, so initialising localised resource Strings needs to be done as few times as
* possible, whenever possible.
*
* Created by benrandall76@gmail.com on 17/04/2016.
*/
public class Battery implements Callable>> {
private final SupportedLanguage sl;
private Object battery;
/**
* Constructor
*
* Used by the {@link Callable} to construct the data ready for {@link Callable#call()}
*
* @param sr the {@link SaiyResources}
* @param sl the {@link SupportedLanguage}
* @param voiceData the array of voice data
* @param confidence the array of confidence scores
*/
public Battery(@NonNull final SaiyResources sr, @NonNull final SupportedLanguage sl,
@NonNull final ArrayList voiceData, @NonNull float[] confidence) {
this.sl = sl;
switch (sl) {
case ENGLISH:
battery = new Battery_en(sr, sl, voiceData, confidence);
break;
case ENGLISH_US:
battery = new Battery_en(sr, sl, voiceData, confidence);
break;
default:
battery = new Battery_en(sr, SupportedLanguage.ENGLISH, voiceData, confidence);
break;
}
}
/**
* Constructor (used during a command)
*
* Used when we will be constructing and managing our own {@link SaiyResources} object
*
* @param sl the {@link SupportedLanguage}
*/
public Battery(@NonNull final SupportedLanguage sl) {
this.sl = sl;
}
/**
* Method to identify the required battery information
*
* @param ctx the application context
* @param voiceData ArrayList of voice data
* @return a {@link CommandBatteryValues} object containing the required parameters
*/
public CommandBatteryValues fetch(@NonNull final Context ctx, @NonNull final ArrayList voiceData) {
switch (sl) {
case ENGLISH:
return Battery_en.sortBattery(ctx, voiceData, sl);
case ENGLISH_US:
return Battery_en.sortBattery(ctx, voiceData, sl);
default:
return Battery_en.sortBattery(ctx, voiceData, SupportedLanguage.ENGLISH);
}
}
/**
* Used by the {@link Callable} in {@link Callable#call()}
*
* @return an array list containing {@link Pair} of {@link CC} and {@link Float} confidence scores
*/
public ArrayList> detectCallable() {
switch (sl) {
case ENGLISH:
return ((Battery_en) battery).detectCallable();
case ENGLISH_US:
return ((Battery_en) battery).detectCallable();
default:
return ((Battery_en) battery).detectCallable();
}
}
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
@Override
public ArrayList> call() throws Exception {
return detectCallable();
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/command/battery/BatteryInformation.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.command.battery;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.support.annotation.NonNull;
import java.math.BigDecimal;
import java.math.RoundingMode;
import ai.saiy.android.localisation.SaiyResources;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.personality.PersonalityResponse;
import ai.saiy.android.processing.Outcome;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.SPH;
/**
* Class to resolve information regarding the device's battery and assign the parameters for the
* vocal response.
*
* Created by benrandall76@gmail.com on 13/06/2016.
*/
public class BatteryInformation {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = BatteryInformation.class.getSimpleName();
public static final int CELSIUS = 0;
public static final int FAHRENHEIT = 1;
private final Intent batteryIntent;
private final Context mContext;
private final SupportedLanguage sl;
private final Outcome outcome;
private final String typeString;
/**
* Constructor
*
* @param mContext the application context
* @param sl the {@link SupportedLanguage}
* @param outcome the {@link Outcome}
* @param typeString the String representation of the battery information request type.
*/
public BatteryInformation(@NonNull final Context mContext, @NonNull SupportedLanguage sl,
@NonNull final Outcome outcome, @NonNull final String typeString) {
this.mContext = mContext;
this.sl = sl;
this.outcome = outcome;
this.typeString = typeString;
batteryIntent = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
/**
* Method to assign the response parameters to the {@link Outcome}
*
* @param type the {@link CommandBatteryValues.Type}
* @return the {@link Outcome}
*/
public Outcome getInfo(@NonNull final CommandBatteryValues.Type type) {
switch (type) {
case TEMPERATURE:
if (DEBUG) {
MyLog.i(CLS_NAME, "getInfo: " + CommandBatteryValues.Type.TEMPERATURE.name());
}
getTemperature();
break;
case PERCENTAGE:
if (DEBUG) {
MyLog.i(CLS_NAME, "getInfo: " + CommandBatteryValues.Type.PERCENTAGE.name());
}
getPercentage();
break;
case VOLTAGE:
if (DEBUG) {
MyLog.i(CLS_NAME, "getInfo: " + CommandBatteryValues.Type.VOLTAGE.name());
}
getVoltage();
break;
case STATUS:
if (DEBUG) {
MyLog.i(CLS_NAME, "getInfo: " + CommandBatteryValues.Type.STATUS.name());
}
getStatus();
break;
case HEALTH:
if (DEBUG) {
MyLog.i(CLS_NAME, "getInfo: " + CommandBatteryValues.Type.HEALTH.name());
}
getHealth();
break;
case UNKNOWN:
if (DEBUG) {
MyLog.w(CLS_NAME, "getInfo: " + CommandBatteryValues.Type.UNKNOWN.name());
}
outcome.setOutcome(Outcome.FAILURE);
outcome.setUtterance(PersonalityResponse.getBatteryErrorUnknownResponse(mContext, sl));
break;
}
return outcome;
}
/**
* Method to resolve the battery health
*/
private void getHealth() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getHealth");
}
if (batteryIntent != null) {
final SaiyResources sr = new SaiyResources(mContext, sl);
final int health = batteryIntent.getIntExtra(BatteryManager.EXTRA_HEALTH,
BatteryManager.BATTERY_HEALTH_UNKNOWN);
switch (health) {
case BatteryManager.BATTERY_HEALTH_COLD:
setHealthResponse(sr.getString(ai.saiy.android.R.string.cold));
break;
case BatteryManager.BATTERY_HEALTH_DEAD:
setHealthResponse(sr.getString(ai.saiy.android.R.string.dead));
break;
case BatteryManager.BATTERY_HEALTH_GOOD:
setHealthResponse(sr.getString(ai.saiy.android.R.string.good));
break;
case BatteryManager.BATTERY_HEALTH_OVERHEAT:
setHealthResponse(sr.getString(ai.saiy.android.R.string.over_heating));
break;
case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
setHealthResponse(sr.getString(ai.saiy.android.R.string.over_voltage));
break;
case BatteryManager.BATTERY_HEALTH_UNKNOWN:
setHealthResponse(sr.getString(ai.saiy.android.R.string.currently_indeterminable));
break;
case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
setHealthResponse(sr.getString(ai.saiy.android.R.string.currently_indeterminable));
break;
default:
setHealthResponse(sr.getString(ai.saiy.android.R.string.currently_indeterminable));
break;
}
sr.reset();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "batteryIntent: null");
}
setAccessFailure();
}
}
/**
* Method to set the {@link Outcome} parameters
*
* @param health the resolved battery {@link CommandBatteryValues.Type#HEALTH}
*/
private void setHealthResponse(@NonNull final String health) {
outcome.setOutcome(Outcome.SUCCESS);
outcome.setUtterance(PersonalityResponse.getBatteryResponse(mContext, sl, typeString, health));
}
/**
* Method to resolve the battery status
*/
private void getStatus() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getStatus");
}
if (batteryIntent != null) {
final SaiyResources sr = new SaiyResources(mContext, sl);
final int status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
switch (status) {
case BatteryManager.BATTERY_STATUS_CHARGING:
int plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED,
BatteryManager.BATTERY_STATUS_UNKNOWN);
switch (plugged) {
case BatteryManager.BATTERY_PLUGGED_AC:
setStatusResponse(sr.getString(ai.saiy.android.R.string.ac_charging));
break;
case BatteryManager.BATTERY_PLUGGED_USB:
setStatusResponse(sr.getString(ai.saiy.android.R.string.usb_charging));
break;
default:
setStatusResponse(sr.getString(ai.saiy.android.R.string.charging));
break;
}
break;
case BatteryManager.BATTERY_STATUS_DISCHARGING:
setStatusResponse(sr.getString(ai.saiy.android.R.string.discharging));
break;
case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
setStatusResponse(sr.getString(ai.saiy.android.R.string.discharging));
break;
case BatteryManager.BATTERY_STATUS_FULL:
setStatusResponse(sr.getString(ai.saiy.android.R.string.fully_charged));
break;
case BatteryManager.BATTERY_STATUS_UNKNOWN:
setStatusResponse(sr.getString(ai.saiy.android.R.string.currently_indeterminable));
break;
default:
setStatusResponse(sr.getString(ai.saiy.android.R.string.currently_indeterminable));
break;
}
sr.reset();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "batteryIntent: null");
}
setAccessFailure();
}
}
/**
* Method to set the {@link Outcome} parameters
*
* @param status the resolved battery {@link CommandBatteryValues.Type#STATUS}
*/
private void setStatusResponse(@NonNull final String status) {
outcome.setOutcome(Outcome.SUCCESS);
outcome.setUtterance(PersonalityResponse.getBatteryResponse(mContext, sl, typeString, status));
}
/**
* Method to resolve the battery voltage
*/
private void getVoltage() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getVoltage");
}
if (batteryIntent != null) {
int voltage = batteryIntent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
if (voltage > 0) {
voltage = voltage / 1000;
if (DEBUG) {
MyLog.i(CLS_NAME, "getVoltage: " + voltage);
}
final SaiyResources sr = new SaiyResources(mContext, sl);
setVoltageResponse(String.valueOf(voltage) + " " + sr.getString(ai.saiy.android.R.string.volts));
sr.reset();
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "getVoltage reporting incorrectly");
}
setAccessFailure();
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "batteryIntent: null");
}
setAccessFailure();
}
}
/**
* Method to set the {@link Outcome} parameters
*
* @param voltage the resolved battery {@link CommandBatteryValues.Type#VOLTAGE}
*/
private void setVoltageResponse(@NonNull final String voltage) {
outcome.setOutcome(Outcome.SUCCESS);
outcome.setUtterance(PersonalityResponse.getBatteryResponse(mContext, sl, typeString, voltage));
}
/**
* Method to resolve the battery percentage
*/
private void getPercentage() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getPercentage");
}
if (batteryIntent != null) {
final int percentage = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
if (percentage > -1) {
if (DEBUG) {
MyLog.i(CLS_NAME, "getPercentage: " + percentage);
}
setPercentageResponse(String.valueOf(percentage) + "%");
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "getPercentage reporting incorrectly");
}
setAccessFailure();
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "batteryIntent: null");
}
setAccessFailure();
}
}
/**
* Method to set the {@link Outcome} parameters
*
* @param percentage the resolved battery {@link CommandBatteryValues.Type#PERCENTAGE}
*/
private void setPercentageResponse(@NonNull final String percentage) {
outcome.setOutcome(Outcome.SUCCESS);
outcome.setUtterance(PersonalityResponse.getBatteryResponse(mContext, sl, typeString, percentage));
}
/**
* Method to resolve the battery temperature
*/
private void getTemperature() {
if (DEBUG) {
MyLog.i(CLS_NAME, "getTemperature");
}
if (batteryIntent != null) {
double celsius = batteryIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
if (celsius > 0) {
celsius = celsius / 10;
if (DEBUG) {
MyLog.i(CLS_NAME, "getTemperature: celsius: " + celsius);
}
final SaiyResources sr = new SaiyResources(mContext, sl);
final String degrees = sr.getString(ai.saiy.android.R.string.degrees);
String units = "";
double temperature = 1;
switch (SPH.getDefaultTemperatureUnits(mContext)) {
case CELSIUS:
units = sr.getString(ai.saiy.android.R.string.celsius);
temperature = celsius;
break;
case FAHRENHEIT:
final double conversion = (((celsius * 1.8) + 32));
final BigDecimal bd = new BigDecimal(conversion).setScale(0, RoundingMode.HALF_UP);
temperature = bd.doubleValue();
units = sr.getString(ai.saiy.android.R.string.fahrenheit);
break;
}
sr.reset();
if (DEBUG) {
MyLog.i(CLS_NAME, "getTemperature: " + temperature);
}
setTemperatureResponse(String.valueOf(temperature) + " " + degrees + " " + units);
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "getTemperature reporting incorrectly");
}
setAccessFailure();
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "batteryIntent: null");
}
setAccessFailure();
}
}
/**
* Method to set the {@link Outcome} parameters
*
* @param temperature the resolved battery {@link CommandBatteryValues.Type#TEMPERATURE}
*/
private void setTemperatureResponse(@NonNull final String temperature) {
outcome.setOutcome(Outcome.SUCCESS);
outcome.setUtterance(PersonalityResponse.getBatteryResponse(mContext, sl, typeString, temperature));
}
/**
* Method set the {@link Outcome#FAILURE} parameters
*/
private void setAccessFailure() {
outcome.setOutcome(Outcome.FAILURE);
outcome.setUtterance(PersonalityResponse.getBatteryErrorAccessResponse(mContext, sl));
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/command/battery/Battery_en.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.command.battery;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.Callable;
import ai.saiy.android.command.helper.CC;
import ai.saiy.android.localisation.SaiyResources;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsList;
/**
* Helper class to resolve battery commands
*
* Created by benrandall76@gmail.com on 12/06/2016.
*/
public class Battery_en {
private static final boolean DEBUG = MyLog.DEBUG;
private static final String CLS_NAME = Battery_en.class.getSimpleName();
private final SupportedLanguage sl;
private final ArrayList voiceData;
private final float[] confidence;
private static String battery;
private static String temperature;
private static String level;
private static String percentage;
private static String percent;
private static String voltage;
private static String volts;
private static String status;
private static String health;
/**
* Constructor
*
* Used by a {@link Callable} to prepare everything that is need when
* {@link Callable#call()} is executed with the {@link SaiyResources} managed elsewhere.
*
* @param sr the {@link SaiyResources}
* @param sl the {@link SupportedLanguage}
* @param voiceData the array of voice data
* @param confidence the array of confidence scores
*/
public Battery_en(@NonNull final SaiyResources sr, @NonNull final SupportedLanguage sl,
@NonNull final ArrayList voiceData, @NonNull float[] confidence) {
this.sl = sl;
this.voiceData = voiceData;
this.confidence = confidence;
if (battery == null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "initialising strings");
}
initStrings(sr);
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "strings initialised");
}
}
}
private static void initStrings(@NonNull final SaiyResources sr) {
battery = sr.getString(ai.saiy.android.R.string.battery);
temperature = sr.getString(ai.saiy.android.R.string.temperature);
level = sr.getString(ai.saiy.android.R.string.level);
percentage = sr.getString(ai.saiy.android.R.string.percentage);
percent = sr.getString(ai.saiy.android.R.string.percent);
voltage = sr.getString(ai.saiy.android.R.string.voltage);
volts = sr.getString(ai.saiy.android.R.string.volts);
status = sr.getString(ai.saiy.android.R.string.status);
health = sr.getString(ai.saiy.android.R.string.health);
}
/**
* Iterate through the voice data array to see if we can match the command.
*
* Note - As the speech array will never contain more than ten entries, to consider the static
* nature and performance issues here, perhaps implementing a matcher, would probably be overkill.
*
* @return an Array list of Pairs containing the {@link CC} and float confidence
*/
public ArrayList> detectCallable() {
final long then = System.nanoTime();
final ArrayList> toReturn = new ArrayList<>();
if (UtilsList.notNaked(voiceData) && UtilsList.notNaked(confidence)
&& voiceData.size() == confidence.length) {
final Locale loc = sl.getLocale();
String vdLower;
int size = voiceData.size();
for (int i = 0; i < size; i++) {
vdLower = voiceData.get(i).toLowerCase(loc).trim();
if (vdLower.contains(battery)
&& ((vdLower.contains(temperature)
|| vdLower.contains(level)
|| vdLower.contains(percentage)
|| vdLower.contains(percent)
|| vdLower.contains(voltage)
|| vdLower.contains(volts)
|| vdLower.contains(status)
|| vdLower.contains(health)))) {
toReturn.add(new Pair<>(CC.COMMAND_BATTERY, confidence[i]));
}
}
}
if (DEBUG) {
MyLog.i(CLS_NAME, "battery: returning ~ " + toReturn.size());
MyLog.getElapsed(CLS_NAME, then);
}
return toReturn;
}
/**
* Static method.
*
* Iterate through the voice data array and return an element of the highest confidence match.
*
* @param ctx the application context
* @param voiceData ArrayList containing the voice data
* @param sl the {@link SupportedLanguage}
* @return a {@link CommandBatteryValues} object containing the required parameters
*/
public static CommandBatteryValues sortBattery(@NonNull final Context ctx, final ArrayList voiceData,
@NonNull final SupportedLanguage sl) {
final long then = System.nanoTime();
final Locale loc = sl.getLocale();
final CommandBatteryValues values = new CommandBatteryValues();
values.setTypeString("");
values.setType(CommandBatteryValues.Type.UNKNOWN);
if (battery == null) {
if (DEBUG) {
MyLog.i(CLS_NAME, "initialising strings");
}
final SaiyResources sr = new SaiyResources(ctx, sl);
initStrings(sr);
sr.reset();
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "strings initialised");
}
}
String vdLower;
for (final String vd : voiceData) {
vdLower = vd.toLowerCase(loc).trim();
if (vdLower.contains(battery)) {
if (vdLower.contains(temperature)) {
values.setTypeString(temperature);
values.setType(CommandBatteryValues.Type.TEMPERATURE);
break;
} else if (vdLower.contains(level)
|| vdLower.contains(percent)
|| vdLower.contains(percentage)) {
values.setTypeString(level);
values.setType(CommandBatteryValues.Type.PERCENTAGE);
break;
} else if (vdLower.contains(voltage)
|| vdLower.contains(volts)) {
values.setTypeString(voltage);
values.setType(CommandBatteryValues.Type.VOLTAGE);
break;
} else if (vdLower.contains(status)) {
values.setTypeString(status);
values.setType(CommandBatteryValues.Type.STATUS);
break;
} else if (vdLower.contains(health)) {
values.setTypeString(health);
values.setType(CommandBatteryValues.Type.HEALTH);
break;
}
}
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
return values;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/command/battery/CommandBattery.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.command.battery;
import android.content.Context;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import ai.saiy.android.command.helper.CommandRequest;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.processing.Outcome;
import ai.saiy.android.utils.MyLog;
/**
* Class to process a Battery request. Handles both remote NLP intents and falling back to
* resolving locally.
*
* Created by benrandall76@gmail.com on 10/02/2016.
*/
public class CommandBattery {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = CommandBattery.class.getSimpleName();
/**
* Resolve the required command returning an {@link Outcome} object
*
* @param ctx the application context
* @param voiceData ArrayList containing the voice data
* @param sl the {@link SupportedLanguage} we are using to analyse the voice data.
* @param cr the {@link CommandRequest}
* @return {@link Outcome} containing everything we need to respond to the command.
*/
public Outcome getResponse(@NonNull final Context ctx, @NonNull final ArrayList voiceData,
@NonNull final SupportedLanguage sl, @NonNull final CommandRequest cr) {
if (DEBUG) {
MyLog.i(CLS_NAME, "voiceData: " + voiceData.size() + " : " + voiceData.toString());
}
final long then = System.nanoTime();
Outcome outcome = new Outcome();
if (cr.isResolved()) {
if (DEBUG) {
MyLog.i(CLS_NAME, "isResolved: true");
}
final CommandBatteryValues cbv = (CommandBatteryValues) cr.getVariableData();
final CommandBatteryValues.Type type = cbv.getType();
outcome = new BatteryInformation(ctx, sl, outcome, cbv.getTypeString()).getInfo(type);
} else {
if (DEBUG) {
MyLog.i(CLS_NAME, "isResolved: false");
}
outcome = new CommandBatteryLocal().getResponse(ctx, voiceData, sl);
}
if (DEBUG) {
MyLog.getElapsed(CLS_NAME, then);
}
return outcome;
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/command/battery/CommandBatteryLocal.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.command.battery;
import android.content.Context;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.processing.Outcome;
/**
* Helper class to resolve a battery command locally.
*
* Created by benrandall76@gmail.com on 13/06/2016.
*/
public class CommandBatteryLocal {
/**
* Resolve the required command returning an {@link Outcome} object
*
* @param ctx the application context
* @param voiceData ArrayList containing the voice data
* @param sl the {@link SupportedLanguage} we are using to analyse the voice data.
* This is not necessarily the Locale of the device, as the user may be
* multi-lingual and/or have set a custom recognition language in a launcher short-cut.
* @return {@link Outcome} containing everything we need to respond to the command.
*/
public Outcome getResponse(@NonNull final Context ctx, @NonNull final ArrayList voiceData,
@NonNull final SupportedLanguage sl) {
final CommandBatteryValues values = new Battery(sl).fetch(ctx, voiceData);
return new BatteryInformation(ctx, sl, new Outcome(), values.getTypeString()).getInfo(values.getType());
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/command/battery/CommandBatteryValues.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.command.battery;
import android.content.Context;
import android.support.annotation.NonNull;
import ai.saiy.android.localisation.SaiyResources;
import ai.saiy.android.localisation.SupportedLanguage;
import ai.saiy.android.utils.MyLog;
import ai.saiy.android.utils.UtilsString;
/**
* Class to hold information and constants to resolve a battery command.
*
* Created by benrandall76@gmail.com on 01/06/2016.
*/
public class CommandBatteryValues {
private final boolean DEBUG = MyLog.DEBUG;
private final String CLS_NAME = CommandBatteryValues.class.getSimpleName();
/**
* Constant battery request types
*/
public enum Type {
UNKNOWN,
TEMPERATURE,
PERCENTAGE,
VOLTAGE,
STATUS,
HEALTH
}
private Type type;
private String typeString;
private long startIndex;
private long endIndex;
private int[][] ranges;
public int[][] getRanges() {
return ranges;
}
public void setRanges(@NonNull final int[][] ranges) {
this.ranges = ranges;
}
public String getTypeString() {
return typeString;
}
public void setTypeString(@NonNull final String typeString) {
this.typeString = typeString;
}
public Type getType() {
return type;
}
public void setType(@NonNull final Type type) {
this.type = type;
}
public long getEndIndex() {
return endIndex;
}
public void setEndIndex(final long endIndex) {
this.endIndex = endIndex;
}
public void setStartIndex(final long startIndex) {
this.startIndex = startIndex;
}
public long getStartIndex() {
return startIndex;
}
/**
* Method to convert the spoken battery command information type, to an {@link Type} for use as
* a constant in further methods.
*
* @param ctx the application context
* @param sl the {@link SupportedLanguage}
* @return one of {@link Type} or {@link Type#UNKNOWN} if the voice data cannot be resolved.
*/
public Type stringToType(@NonNull final Context ctx, @NonNull final SupportedLanguage sl,
@NonNull final String typeString) {
if (UtilsString.notNaked(typeString)) {
final SaiyResources sr = new SaiyResources(ctx, sl);
final String type = typeString.toLowerCase(sl.getLocale()).trim();
final String temperature = sr.getString(ai.saiy.android.R.string.temperature);
final String percent = sr.getString(ai.saiy.android.R.string.percent);
final String percentage = sr.getString(ai.saiy.android.R.string.percentage);
final String level = sr.getString(ai.saiy.android.R.string.level);
final String voltage = sr.getString(ai.saiy.android.R.string.voltage);
final String status = sr.getString(ai.saiy.android.R.string.status);
final String health = sr.getString(ai.saiy.android.R.string.health);
sr.reset();
if (type.matches(temperature)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "stringToType: " + Type.TEMPERATURE.name());
}
return Type.TEMPERATURE;
} else if (type.matches(percent)
|| type.matches(percentage)
|| type.matches(level)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "stringToType: " + Type.PERCENTAGE.name());
}
return Type.PERCENTAGE;
} else if (type.matches(voltage)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "stringToType: " + Type.VOLTAGE.name());
}
return Type.VOLTAGE;
} else if (type.matches(status)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "stringToType: " + Type.STATUS.name());
}
return Type.STATUS;
} else if (type.matches(health)) {
if (DEBUG) {
MyLog.i(CLS_NAME, "stringToType: " + Type.HEALTH.name());
}
return Type.HEALTH;
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "stringToType: " + Type.UNKNOWN.name());
}
return Type.UNKNOWN;
}
} else {
if (DEBUG) {
MyLog.w(CLS_NAME, "stringToType: literal naked");
}
return Type.UNKNOWN;
}
}
}
================================================
FILE: app/src/main/java/ai/saiy/android/command/cancel/Cancel.java
================================================
/*
* Copyright (c) 2016. Saiy Ltd. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.saiy.android.command.cancel;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Pair;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import ai.saiy.android.command.helper.CC;
import ai.saiy.android.localisation.SaiyResources;
import ai.saiy.android.localisation.SupportedLanguage;
/**
* Helper class to detect the command Cancel, which is used in many places across the application,
* not just when trying to resolve commands.
*
* Performance is key, so initialising localised resource Strings needs to be done as few times as
* possible, whenever possible.
*
* Created by benrandall76@gmail.com on 06/04/2016.
*/
public class Cancel implements Callable>> {
private final SupportedLanguage sl;
private final Object cancel;
/**
* Constructor
*
* Used by the {@link Callable} to construct the data ready for {@link Callable#call()}
*
* @param sr the {@link SaiyResources}
* @param sl the {@link SupportedLanguage}
* @param voiceData the array of voice data
* @param confidence the array of confidence scores
*/
public Cancel(@NonNull final SaiyResources sr, @NonNull final SupportedLanguage sl,
@NonNull final ArrayList