Commit e88590d9 by Kunj Gupta

Added Arrow flavor.

parent 02602491
Showing with 2370 additions and 102 deletions
......@@ -26,13 +26,13 @@ android {
}
}
compileSdkVersion 24
buildToolsVersion "24.0.2"
compileSdkVersion 27
buildToolsVersion "27.0.0"
defaultConfig {
applicationId "com.vsoft.servicenow"
minSdkVersion 14
targetSdkVersion 24
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "0.0.29"
multiDexEnabled true
......@@ -61,22 +61,31 @@ android {
ge {
applicationId "com.vsoft.servicenow.ge"
}
arrow {
dimension 'tier'
applicationId "com.vsoft.servicenow.arrow"
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
compile 'com.jakewharton:butterknife:8.8.1'
apt 'com.jakewharton:butterknife-compiler:8.8.1'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.google.android.gms:play-services-analytics:11.8.0'
compile('com.crashlytics.sdk.android:crashlytics:2.6.2@aar') {
apt 'com.jakewharton:butterknife-compiler:8.8.1'
compile('com.crashlytics.sdk.android:crashlytics:2.9.0@aar') {
transitive = true;
}
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.github.nkzawa:socket.io-client:0.3.0'
compile 'com.android.support:appcompat-v7:27.0.2'
compile 'com.android.support:cardview-v7:27.0.2'
compile 'com.android.support:recyclerview-v7:27.0.2'
compile 'com.android.support:animated-vector-drawable:27.0.2'
compile 'com.android.support:support-media-compat:27.0.2'
compile 'com.android.support:support-v4:27.0.2'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vsoft.servicenow">
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application>
<activity
android:screenOrientation="portrait"
android:name=".chat.ChatActivity" />
</application>
</manifest>
package com.vsoft.servicenow;
/**
* @author Kunj on 05/01/17.
*/
public class AppConfig {
public static final String CHAT_SERVER_URL = "http://111.93.6.218:12910/";//https://socket-io-chat.now.sh/";
public static final String APP_INTERNAL_NAME = "Arrow";
public static final String DOMAIN_PRODUCTION = "https://ven01199.service-now.com/";
public static final String DOMAIN_TEST = "https://ven01199.service-now.com/";//"https://uofltest.service-now.com/";
public static final String LOGIN_CLIENT_ID_PRODUCTION = "d958eb06b0f3830093781f441d59febc";
public static final String LOGIN_CLIENT_SECRET_PRODUCTION = "krD*!O}1.8";
public static final String LOGIN_CLIENT_ID_TEST = "d958eb06b0f3830093781f441d59febc";
public static final String LOGIN_CLIENT_SECRET_TEST = "krD*!O}1.8";
/**
* Web services urls
*/
/*Catalogue Category API */
public static final String URL_GET_CATALOGUE = "api/vsng2/uofl_mobile/catalogue_screen";
/*Catalogue Category Items API */
public static final String URL_GET_CATALOGUE_ITEM = "api/vsng2/uofl_mobile/catalog_item";
/*Variable form API */
public static final String URL_GET_VARIABLE = "api/vsng2/uofl_mobile/catalogue_variable_screen";
public static final String URL_GET_UI_POLICY = "/api/vsng2/uofl_mobile/uipolicy";
public static final String URL_GET_VARIABLE_CHOICE = "/api/vsng2/uofl_mobile/question_choice";
public static final String URL_POST_CATALOGUE_ITEM = "api/vsng2/uofl_mobile";
}
package com.vsoft.servicenow.adapters;
import android.content.Context;
import android.content.res.TypedArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.vsoft.servicenow.R;
/**
* Created by kunj on 18/8/16.
*/
public class HomeScreenAdapter extends BaseAdapter {
private final LayoutInflater mInflater;
private final String[] mGridValues;
private final TypedArray icons;
//Constructor to initialize values
public HomeScreenAdapter(Context mContext, String[ ] mGridValues,TypedArray icons) {
this.mGridValues = mGridValues;
this.icons=icons;
mInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
// Number of times getVariableViewContainer method call depends upon mGridValues.length
return mGridValues.length;
}
@Override
public String getItem(int position) {
return mGridValues[position];
}
@Override
public long getItemId(int position) {
return position;
}
// Number of times getVariableViewContainer method call depends upon mGridValues.length
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.home_screen_adapter, parent, false);
holder = new ViewHolder();
holder.textview = (TextView) convertView.findViewById(R.id.home_screen_adapter_text_view);
holder.imageView = (ImageView) convertView.findViewById(R.id.home_screen_adapter_image_view);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textview.setText(mGridValues[position]);
holder.imageView.setImageResource(icons.getResourceId(position, -1));
return convertView;
}
static class ViewHolder {
private TextView textview;
private ImageView imageView;
}
}
package com.vsoft.servicenow.chat;
public class Message {
public static final int TYPE_MESSAGE = 0;
public static final int TYPE_LOG = 1;
public static final int TYPE_ACTION = 2;
private int mType;
private String mMessage;
private String mUsername;
private boolean mEnableSpeaker;
private Message() {}
public int getType() {
return mType;
};
public String getMessage() {
return mMessage;
};
public String getUsername() {
return mUsername;
};
public boolean ismEnableSpeaker() {
return mEnableSpeaker;
}
public static class Builder {
private final int mType;
private String mUsername;
private String mMessage;
private boolean mEnableSpeaker;
public Builder(int type) {
mType = type;
}
public Builder username(String username) {
mUsername = username;
return this;
}
public Builder message(String message) {
mMessage = message;
return this;
}
public Builder enableSpeaker(boolean enableSpeaker) {
mEnableSpeaker = enableSpeaker;
return this;
}
public Message build() {
Message message = new Message();
message.mType = mType;
message.mUsername = mUsername;
message.mMessage = mMessage;
message.mEnableSpeaker = mEnableSpeaker;
return message;
}
}
}
package com.vsoft.servicenow.chat;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.vsoft.servicenow.R;
import com.vsoft.servicenow.db.models.Catalogue;
import com.vsoft.servicenow.ui.HomeScreen;
import com.vsoft.servicenow.utils.CatalogueLog;
import com.vsoft.servicenow.utils.Constants;
import com.vsoft.servicenow.utils.PrefManager;
import com.vsoft.servicenow.utils.Util;
import java.util.List;
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder> {
private List<Message> mMessages;
private int[] mUsernameColors;
private Context mContext;
private int type = -1;
public MessageAdapter(Context context, List<Message> messages) {
mMessages = messages;
mContext = context;
mUsernameColors = context.getResources().getIntArray(R.array.username_colors);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layout = -1;
switch (viewType) {
case Message.TYPE_MESSAGE:
type = -1;
layout = R.layout.item_message;
break;
case Message.TYPE_LOG:
type = Message.TYPE_LOG;
layout = R.layout.item_log;
break;
case Message.TYPE_ACTION:
type = -1;
layout = R.layout.item_action;
break;
}
View v = LayoutInflater
.from(parent.getContext())
.inflate(layout, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
Message message = mMessages.get(position);
viewHolder.setMessage(message.getUsername(), message.getMessage());
if(message.getUsername() != null) {
viewHolder.setUsername(message.getUsername());
}
}
@Override
public int getItemCount() {
return mMessages.size();
}
@Override
public int getItemViewType(int position) {
return mMessages.get(position).getType();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView mUsernameView, mHrNameView;
private TextView mUserMessageView, mHRMessageView;
private TextView mTitleMessageTextView, mLogMessageTextView;
private TextView mNameItemActionTextView;
public ViewHolder(View itemView) {
super(itemView);
mUsernameView = (TextView) itemView.findViewById(R.id.user_username);
mHrNameView = (TextView) itemView.findViewById(R.id.hr_username);
mUserMessageView = (TextView) itemView.findViewById(R.id.user_message);
mHRMessageView = (TextView) itemView.findViewById(R.id.hr_message);
mTitleMessageTextView = (TextView) itemView.findViewById(R.id.title_message);
mLogMessageTextView = (TextView) itemView.findViewById(R.id.log_message);
mNameItemActionTextView = (TextView) itemView.findViewById(R.id.item_action_username);
}
public void setUsername(String username) {
if(mUsernameView != null && mHrNameView != null) {
if (username.equals(PrefManager.getSharedPref(mContext, PrefManager.PREFERENCE_FIRST_NAME))) {
mUsernameView.setVisibility(View.VISIBLE);
mHrNameView.setVisibility(View.GONE);
mUsernameView.setText(username);
mUsernameView.setTextColor(getUsernameColor(username));
} else {
mUsernameView.setVisibility(View.GONE);
mHrNameView.setVisibility(View.VISIBLE);
mHrNameView.setText(username);
mHrNameView.setTextColor(getUsernameColor(username));
}
} else if(mNameItemActionTextView != null) {
mNameItemActionTextView.setText(username);
}
}
public void setMessage(String userName, String message) {
//if (null == mHRMessageView) return;
if(type == -1 && userName != null) {
if(mUserMessageView != null && mHRMessageView != null) {
if (userName.equals(PrefManager.getSharedPref(mContext, PrefManager.PREFERENCE_FIRST_NAME))) {
mUserMessageView.setVisibility(View.VISIBLE);
mHRMessageView.setVisibility(View.GONE);
mUserMessageView.setMovementMethod(LinkMovementMethod.getInstance());
mUserMessageView.setText(Util.fromHtml(message));
} else {
mUserMessageView.setVisibility(View.GONE);
mHRMessageView.setVisibility(View.VISIBLE);
mHRMessageView.setMovementMethod(LinkMovementMethod.getInstance());
mHRMessageView.setText(Util.fromHtml(message));
}
}
} else if(type == Message.TYPE_LOG) {
if(message.contains(mContext.getString(R.string.message_welcome))) {
mTitleMessageTextView.setVisibility(View.VISIBLE);
mLogMessageTextView.setVisibility(View.GONE);
mTitleMessageTextView.setText(message);
} else {
mTitleMessageTextView.setVisibility(View.GONE);
mLogMessageTextView.setVisibility(View.VISIBLE);
mLogMessageTextView.setText(message);
}
}
}
private int getUsernameColor(String username) {
int hash = 7;
for (int i = 0, len = username.length(); i < len; i++) {
hash = username.codePointAt(i) + (hash << 5) - hash;
}
int index = Math.abs(hash % mUsernameColors.length);
return mUsernameColors[index];
}
}
}
package com.vsoft.servicenow.chat;
import android.content.Context;
import android.media.AudioManager;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;
import com.vsoft.servicenow.utils.Util;
import java.util.HashMap;
import java.util.Locale;
/**
* Created by chaukadev on 2/27/18.
*/
public class Speaker implements TextToSpeech.OnInitListener {
private TextToSpeech tts;
private boolean ready = false;
private boolean allowed = false;
public Speaker(Context context) {
tts = new TextToSpeech(context, this);
}
public boolean isAllowed() {
return allowed;
}
public void allow(boolean allowed) {
this.allowed = allowed;
}
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
// Change this to match your
// locale
tts.setLanguage(Locale.US);
ready = true;
} else {
ready = false;
}
}
public void speak(String text){
// Speak only if the TTS is ready
// and the user has allowed speech
if(ready && allowed) {
HashMap<String, String> hash = new HashMap<String,String>();
hash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_MUSIC));
hash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "UniqueID");
tts.speak(Util.fromHtml(text).toString(), TextToSpeech.QUEUE_ADD, hash);
}
}
public boolean isSpeaking() {
return tts.isSpeaking();
}
public void stop(){
tts.stop();
}
public void pause(int duration){
tts.playSilence(duration, TextToSpeech.QUEUE_ADD, null);
}
// Free up resources
public void destroy(){
tts.shutdown();
}
public TextToSpeech getTTS() {
return tts;
}
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import java.util.Random;
/**
* AnimatorBarRms
*
* @author Vikram Ezhil
*/
class AnimatorBarRms implements OnBarParamsAnimListener {
private static final float QUIT_RMSDB_MAX = 2f;
private static final float MEDIUM_RMSDB_MAX = 5.5f;
private static final long BAR_ANIMATION_UP_DURATION = 130;
private static final long BAR_ANIMATION_DOWN_DURATION = 500;
final private RecognitionBarView bar;
private float fromHeightPart;
private float toHeightPart;
private long startTimestamp;
private boolean isPlaying;
private boolean isUpAnimation;
AnimatorBarRms(RecognitionBarView bar) {
this.bar = bar;
}
@Override
public void start() {
isPlaying = true;
}
@Override
public void stop() {
isPlaying = false;
}
@Override
public void animate() {
if (isPlaying) {
update();
}
}
void onRmsChanged(float rmsdB) {
float newHeightPart;
if (rmsdB < QUIT_RMSDB_MAX) {
newHeightPart = 0.2f;
} else if (rmsdB >= QUIT_RMSDB_MAX && rmsdB <= MEDIUM_RMSDB_MAX) {
newHeightPart = 0.3f + new Random().nextFloat();
if (newHeightPart > 0.6f) newHeightPart = 0.6f;
} else {
newHeightPart = 0.7f + new Random().nextFloat();
if (newHeightPart > 1f) newHeightPart = 1f;
}
if (newHeightIsSmallerCurrent(newHeightPart)) {
return;
}
fromHeightPart = (float) bar.getHeight() / bar.getMaxHeight();
toHeightPart = newHeightPart;
startTimestamp = System.currentTimeMillis();
isUpAnimation = true;
isPlaying = true;
}
private boolean newHeightIsSmallerCurrent(float newHeightPart) {
return (float) bar.getHeight() / bar.getMaxHeight() > newHeightPart;
}
private void update() {
long currTimestamp = System.currentTimeMillis();
long delta = currTimestamp - startTimestamp;
if (isUpAnimation) {
animateUp(delta);
} else {
animateDown(delta);
}
}
private void animateUp(long delta) {
boolean finished = false;
int minHeight = (int) (fromHeightPart * bar.getMaxHeight());
int toHeight = (int) (bar.getMaxHeight() * toHeightPart);
float timePart = (float) delta / BAR_ANIMATION_UP_DURATION;
AccelerateInterpolator interpolator = new AccelerateInterpolator();
int height = minHeight + (int) (interpolator.getInterpolation(timePart) * (toHeight - minHeight));
if (height < bar.getHeight()) {
return;
}
if (height >= toHeight) {
height = toHeight;
finished = true;
}
bar.setHeight(height);
bar.update();
if (finished) {
isUpAnimation = false;
startTimestamp = System.currentTimeMillis();
}
}
private void animateDown(long delta) {
int minHeight = bar.getRadius() * 2;
int fromHeight = (int) (bar.getMaxHeight() * toHeightPart);
float timePart = (float) delta / BAR_ANIMATION_DOWN_DURATION;
DecelerateInterpolator interpolator = new DecelerateInterpolator();
int height = minHeight + (int) ((1f - interpolator.getInterpolation(timePart)) * (fromHeight - minHeight));
if (height > bar.getHeight()) {
return;
}
if (height <= minHeight) {
finish();
return;
}
bar.setHeight(height);
bar.update();
}
private void finish() {
bar.setHeight(bar.getRadius() * 2);
bar.update();
isPlaying = false;
}
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import java.util.List;
/**
* AnimatorIdle
*
* @author Vikram Ezhil
*/
class AnimatorIdle implements OnBarParamsAnimListener {
private static final long IDLE_DURATION = 1500;
private long startTimestamp;
private boolean isPlaying;
private final int floatingAmplitude;
private final List<RecognitionBarView> bars;
AnimatorIdle(List<RecognitionBarView> bars, int floatingAmplitude) {
this.floatingAmplitude = floatingAmplitude;
this.bars = bars;
}
@Override
public void start() {
isPlaying = true;
startTimestamp = System.currentTimeMillis();
}
@Override
public void stop() {
isPlaying = false;
}
@Override
public void animate() {
if (isPlaying) {
update(bars);
}
}
void update(List<RecognitionBarView> bars) {
long currTimestamp = System.currentTimeMillis();
if (currTimestamp - startTimestamp > IDLE_DURATION) {
startTimestamp += IDLE_DURATION;
}
long delta = currTimestamp - startTimestamp;
int i = 0;
for (RecognitionBarView bar : bars) {
updateCirclePosition(bar, delta, i);
i++;
}
}
private void updateCirclePosition(RecognitionBarView bar, long delta, int num) {
float angle = ((float) delta / IDLE_DURATION) * 360f + 120f * num;
int y = (int) (Math.sin(Math.toRadians(angle)) * floatingAmplitude) + bar.getStartY();
bar.setY(y);
bar.update();
}
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import java.util.ArrayList;
import java.util.List;
/**
* AnimatorRms
*
* @author Vikram Ezhil
*/
class AnimatorRms implements OnBarParamsAnimListener {
final private List<AnimatorBarRms> barAnimators;
AnimatorRms(List<RecognitionBarView> recognitionBars) {
this.barAnimators = new ArrayList<>();
for (RecognitionBarView bar : recognitionBars) {
barAnimators.add(new AnimatorBarRms(bar));
}
}
@Override
public void start() {
for (AnimatorBarRms barAnimator : barAnimators) {
barAnimator.start();
}
}
@Override
public void stop() {
for (AnimatorBarRms barAnimator : barAnimators) {
barAnimator.stop();
}
}
@Override
public void animate() {
for (AnimatorBarRms barAnimator : barAnimators) {
barAnimator.animate();
}
}
public void onRmsChanged(float rmsDB) {
for (AnimatorBarRms barAnimator : barAnimators) {
barAnimator.onRmsChanged(rmsDB);
}
}
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import android.graphics.Point;
import android.view.animation.AccelerateDecelerateInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* AnimatorRotating
*
* @author Vikram Ezhil
*/
class AnimatorRotating implements OnBarParamsAnimListener {
private static final long DURATION = 2000;
private static final long ACCELERATE_ROTATION_DURATION = 1000;
private static final long DECELERATE_ROTATION_DURATION = 1000;
private static final float ROTATION_DEGREES = 720f;
private static final float ACCELERATION_ROTATION_DEGREES = 40f;
private long startTimestamp;
private boolean isPlaying;
private final int centerX, centerY;
private final List<Point> startPositions;
private final List<RecognitionBarView> bars;
AnimatorRotating(List<RecognitionBarView> bars, int centerX, int centerY) {
this.centerX = centerX;
this.centerY = centerY;
this.bars = bars;
this.startPositions = new ArrayList<>();
for (RecognitionBarView bar : bars) {
startPositions.add(new Point(bar.getX(), bar.getY()));
}
}
@Override
public void start() {
isPlaying = true;
startTimestamp = System.currentTimeMillis();
}
@Override
public void stop() {
isPlaying = false;
}
@Override
public void animate() {
if (!isPlaying) return;
long currTimestamp = System.currentTimeMillis();
if (currTimestamp - startTimestamp > DURATION) {
startTimestamp += DURATION;
}
long delta = currTimestamp - startTimestamp;
float interpolatedTime = (float) delta / DURATION;
float angle = interpolatedTime * ROTATION_DEGREES;
int i = 0;
for (RecognitionBarView bar : bars) {
float finalAngle = angle;
if (i > 0 && delta > ACCELERATE_ROTATION_DURATION) {
finalAngle += decelerate(delta, bars.size() - i);
} else if (i > 0) {
finalAngle += accelerate(delta, bars.size() - i);
}
rotate(bar, finalAngle, startPositions.get(i));
i++;
}
}
private float decelerate(long delta, int scale) {
long accelerationDelta = delta - ACCELERATE_ROTATION_DURATION;
AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();
float interpolatedTime = interpolator.getInterpolation((float) accelerationDelta / DECELERATE_ROTATION_DURATION);
float decelerationAngle = -interpolatedTime * (ACCELERATION_ROTATION_DEGREES * scale);
return ACCELERATION_ROTATION_DEGREES * scale + decelerationAngle;
}
private float accelerate(long delta, int scale) {
AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();
float interpolatedTime = interpolator.getInterpolation((float) delta / ACCELERATE_ROTATION_DURATION);
return interpolatedTime * (ACCELERATION_ROTATION_DEGREES * scale);
}
/**
* X = x0 + (x - x0) * cos(a) - (y - y0) * sin(a);
* Y = y0 + (y - y0) * cos(a) + (x - x0) * sin(a);
*/
private void rotate(RecognitionBarView bar, double degrees, Point startPosition) {
double angle = Math.toRadians(degrees);
int x = centerX + (int) ((startPosition.x - centerX) * Math.cos(angle) -
(startPosition.y - centerY) * Math.sin(angle));
int y = centerY + (int) ((startPosition.x - centerX) * Math.sin(angle) +
(startPosition.y - centerY) * Math.cos(angle));
bar.setX(x);
bar.setY(y);
bar.update();
}
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import android.graphics.Point;
import java.util.ArrayList;
import java.util.List;
/**
* AnimatorTransform
*
* @author Vikram Ezhil
*/
class AnimatorTransform implements OnBarParamsAnimListener {
private static final long DURATION = 300;
private long startTimestamp;
private boolean isPlaying;
private OnInterpolationFinishedListener listener;
private final int radius;
private final int centerX, centerY;
private final List<Point> finalPositions = new ArrayList<>();
private final List<RecognitionBarView> bars;
AnimatorTransform(List<RecognitionBarView> bars, int centerX, int centerY, int radius) {
this.centerX = centerX;
this.centerY = centerY;
this.bars = bars;
this.radius = radius;
}
@Override
public void start() {
isPlaying = true;
startTimestamp = System.currentTimeMillis();
initFinalPositions();
}
@Override
public void stop() {
isPlaying = false;
if (listener != null) {
listener.onFinished();
}
}
@Override
public void animate() {
if (!isPlaying) return;
long currTimestamp = System.currentTimeMillis();
long delta = currTimestamp - startTimestamp;
if (delta > DURATION) {
delta = DURATION;
}
for (int i = 0; i < bars.size(); i++) {
RecognitionBarView bar = bars.get(i);
int x = bar.getStartX() + (int) ((finalPositions.get(i).x - bar.getStartX()) * ((float) delta / DURATION));
int y = bar.getStartY() + (int) ((finalPositions.get(i).y - bar.getStartY()) * ((float) delta / DURATION));
bar.setX(x);
bar.setY(y);
bar.update();
}
if (delta == DURATION) {
stop();
}
}
private void initFinalPositions() {
Point startPoint = new Point();
startPoint.x = centerX;
startPoint.y = centerY - radius;
for (int i = 0; i < RecognitionProgressView.BARS_COUNT; i++) {
Point point = new Point(startPoint);
rotate((360d / RecognitionProgressView.BARS_COUNT) * i, point);
finalPositions.add(point);
}
}
/**
* X = x0 + (x - x0) * cos(a) - (y - y0) * sin(a);
* Y = y0 + (y - y0) * cos(a) + (x - x0) * sin(a);
**/
private void rotate(double degrees, Point point) {
double angle = Math.toRadians(degrees);
int x = centerX + (int) ((point.x - centerX) * Math.cos(angle) -
(point.y - centerY) * Math.sin(angle));
int y = centerY + (int) ((point.x - centerX) * Math.sin(angle) +
(point.y - centerY) * Math.cos(angle));
point.x = x;
point.y = y;
}
void setOnInterpolationFinishedListener(OnInterpolationFinishedListener listener) {
this.listener = listener;
}
interface OnInterpolationFinishedListener {
void onFinished();
}
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import android.Manifest;
import android.app.Fragment;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import com.vsoft.servicenow.R;
/**
* Droid Speech Permissions - Non UI Fragment
*
* @author Vikram Ezhil
*/
public class DroidSpeechPermissions extends Fragment {
private static final int REQUEST_AUDIO_PERMISSIONS = 100;
private OnDSPermissionsListener droidSpeechPermissionsListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
try {
// Initializing the droid speech permission listener
droidSpeechPermissionsListener = (OnDSPermissionsListener) getActivity();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Checks for audio permissions
*
* @param context The application instance context
* @return The audio permission status
*/
public boolean checkForAudioPermissions(Context context) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || context.checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
}
/**
* Requests for audio permissions
*/
public void requestForAudioPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_AUDIO_PERMISSIONS);
}
}
@SuppressWarnings("NullableProblems")
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_AUDIO_PERMISSIONS:
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
// Audio permission not granted
// if(droidSpeechPermissionsListener != null)
droidSpeechPermissionsListener.onDroidSpeechAudioPermissionStatus(false, getResources().getString(R.string.ds_mic_permissions_required));
return;
}
}
// Audio permission granted
droidSpeechPermissionsListener.onDroidSpeechAudioPermissionStatus(true, null);
break;
}
}
}
package com.vsoft.servicenow.speechRecognizer;
import android.content.Context;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
/**
* Droid Speech Extensions
*
* @author Vikram Ezhil
*/
class Extensions {
final static int[] PV_COLORS = new int[]{Color.BLUE, Color.RED, Color.YELLOW, Color.BLUE, Color.GREEN};
final static int[] PV_BARS_HEIGHT = new int[]{24, 28, 22, 27, 20};
final static int PV_HEIGHT = 100;
final static int PV_CIRCLE_RADIUS = 5;
final static int PV_CIRCLE_SPACING = 2;
final static int PV_IDLE_STATE = 2;
final static int PV_ROTATION_RADIUS = 10;
final static int MAX_VOICE_RESULTS = 5;
final static int MAX_PAUSE_TIME = 500;
final static int PARTIAL_DELAY_TIME = 500;
final static int ERROR_TIMEOUT = 5000;
final static int AUDIO_BEEP_DISABLED_TIMEOUT = 30000;
/**
* Checks if the internet is enabled
*
* @param context The application context instance
* @return The internet enabled status
*/
static boolean isInternetEnabled(Context context) {
// Initializing the connectivity Manager
ConnectivityManager activeConnection = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// Getting the network information
NetworkInfo networkInfo = activeConnection.getActiveNetworkInfo();
return networkInfo != null && (networkInfo.getType() == ConnectivityManager.TYPE_WIFI || networkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
}
}
package com.vsoft.servicenow.speechRecognizer;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import java.util.ArrayList;
import java.util.List;
/**
* Droid Speech Language Receiver
*
* @author Vikram Ezhil
*/
class LanguageReceiver extends BroadcastReceiver {
private OnLanguageDetailsListener onLanguageDetailsListener;
@Override
public void onReceive(Context context, Intent intent) {
List<String> supportedLanguages = new ArrayList<>();
String defaultLanguagePreference = null;
if (getResultCode() == Activity.RESULT_OK) {
Bundle results = getResultExtras(true);
if (results != null) {
if (results.containsKey(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE)) {
defaultLanguagePreference = results.getString(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE);
}
if (results.containsKey(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES)) {
if (results.getStringArrayList(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES) != null) {
supportedLanguages = results.getStringArrayList(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES);
}
}
}
}
if (onLanguageDetailsListener != null) {
// Sending an update with the language details information
onLanguageDetailsListener.onLanguageDetailsInfo(defaultLanguagePreference, supportedLanguages);
}
}
/**
* Sets the language details listener
*
* @param onLanguageDetailsListener The language details listener
*/
void setOnLanguageDetailsListener(OnLanguageDetailsListener onLanguageDetailsListener) {
this.onLanguageDetailsListener = onLanguageDetailsListener;
}
}
package com.vsoft.servicenow.speechRecognizer;
/**
* Bar Animation Listener
*
* @author Vikram Ezhil
*/
interface OnBarParamsAnimListener {
/**
* Sends update to start animation
*/
void start();
/**
* Sends update to stop animation
*/
void stop();
/**
* Sends update to animate
*/
void animate();
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import java.util.List;
/**
* Droid Speech Listener
*
* @author Vikram Ezhil
*/
public interface OnDSListener {
/**
* The droid speech supported languages
*
* @param currentSpeechLanguage The current speech language
* @param supportedSpeechLanguages The supported speech languages
*/
void onDroidSpeechSupportedLanguages(String currentSpeechLanguage, List<String> supportedSpeechLanguages);
/**
* The droid speech rms changed result
*
* @param rmsChangedValue The rms changed result
*/
void onDroidSpeechRmsChanged(float rmsChangedValue);
/**
* The droid speech recognizer live result
*
* @param liveSpeechResult The live speech result
*/
void onDroidSpeechLiveResult(String liveSpeechResult);
/**
* The droid speech recognizer final result
*
* @param finalSpeechResult The final speech result
*/
void onDroidSpeechFinalResult(String finalSpeechResult);
/**
* The droid speech recognition was closed by user
*/
void onDroidSpeechClosedByUser();
/**
* The droid speech recognizer error update
*
* @param errorMsg The error message
*/
void onDroidSpeechError(String errorMsg);
}
package com.vsoft.servicenow.speechRecognizer;
/**
* Droid Speech Permissions Listener
*
* @author Vikram Ezhil
*/
public interface OnDSPermissionsListener {
/**
* Sends an update with the droid speech audio permission status
*
* @param audioPermissionGiven The audio permission given status
* @param errorMsgIfAny Error message if any
*/
void onDroidSpeechAudioPermissionStatus(boolean audioPermissionGiven, String errorMsgIfAny);
}
package com.vsoft.servicenow.speechRecognizer;
import java.util.List;
/**
* Droid Speech Language Details Listener
*
* @author Vikram Ezhil
*/
interface OnLanguageDetailsListener {
/**
* Sends an update with the device language details
*
* @param defaultLanguage The default language
* @param otherLanguages The other supported languages
*/
void onLanguageDetailsInfo(String defaultLanguage, List<String> otherLanguages);
}
package com.vsoft.servicenow.speechRecognizer;
import java.util.ArrayList;
import java.util.List;
/**
* Droid Speech Properties
*
* @author Vikram Ezhil
*/
class Properties {
List<String> supportedSpeechLanguages = new ArrayList<>();
String currentSpeechLanguage;
String listeningMsg;
String oneStepVerifySpeechResult;
long startListeningTime;
long pauseAndSpeakTime;
boolean offlineSpeechRecognition = false;
boolean continuousSpeechRecognition = true;
boolean showRecognitionProgressView = false;
boolean oneStepResultVerify = false;
boolean onReadyForSpeech = false;
boolean speechResultFound = false;
boolean closedByUser = false;
}
package com.vsoft.servicenow.speechRecognizer;
import android.graphics.RectF;
/**
* Recognition Bar View
*
* @author Vikram Ezhil
*/
class RecognitionBarView {
private int x;
private int y;
private int radius;
private int height;
private final int maxHeight;
private final int startX;
private final int startY;
final private RectF rect;
RecognitionBarView(int x, int y, int height, int maxHeight, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.startX = x;
this.startY = y;
this.height = height;
this.maxHeight = maxHeight;
this.rect = new RectF(x - radius,
y - height / 2,
x + radius,
y + height / 2);
}
void update() {
rect.set(x - radius,
y - height / 2,
x + radius,
y + height / 2);
}
int getX() {
return x;
}
void setX(int x) {
this.x = x;
}
int getY() {
return y;
}
void setY(int y) {
this.y = y;
}
int getHeight() {
return height;
}
void setHeight(int height) {
this.height = height;
}
int getMaxHeight() {
return maxHeight;
}
int getStartX() {
return startX;
}
int getStartY() {
return startY;
}
RectF getRect() {
return rect;
}
int getRadius() {
return radius;
}
}
\ No newline at end of file
package com.vsoft.servicenow.speechRecognizer;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Recognition Progress View
*
* @author Vikram Ezhil
*/
class RecognitionProgressView extends View {
public static final int BARS_COUNT = 5;
private static final int CIRCLE_RADIUS_DP = 5;
private static final int CIRCLE_SPACING_DP = 11;
private static final int ROTATION_RADIUS_DP = 25;
private static final int IDLE_FLOATING_AMPLITUDE_DP = 3;
private static final int[] DEFAULT_BARS_HEIGHT_DP = {60, 46, 70, 54, 64};
private static final float MDPI_DENSITY = 1.5f;
private final List<RecognitionBarView> recognitionBars = new ArrayList<>();
private Paint paint;
private OnBarParamsAnimListener animator;
private int radius;
private int spacing;
private int rotationRadius;
private int amplitude;
private float density;
private boolean animating;
private int barColor = -1;
private int[] barColors;
private int[] barMaxHeights;
// MARK: SpeechProgressView Constructors
public RecognitionProgressView(Context context) {
super(context);
init();
}
public RecognitionProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RecognitionProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
// MARK: View Methods
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (recognitionBars.isEmpty()) {
initBars();
} else if (changed) {
recognitionBars.clear();
initBars();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (recognitionBars.isEmpty()) {
return;
}
if (animating) {
animator.animate();
}
for (int i = 0; i < recognitionBars.size(); i++) {
RecognitionBarView bar = recognitionBars.get(i);
if (barColors != null) {
paint.setColor(barColors[i]);
} else if (barColor != -1) {
paint.setColor(barColor);
}
canvas.drawRoundRect(bar.getRect(), radius, radius, paint);
}
if (animating) {
invalidate();
}
}
/**
* Starts animating view
*/
public void play() {
startIdleInterpolation();
animating = true;
}
/**
* Stops animating view
*/
public void stop() {
if (animator != null) {
animator.stop();
animator = null;
}
animating = false;
resetBars();
}
/**
* Set one color to all bars in view
*/
public void setSingleColor(int color) {
barColor = color;
}
/**
* Set different colors to bars in view
*
* @param colors - array with size = {@link #BARS_COUNT}
*/
public void setColors(int[] colors) {
if (colors == null) return;
barColors = new int[BARS_COUNT];
if (colors.length < BARS_COUNT) {
System.arraycopy(colors, 0, barColors, 0, colors.length);
for (int i = colors.length; i < BARS_COUNT; i++) {
barColors[i] = colors[0];
}
} else {
System.arraycopy(colors, 0, barColors, 0, BARS_COUNT);
}
}
/**
* Set sizes of bars in view
*
* @param heights - array with size = {@link #BARS_COUNT},
* if not set uses default bars heights
*/
public void setBarMaxHeightsInDp(int[] heights) {
if (heights == null) return;
barMaxHeights = new int[BARS_COUNT];
if (heights.length < BARS_COUNT) {
System.arraycopy(heights, 0, barMaxHeights, 0, heights.length);
for (int i = heights.length; i < BARS_COUNT; i++) {
barMaxHeights[i] = heights[0];
}
} else {
System.arraycopy(heights, 0, barMaxHeights, 0, BARS_COUNT);
}
}
/**
* Set radius of circle
*
* @param radius - Default value = {@link #CIRCLE_RADIUS_DP}
*/
public void setCircleRadiusInDp(int radius) {
this.radius = (int) (radius * density);
}
/**
* Set spacing between circles
*
* @param spacing - Default value = {@link #CIRCLE_SPACING_DP}
*/
public void setSpacingInDp(int spacing) {
this.spacing = (int) (spacing * density);
}
/**
* Set idle animation amplitude
*
* @param amplitude - Default value = {@link #IDLE_FLOATING_AMPLITUDE_DP}
*/
public void setIdleStateAmplitudeInDp(int amplitude) {
this.amplitude = (int) (amplitude * density);
}
/**
* Set rotation animation radius
*
* @param radius - Default value = {@link #ROTATION_RADIUS_DP}
*/
public void setRotationRadiusInDp(int radius) {
this.rotationRadius = (int) (radius * density);
}
private void init() {
paint = new Paint();
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.GRAY);
density = getResources().getDisplayMetrics().density;
radius = (int) (CIRCLE_RADIUS_DP * density);
spacing = (int) (CIRCLE_SPACING_DP * density);
rotationRadius = (int) (ROTATION_RADIUS_DP * density);
amplitude = (int) (IDLE_FLOATING_AMPLITUDE_DP * density);
if (density <= MDPI_DENSITY) {
amplitude *= 2;
}
}
private void initBars() {
final List<Integer> heights = initBarHeights();
int firstCirclePosition = getMeasuredWidth() / 2 -
2 * spacing -
4 * radius;
for (int i = 0; i < BARS_COUNT; i++) {
int x = firstCirclePosition + (2 * radius + spacing) * i;
RecognitionBarView bar = new RecognitionBarView(x, getMeasuredHeight() / 2, 2 * radius, heights.get(i), radius);
recognitionBars.add(bar);
}
}
private List<Integer> initBarHeights() {
final List<Integer> barHeights = new ArrayList<>();
if (barMaxHeights == null) {
for (int i = 0; i < BARS_COUNT; i++) {
barHeights.add((int) (DEFAULT_BARS_HEIGHT_DP[i] * density));
}
} else {
for (int i = 0; i < BARS_COUNT; i++) {
barHeights.add((int) (barMaxHeights[i] * density));
}
}
return barHeights;
}
private void resetBars() {
for (RecognitionBarView bar : recognitionBars) {
bar.setX(bar.getStartX());
bar.setY(bar.getStartY());
bar.setHeight(radius * 2);
bar.update();
}
}
private void startIdleInterpolation() {
animator = new AnimatorIdle(recognitionBars, amplitude);
animator.start();
}
private void startRmsInterpolation() {
resetBars();
animator = new AnimatorRms(recognitionBars);
animator.start();
}
private void startTransformInterpolation() {
resetBars();
animator = new AnimatorTransform(recognitionBars, getWidth() / 2, getHeight() / 2, rotationRadius);
animator.start();
((AnimatorTransform) animator).setOnInterpolationFinishedListener(new AnimatorTransform.OnInterpolationFinishedListener() {
@Override
public void onFinished() {
startRotateInterpolation();
}
});
}
private void startRotateInterpolation() {
animator = new AnimatorRotating(recognitionBars, getWidth() / 2, getHeight() / 2);
animator.start();
}
/**
* Updates the view with the rmsDB Value
*
* @param rmsdB The rmsdb value
*/
public void rmsValue(float rmsdB) {
if (animator == null || rmsdB < 1f) {
return;
}
if (!(animator instanceof AnimatorRms)) {
startRmsInterpolation();
}
if (animator instanceof AnimatorRms) {
((AnimatorRms) animator).onRmsChanged(rmsdB);
}
}
}
package com.vsoft.servicenow.ui;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.GridView;
import com.github.nkzawa.emitter.Emitter;
import com.github.nkzawa.socketio.client.Socket;
import com.google.android.gms.analytics.Tracker;
import com.vsoft.servicenow.chat.ChatActivity;
import com.vsoft.servicenow.utils.Util;
import com.vsoft.servicenow.CatalogueApplication;
import com.vsoft.servicenow.R;
import com.vsoft.servicenow.adapters.HomeScreenAdapter;
import com.vsoft.servicenow.utils.PrefManager;
import org.json.JSONException;
import org.json.JSONObject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnItemClick;
/**
* Created by Kunj on 11/8/16.
*/
public class HomeScreen extends AppCompatActivity {
@BindView(R.id.tool_bar_view) Toolbar mToolbar;
@BindView(R.id.home_screen_grid_view) GridView mGridView;
private Socket mSocket;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.home_screen);
CatalogueApplication app = (CatalogueApplication) getApplication();
mSocket = app.getSocket();
ButterKnife.bind(this);
CatalogueApplication application = (CatalogueApplication) getApplication();
Tracker tracker = application.getDefaultTracker();
// Send initial screen view hit.
Util.sendScreenName(tracker, getString(R.string.home_screen_string));
String[] gridValuesArray = getResources().getStringArray(R.array.home_screen_array);
TypedArray gridViewIcons = getResources().obtainTypedArray(R.array.home_screen_icon_array);
HomeScreenAdapter adapter = new HomeScreenAdapter(this, gridValuesArray, gridViewIcons);
mGridView.setAdapter(adapter);
// mSocket.on("login", onLogin);
}
@Override
protected void onDestroy() {
super.onDestroy();
// mSocket.off("login", onLogin);
}
@OnItemClick(R.id.home_screen_grid_view)
void gridViewItemClicked(int position) {
if (position == 0) {
startActivity(new Intent(HomeScreen.this, ReportIncidentScreen.class));
} else if (position == 1) {
startActivity(new Intent(HomeScreen.this, CatalogueScreen.class));
} else if (position == 2) {
startActivity(new Intent(HomeScreen.this, MyIncidentScreen.class));
} else if (position == 3) {
startActivity(new Intent(HomeScreen.this, MyRequestActivity.class));
} else if (position == 4) {
// perform the user login attempt.
String userFirstName = PrefManager.getSharedPref(HomeScreen.this, PrefManager.PREFERENCE_FIRST_NAME);
// CatalogueLog.e("Socket is connect: "+mSocket.connected());
// if(!mSocket.connected())
// mSocket.connect();
// mSocket.emit("add user", userFirstName);
Intent intent = new Intent(HomeScreen.this, ChatActivity.class);
intent.putExtra("username", userFirstName);
startActivity(intent);
}
}
@OnClick(R.id.home_screen_logout_image_view)
void logoutOnClicked() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.home_screen_logout_confirmation_msg_string)
.setCancelable(false)
.setPositiveButton(R.string.ok_string, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_ACCESS_TOKEN, "");
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_REFRESH_TOKEN, "");
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_LAST_NAME, "");
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_SYS_ID, "");
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_FIRST_NAME, "");
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_USER_ID, "");
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_USER_FULL_NAME, "");
PrefManager.setSharedPref(HomeScreen.this, PrefManager.PREFERENCE_USER_EMAIL_ID, "");
Intent loginIntent = new Intent(HomeScreen.this, LoginScreen.class);
loginIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(loginIntent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
alert.show();
}
private Emitter.Listener onLogin = new Emitter.Listener() {
@Override
public void call(Object... args) {
JSONObject data = (JSONObject) args[0];
int numUsers;
try {
numUsers = data.getInt("numUsers");
} catch (JSONException e) {
return;
}
Intent intent = new Intent();
intent.putExtra("username", PrefManager.getSharedPref(HomeScreen.this, PrefManager.PREFERENCE_FIRST_NAME));
intent.putExtra("numUsers", numUsers);
setResult(RESULT_OK, intent);
finish();
}
};
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/LightBackgroundStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".chat.ChatActivity">
<include layout="@layout/toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".chat.ChatActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/messages"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="@dimen/small_margin"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/black"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin">
<EditText
android:id="@+id/message_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/prompt_message"
android:imeActionId="@+id/send"
android:imeActionLabel="@string/action_send"
android:imeOptions="actionSend"
android:inputType="textCapSentences"
android:maxLines="1"
android:singleLine="true" />
<ImageButton
android:id="@+id/send_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/action_send"
android:src="@android:drawable/ic_menu_send" />
<ImageButton
android:id="@+id/voice_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/action_send"
android:src="@android:drawable/ic_btn_speak_now" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingTop="@dimen/spacing">
<TextView
android:id="@+id/item_action_username"
style="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textStyle="bold"/>
<TextView
android:id="@+id/action"
style="?android:textAppearanceMedium"
android:textColor="?android:textColorSecondary"
android:text="@string/user_action_typing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/spacing"
android:paddingRight="@dimen/spacing"
android:singleLine="true"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/log_message"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:visibility="gone"
android:paddingTop="@dimen/spacing"
android:textColor="?android:textColorSecondary" />
<TextView
android:id="@+id/title_message"
style="?android:textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:gravity="center"
android:paddingTop="@dimen/spacing"
android:paddingBottom="@dimen/spacing"
android:background="@color/colorPrimaryDark"
android:textColor="@android:color/white" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/spacing">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:gravity="right"
android:orientation="vertical"
android:layout_marginLeft="@dimen/chat_margin"
android:layout_marginRight="@dimen/normal_margin"
android:background="@color/user_background_chat_color"
android:paddingBottom="@dimen/small_margin"
android:paddingTop="@dimen/small_margin">
<TextView
android:id="@+id/user_username"
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/spacing"
android:paddingRight="@dimen/spacing"
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
<TextView
android:id="@+id/user_message"
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/user_username"
android:paddingLeft="@dimen/spacing"
android:paddingRight="@dimen/spacing"
android:textColor="?android:textColorPrimary" />
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/normal_margin"
android:layout_marginRight="@dimen/chat_margin"
android:background="@color/hr_background_chat_color"
android:paddingBottom="@dimen/small_margin"
android:paddingTop="@dimen/small_margin">
<TextView
android:id="@+id/hr_username"
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:paddingLeft="@dimen/spacing"
android:paddingRight="@dimen/spacing"
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
<TextView
android:id="@+id/hr_message"
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/hr_username"
android:paddingLeft="@dimen/spacing"
android:paddingRight="@dimen/spacing"
android:textColor="?android:textColorPrimary" />
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/transparent">
<com.vsoft.servicenow.speechRecognizer.RecognitionProgressView
android:id="@+id/recognitionProgressView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"/>
<TextView
android:id="@+id/recognitionProgressMsg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/progressMsgMargin"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/app_name"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical">
<ImageView
android:layout_margin="10dp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/splash_logo" />
</FrameLayout>
\ No newline at end of file
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item android:id="@+id/action_leave"
android:title="@string/action_leave"
android:orderInCategory="100"
app:showAsAction="never"/>
</menu>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="home_screen_array">
<item>Report Incident</item>
<item>Order Services</item>
<item>My Incidents</item>
<item>My Requests</item>
<item>Chatbot</item>
</string-array>
<array name="home_screen_icon_array">
<item>@drawable/ic_myincident_icon</item>
<item>@drawable/ic_order_service_icon</item>
<item>@drawable/ic_my_incidents_icon</item>
<item>@drawable/ic_my_requiest_icon</item>
<item>@drawable/ic_my_requiest_icon</item>
</array>
<string-array name="incident_impact_array">
<item>-None-</item>
<item>1 - High</item>
<item>2 - Medium</item>
<item>3 - Low</item>
</string-array>
<array name="username_colors">
<item>@color/username0</item>
<item>@color/username1</item>
<item>@color/username2</item>
<item>@color/username3</item>
<item>@color/username4</item>
<item>@color/username5</item>
<item>@color/username6</item>
<item>@color/username7</item>
<item>@color/username8</item>
<item>@color/username9</item>
<item>@color/username10</item>
<item>@color/username11</item>
</array>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#00A1DB</color>
<color name="colorPrimaryDark">#00A1DB</color>
<color name="colorAccent">#1a4a7d</color>
<color name="tool_bar_title_color">@color/colorPrimaryDark</color>
<color name="view_not_implemented_color">#ff0000</color>
<color name="submit_button_bg_color">@color/colorPrimaryDark</color>
<!--Login Screen-->
<color name="login_screen_login_button_background_color">@color/colorPrimaryDark</color>
<color name="login_screen_edit_text_background_color">@color/colorPrimaryDark</color>
<!--Chatbot Screen-->
<color name="username0">#e21400</color>
<color name="username1">#91580f</color>
<color name="username2">#f8a700</color>
<color name="username3">#f78b00</color>
<color name="username4">#58dc00</color>
<color name="username5">#287b00</color>
<color name="username6">#a8f07a</color>
<color name="username7">#4ae8c4</color>
<color name="username8">#3b88eb</color>
<color name="username9">#3824aa</color>
<color name="username10">#a700ff</color>
<color name="username11">#d300e7</color>
<color name="hr_background_chat_color">#f7fcfc</color>
<color name="user_background_chat_color">#e6f3f5</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="spacing">8dp</dimen>
<dimen name="chat_margin">40dp</dimen>
<!--SPEECH RECOGNIZER-->
<dimen name="progressMsgMargin">10dp</dimen>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="incident_from_short_description_text_limit">160</integer>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">Arrow POC</string>
<!--Chat Related String-->
<string name="action_leave">Leave</string>
<string name="action_send">Send</string>
<string name="prompt_message">Message</string>
<string name="connect">Connected</string>
<string name="disconnect">Disconnected, Please check your internet connection</string>
<string name="error_connect">Failed to connect</string>
<!-- messages -->
<string name="message_welcome">Chat with HR Bot</string>
<plurals name="message_participants">
<item quantity="one">there\'s %d participant</item>
<item quantity="other">there are %d participants</item>
</plurals>
<string name="message_user_joined">%s joined</string>
<string name="message_user_left">%s left</string>
<string name="user_action_typing">is typing</string>
<!--Speech Recognizer-->
<string name="ds_listening">Listening…</string>
<string name="ds_internet_not_enabled">Internet is not enabled</string>
<string name="ds_mic_permissions_required">Please provide microphone permissions</string>
<string name="ds_unknown_error">Unknown error</string>
<string name="ds_progress_layout_error">Unable to add speech progress view</string>
<string name="ds_confirm">Confirm</string>
<string name="ds_retry">Retry</string>
<string-array name="droid_speech_errors">
<item>Network Timeout</item>
<item>Network Error</item>
<item>Audio Error</item>
<item>Server Error</item>
<item>Client Error</item>
<item>Speech Timeout</item>
<item>No match</item>
<item>Speech Recognizer busy</item>
<item>Insufficient permissions</item>
</string-array>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="windowNoTitle">true</item>
<item name="actionOverflowMenuStyle">@style/OverflowMenu</item>
</style>
<style name="OverflowMenu" parent="Widget.AppCompat.PopupMenu.Overflow">
<!-- Required for pre-Lollipop. -->
<item name="overlapAnchor">false</item>
<!-- Required for Lollipop. -->
<item name="android:overlapAnchor">false</item>
</style>
<style name="WhiteBackgroundStyle" parent="@style/Theme.AppCompat">
<item name="android:background">@android:color/white</item>
</style>
<style name="LightBackgroundStyle" parent="@style/Theme.AppCompat">
<item name="android:background">@color/screen_bg_color</item>
</style>
<style name="DatePickerCustomDialog" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">@android:color/white</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowNoTitle">true</item>
<item name="editTextStyle">@android:style/Widget.EditText</item>
</style>
<style name="CustomDialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Replace placeholder ID with your tracking ID -->
<string name="ga_trackingId">UA-83545030-1</string>
<!-- Enable automatic activity tracking -->
<bool name="ga_autoActivityTracking">true</bool>
<!-- Enable automatic exception tracking -->
<bool name="ga_reportUncaughtExceptions">true</bool>
</resources>
\ No newline at end of file
......@@ -13,112 +13,131 @@ import android.util.Log;
import android.widget.Toast;
import com.crashlytics.android.Crashlytics;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.Tracker;
import com.vsoft.servicenow.db.DBManager;
import com.vsoft.servicenow.service.SyncService;
import com.vsoft.servicenow.ui.LoginScreen;
import com.vsoft.servicenow.utils.CatalogueLog;
import com.vsoft.servicenow.utils.Constants;
import com.vsoft.servicenow.utils.PrefManager;
import java.net.URISyntaxException;
import io.fabric.sdk.android.Fabric;
public class CatalogueApplication extends Application {
private static DBManager sDBManager;
private ConnectivityManager mConMgr;
private static Context mContext;
private Tracker mTracker;
private BroadcastReceiver mSyncBroadCastSyncReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getStringExtra(Constants.APPLICATION_BROADCAST_DATA_ACTION);
Log.d(Constants.TAG, "CatalogueApplication: onReceive: action: "+action);
if(Constants.ACTION_SYNC.equals(action)) {
Intent syncMatchSummaryIntentIntent = new Intent(context, SyncService.class);
if (Constants.DEBUG) Log.d(Constants.TAG, "CatalogueApplication: Start SyncService");
context.startService(syncMatchSummaryIntentIntent);
}
}
};
private BroadcastReceiver mSyncBroadCastLoginReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getStringExtra(Constants.APPLICATION_BROADCAST_DATA_ACTION);
Log.d(Constants.TAG, "CatalogueApplication: onReceive: action: "+action);
if(Constants.ACTION_PROMPT_LOGIN.equals(action)) {
Toast.makeText(CatalogueApplication.this, R.string.prompt_relogin_login_expired, Toast.LENGTH_SHORT).show();
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_ACCESS_TOKEN, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_REFRESH_TOKEN, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_LAST_NAME, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_SYS_ID, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_FIRST_NAME, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_USER_ID, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_USER_FULL_NAME, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_USER_EMAIL_ID, "");
Intent loginIntent = new Intent(CatalogueApplication.this, LoginScreen.class);
loginIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(loginIntent);
}
}
};
@Override
public void onCreate() {
super.onCreate();
Fabric.with(this, new Crashlytics());
mContext = getApplicationContext();
LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadCastLoginReceiver,
new IntentFilter(Constants.APPLICATION_BROADCAST_INTENT));
LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadCastSyncReceiver,
new IntentFilter(Constants.APPLICATION_BROADCAST_INTENT));
private static DBManager sDBManager;
private ConnectivityManager mConMgr;
private static Context mContext;
private Tracker mTracker;
private Socket mSocket;
{
try {
mSocket = IO.socket(AppConfig.CHAT_SERVER_URL);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private BroadcastReceiver mSyncBroadCastSyncReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getStringExtra(Constants.APPLICATION_BROADCAST_DATA_ACTION);
Log.d(Constants.TAG, "CatalogueApplication: onReceive: action: " + action);
if (Constants.ACTION_SYNC.equals(action)) {
Intent syncMatchSummaryIntentIntent = new Intent(context, SyncService.class);
if (Constants.DEBUG)
Log.d(Constants.TAG, "CatalogueApplication: Start SyncService");
context.startService(syncMatchSummaryIntentIntent);
}
}
};
private BroadcastReceiver mSyncBroadCastLoginReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getStringExtra(Constants.APPLICATION_BROADCAST_DATA_ACTION);
Log.d(Constants.TAG, "CatalogueApplication: onReceive: action: " + action);
if (Constants.ACTION_PROMPT_LOGIN.equals(action)) {
Toast.makeText(CatalogueApplication.this, R.string.prompt_relogin_login_expired, Toast.LENGTH_SHORT).show();
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_ACCESS_TOKEN, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_REFRESH_TOKEN, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_LAST_NAME, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_SYS_ID, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_FIRST_NAME, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_USER_ID, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_USER_FULL_NAME, "");
PrefManager.setSharedPref(CatalogueApplication.this, PrefManager.PREFERENCE_USER_EMAIL_ID, "");
Intent loginIntent = new Intent(CatalogueApplication.this, LoginScreen.class);
loginIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(loginIntent);
}
}
};
@Override
public void onCreate() {
super.onCreate();
Fabric.with(this, new Crashlytics());
mContext = getApplicationContext();
LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadCastLoginReceiver,
new IntentFilter(Constants.APPLICATION_BROADCAST_INTENT));
LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadCastSyncReceiver,
new IntentFilter(Constants.APPLICATION_BROADCAST_INTENT));
/*Database is created*/
initializeDatabase();
}
@Override
public void onTerminate() {
super.onTerminate();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadCastLoginReceiver);
LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadCastSyncReceiver);
}
public static Context getContext() {
return mContext;
}
synchronized public Tracker getDefaultTracker() {
if (mTracker == null) {
GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
// To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG
mTracker = analytics.newTracker(R.xml.global_tracker);
}
return mTracker;
}
/*DataBase is created*/
public void initializeDatabase() {
if(sDBManager == null) {
sDBManager = new DBManager(this);
sDBManager.getWritableDatabase();
}
}
public static SQLiteDatabase getDatabase() {
if(sDBManager != null) {
return sDBManager.getWritableDatabase();
}
return null;
}
public boolean isNetConnected() {
if(mConMgr==null)
mConMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = mConMgr.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnected();
}
initializeDatabase();
}
@Override
public void onTerminate() {
super.onTerminate();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadCastLoginReceiver);
LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadCastSyncReceiver);
}
public static Context getContext() {
return mContext;
}
synchronized public Tracker getDefaultTracker() {
if (mTracker == null) {
GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
// To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG
mTracker = analytics.newTracker(R.xml.global_tracker);
}
return mTracker;
}
/*DataBase is created*/
public void initializeDatabase() {
if (sDBManager == null) {
sDBManager = new DBManager(this);
sDBManager.getWritableDatabase();
}
}
public static SQLiteDatabase getDatabase() {
if (sDBManager != null) {
return sDBManager.getWritableDatabase();
}
return null;
}
public boolean isNetConnected() {
if (mConMgr == null)
mConMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = mConMgr.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnected();
}
public Socket getSocket() {
return mSocket;
}
}
......@@ -6,6 +6,7 @@ import android.graphics.Typeface;
import android.support.v4.content.ContextCompat;
import android.text.Html;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.view.Gravity;
......@@ -622,4 +623,15 @@ public class Util {
InputMethodManager imm =(InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
@SuppressWarnings("deprecation")
public static Spanned fromHtml(String html){
Spanned result;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
result = Html.fromHtml(html,Html.FROM_HTML_MODE_LEGACY);
} else {
result = Html.fromHtml(html);
}
return result;
}
}
......@@ -20,6 +20,7 @@ allprojects {
maven {
url "https://maven.google.com"
}
maven { url "https://jitpack.io" }
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment