diff --git a/.gitignore b/.gitignore index 8d2ec513ef..7ac83ef566 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ local.properties .project .settings/ .classpath -.vscode/ \ No newline at end of file +.vscode/ +NDK_14b/ +.cxx/ diff --git a/app/build.gradle b/app/build.gradle index ea8069d3bd..ec35a27a8b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,18 +9,20 @@ buildscript { } dependencies { - classpath 'com.github.menny:GradleVersion:0.0.3' + classpath 'net.evendanan.autoversion:gradle-plugin:0.1.7' +// classpath 'com.github.menny:GradleVersion:0.0.3' + //classpath 'com.android.tools.build:gradle:3.3.0' } } apply plugin: 'com.android.application' apply plugin: 'com.github.triplet.play' -apply plugin: 'net.evendanan.versiongenerator' +apply plugin: 'net.evendanan.autoversion' def generators = [ new net.evendanan.versiongenerator.generators.EnvBuildVersionGenerator.CircleCi( 1650/*adding to version. Should not ever change*/, - -2575-2600 /*decrementing due to minor, every minor/major bump, this should be decremented*/), + -2575 - 2600 /*decrementing due to minor, every minor/major bump, this should be decremented*/), new net.evendanan.versiongenerator.generators.StaticVersionGenerator() ] @@ -31,6 +33,8 @@ apply from: "${rootDir}/gradle/android_general.gradle" android { defaultConfig { applicationId 'com.menny.android.anysoftkeyboard' + minSdkVersion 19 + multiDexEnabled true versionCode versionData.versionCode versionName versionData.versionName @@ -86,6 +90,11 @@ android { matchingFallbacks = ['release', 'debug'] } } + sourceSets { + main { + java.srcDirs = ['src/main/java', 'src/main/java/com/menny/android/anysoftkeyboard/DAO_DB'] + } + } } play { @@ -104,7 +113,7 @@ if (playStoreWhatsNewFile.text.length() > 500) { println("Locale " + Locale.getDefault()) println("file encoding " + CharsetToolkit.defaultSystemCharset) println("File contents:") - println("***" + playStoreWhatsNewFile.text + "***") + println("***" + playStoreWhatsNewFile.text + "***") throw new IllegalStateException("whatsnew file can not be longer than 500 characters! Currently " + playStoreWhatsNewFile.text.length()) } @@ -119,6 +128,21 @@ dependencies { implementation project(':overlay') implementation project(':pixel') + implementation('org.sagebionetworks.bridge:android-sdk:0.2.0') { + exclude group: 'com.google.code.findbugs' + } + + //https://developer.android.com/jetpack/androidx/releases/work#declaring_dependencies + def work_version = "1.0.1" + implementation("android.arch.work:work-runtime:$work_version") { + exclude group: 'com.google.guava', module: 'listenablefuture' + } + implementation("android.arch.work:work-rxjava2:$work_version") { + exclude group: 'com.google.guava', module: 'listenablefuture' + } + + implementation 'com.github.akarnokd:rxjava2-interop:0.13.2' + implementation "com.anysoftkeyboard.api:api:$AnySoftKeyboardApiVersion" implementation "com.android.support:support-fragment:$supportLibVersion" implementation "com.android.support:appcompat-v7:$supportLibVersion" @@ -138,6 +162,45 @@ dependencies { implementation 'com.anysoftkeybaord.languages:base:4.0.368@aar' implementation 'com.anysoftkeybaord.languages.english:english:4.0.368@aar' + implementation 'com.android.support:support-annotations:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'android.arch.lifecycle:extensions:1.1.1' testImplementation project(path: ':base-test') testImplementation 'com.github.triplet.simpleprovider:simpleprovider:1.1.0' + +// // Room components + /* + * Room depends on 26.1 of support library, SupportLibrary does not promise interop between versions. + * hence code breaking? + * Trying to resolve by excluding from group + * */ + implementation("android.arch.persistence.room:runtime:$rootProject.roomVersion") { + exclude group: 'com.android.support' + } + annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion" +// } +} + +// trying to resolve the android version issues +//configurations.all { +// resolutionStrategy.eachDependency { DependencyResolveDetails details -> +// def requested = details.requested +// if (requested.group == 'com.android.support') { +// if (!requested.name.startsWith("multidex")) { +// details.useVersion '28.0.0' +// } +// } +// } +// +// + +configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + def requested = details.requested + if (requested.group == 'com.android.support') { + if (!requested.name.startsWith("multidex")) { + details.useVersion '25.4.0' + } + } + } } diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml index 189b19b15b..6f45288176 100644 --- a/app/src/debug/AndroidManifest.xml +++ b/app/src/debug/AndroidManifest.xml @@ -4,5 +4,7 @@ + tools:replace="android:name" > + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6bf1cc5e2d..483492b7e4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + xmlns:tools="http://schemas.android.com/tools" + package="com.menny.android.anysoftkeyboard"> + - - - - + tools:ignore="UnusedAttribute" /> + - - - - - - - + + + - - - + tools:ignore="GoogleAppIndexingWarning,UnusedAttribute" + tools:replace="android:label"> + + + - + + + + + + + + + + + + - - - - + - + + - - - - + + + + + + + @@ -96,7 +118,6 @@ android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/Theme.AskApp.NoTitle.NoActionBar" android:windowSoftInputMode="adjustPan" /> - - + android:theme="@style/Theme.AskApp.Popup" /> - + android:theme="@style/Theme.AskApp.NoTitle" /> - + \ No newline at end of file diff --git a/app/src/main/assets/consent_full.html b/app/src/main/assets/consent_full.html new file mode 100644 index 0000000000..4e838f1203 --- /dev/null +++ b/app/src/main/assets/consent_full.html @@ -0,0 +1,47 @@ +

Review

+

Review the form below, and tap Agree if you're ready to continue.

+

How can we better understand the mood fluctuations and subtle cognitive changes in people with bipolar disorder together?

+

People with bipolar disorder can have very different and more or less severe mood symptoms day to day. This affects quality of life and makes managing treatment difficult. Moreover, recent scientific studies have shown that people with bipolar disorder often have subtle but distinct difficulties with cognition, such as inability to focus, during both depression and mania.

+

Some people notice that these difficulties do not completely go away even after they fully recovered from their mood symptoms. It has been hypothesized that these cognitive difficulties in bipolar disorder strongly predict level of functioning in the future. In this Researchkit study, we would like to investigate the feasibility of BiAffect, a mobile application or 'app' on iPhone that aims to understand the relationship between mood and neurocognitive functioning in bipolar disorder using keystroke dynamics (such as typing speed and typing errors) and other passive sensor information (such as accelerometer). Note that these research activities are limited to beta/feasibility testing, and thus are not intended as a means to diagnose or treat bipolar disorder in study participants.

+

Currently, diagnosis and treatment of bipolar disorder rely on careful history taking and mental status examination by an experienced clinician, at times aided by self-report or caretaker-informed questionnaires. In general, these reports have to be interpreted by providers in order to extract patterns that could indicate an imminent change in mood. Moreover, they do not necessarily represent objective psycho-physiological markers.

+

On the other hand, the pervasive use of mobile wireless devices has significantly shaped interpersonal communications in modern life. Indeed, as personal smartphone technology advances, people are increasingly interacting with one another via typed (rather than oral) communications. For this reason, we want to investigate if keyboard dynamics and sensor data from iPhone serve as more objective biomarkers or 'virtual mental-health footprints' of bipolar disorder.

+

If you decide to join the study you will need to download the study application on your mobile device. This application will install a custom-built keyboard that replaces the standard default iPhone keyboard in order for the researchers to better understand keyboard dynamics metadata (but not to track the actual words). Then periodically we will ask you to answer questions and perform some activities via your mobile phone. These survey questions may be about your health, exercise, diet, sleep, medicines, etc. or about how you are feeling in general. In addition to surveys, the activities will be some brief tasks that you perform while holding your phone for a short period of time. Your study data will include your responses to surveys and the measurements from the phone itself when you perform an activity.

+

Data Gathering

+

How is your data collected and maintained?

+

Your data, without your name, will be added to the data of other study participants and analyzed by the study team. You will have a unique account that you can use to review your own data.

+

We will electronically process your data and separate your account information (email) from your study data (your responses to surveys and the measurements from the phone itself when you perform activities). We will combine your coded study data (without your name or other identifying information) with those of other study participants. The combined data will be encrypted and transferred electronically to secure servers at Sage Bionetworks for storage, and will be accessible by researchers at the University of Illinois at Chicago.

+

Privacy

+

How will the information I provide be kept confidential?

+

We are committed to protect your information and keep your identity as confidential as possible, however total confidentiality cannot be guaranteed. Except as required by law, you will not be identified by name or by any other direct personal identifier. The data collected through the app will be encrypted on the smartphone, transferred electronically and stored securely on the data repository and analysis platform using cloud services at Sage Bionetworks. Your contact information, including your name and e-mail address will be stored separately from the study data. We will use a random code number instead of your name on all your study data. This code cannot be used to directly identify you. Information about the code will be kept in a secure system.

+

We will not access your personal contacts, other applications, text or email message content, or websites visited. We will never sell, rent or lease your contact information.

+

Data Use

+

How will the data collected be utilized?

+

The research team will analyze the combined data and report findings back to the community through blogs or scientific publications. Your data, without your name, will be added to the data of other study participants and may be analyzed by the study team. You will have a unique account that you can use to review your own data.

+

Time Commitment

+

How much time will study activities require?

+

This study should take you about 10 minutes each week. We will send notices on your phone asking you to complete these activities and surveys. You may choose to act at your convenience, (either then or later) and you may choose to participate in all or only in some parts of the study. You can adjust the app settings to turn on and off sending data at any time.

+

Study Survey

+

What kinds of questions will the surveys ask?

+

We may periodically ask you to answer questions about yourself (e.g., your sleep, appetite, and your energy and activity level), your medical history (e.g., if you or any of your blood relatives have been given a diagnosis of bipolar disorder), and your current health and symptoms (e.g., if you have been feeling down, depressed, hopeless, or trouble with your concentration) to track changes. You may skip any questions that you do not wish to answer.

+

Study Tasks

+

What kinds of tasks need to be performed?

+

We will ask you to perform specific tasks weekly while holding or using your mobile phone and record sensor data directly from your phone.

+

Withdrawing

+

How can I withdraw from this study?

+

Your participation in this study is voluntary. If you are eligible and decide to participate, we will ask you to provide an e-signature, by signing with your finger on the screen at the end of the enrollment process. We will also send a copy of this consent form, along with your e-signature, to your email address.

+

You do not have to sign this consent form. But if you do not, you will not be able to participate in this research study. You may decide not to participate or you may leave the study at any time. Your decision will not result in any penalty or loss of benefits to which you are entitled.

+

Although you can withdraw from the study at any time, you cannot withdraw the coded study data that have already been distributed. If you withdraw from the study, we will stop collecting new data but the coded data that you have already provided will not be able to be destroyed or deleted.

+

The Study Principal Investigator or the sponsor may also withdraw you from the study without your consent at any time for any reason, including if it is in your best interest, you do not consent to continue in the study after being told of changes in the research that may affect you, or if the study is cancelled.

+

Contact information for the study principal investigators is as follows:

+
    +
  1. Peter C. Nelson, PhD
    Professor of Computer Science
    Dean of Engineering
    University of Illinois at Chicago
    College of Engineering
    851 South Morgan Street
    Room 838 SEO, M/C 159
    Chicago, IL 60607
  2. +
+
    +
  1. Alex Leow, MD PhD
    Associate Professor of Psychiatry and Bioengineering
    The Psychiatric Institute
    1601 West Taylor Street
    Room 584
    Chicago, IL 60612
  2. +
+

Contact information for the research sponsor is as follows:

+ +

+

If you have questions about your rights as a research subject or concerns, complaints, or to offer input you may call the University of Illinois at Chicago (UIC) office for the Protection of Research Subjects (OPRS) at 312-996-1711 or 1-866-789-6215 (toll-free) or e-mail OPRS at uicirb@uic.edu

diff --git a/app/src/main/assets/study_public_key.pem b/app/src/main/assets/study_public_key.pem new file mode 100644 index 0000000000..439d116891 --- /dev/null +++ b/app/src/main/assets/study_public_key.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE7TCCA9WgAwIBAgIHBBFXFu5E4jANBgkqhkiG9w0BAQUFADCBqzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxlMRkwFwYDVQQKDBBT +YWdlIEJpb25ldHdvcmtzMQ8wDQYDVQQLDAZCcmlkZ2UxJDAiBgkqhkiG9w0BCQEW +FWJyaWRnZUlUQHNhZ2ViYXNlLm9yZzErMCkGA1UEAwwiaHR0cHM6Ly93ZWJzZXJ2 +aWNlcy5zYWdlYnJpZGdlLm9yZzAeFw0xNjExMTMwMDIzNTlaFw00NDA0MDEwMDIz +NTlaMIGrMQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0 +bGUxGTAXBgNVBAoMEFNhZ2UgQmlvbmV0d29ya3MxDzANBgNVBAsMBkJyaWRnZTEk +MCIGCSqGSIb3DQEJARYVYnJpZGdlSVRAc2FnZWJhc2Uub3JnMSswKQYDVQQDDCJo +dHRwczovL3dlYnNlcnZpY2VzLnNhZ2VicmlkZ2Uub3JnMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAly7CpnZ4IJ28ob9ur065NQaTEDPf5fO9MNKcm45q +b+E4BJB6ukVOZSd5hzEWzTCFZRLECfXprMjGZqEsRX2Rfuc+9toeCG/K+LtS7t/y +qjAh6qnpWbabWY1BowJP8nVvFUIbyaXDk+SulLD+mrc81cJS5HKghsy6NE4x1Tmx +sKrV6Sq/9lKcbIW03G5uOWHsOMRACNlJ3piUbkeNqmsczfjGSGLhdXLIfWp8BTrd +eAbrfSwrp1odi0Ql+LfkfQxI/EkZu00xKmv2zvHH5OH8/nU7v1xd5W6oXmzjRbZo +lmZSumCA9nK2L0Hx9Qfs3X+XltnaouRv0mSbAR116duthwIDAQABo4IBEjCCAQ4w +DAYDVR0TBAUwAwEB/zCB3gYDVR0jBIHWMIHTgBSVPNx9VnGdHEd1anGgPZyAmgtU +w6GBsaSBrjCBqzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdT +ZWF0dGxlMRkwFwYDVQQKDBBTYWdlIEJpb25ldHdvcmtzMQ8wDQYDVQQLDAZCcmlk +Z2UxJDAiBgkqhkiG9w0BCQEWFWJyaWRnZUlUQHNhZ2ViYXNlLm9yZzErMCkGA1UE +AwwiaHR0cHM6Ly93ZWJzZXJ2aWNlcy5zYWdlYnJpZGdlLm9yZ4IHBBFXFu5E4jAd +BgNVHQ4EFgQUlTzcfVZxnRxHdWpxoD2cgJoLVMMwDQYJKoZIhvcNAQEFBQADggEB +AATg2HmV3PsIQdYzag5cF+fgiGJyQO2NEHVbMpubGcM8axxpQDmCdR45LpHumSdj +qrPLTYGi3T15mkix6XIubiKvnsJxndXDWoB+5PiF2VZpzgWgAKFoI2FhPxwYFath +CLwHlLHPMEgQeaAEzM7nCalJgZ1umr4N6Ilts5jNn0CyaNk0gtwMLu2TYoFdIywC ++IhRHI9amGzMLBaofK6KFXLjYlh1Sh+zanXMJy+QnCmW96NCTqyeLRh05kC8aQJv +iEbKCdJJv9KL+YCdXoC+R0Ih3T0jad5Ov9i3YG+gx8/eiDH5/NZ777QxPFZbZcux +XcYWR6JowCrcZxA77lq3TLg= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/app/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java b/app/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java index ac6c692cde..34c7500fda 100644 --- a/app/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java +++ b/app/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java @@ -19,6 +19,7 @@ import android.app.AlertDialog; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Build; import android.os.IBinder; @@ -60,12 +61,14 @@ import com.anysoftkeyboard.utils.IMEUtil; import com.google.android.voiceime.VoiceRecognitionTrigger; import com.menny.android.anysoftkeyboard.AnyApplication; +import com.menny.android.anysoftkeyboard.BiAffect.BiAManager; import com.menny.android.anysoftkeyboard.BuildConfig; import com.menny.android.anysoftkeyboard.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import android.util.*; /** * Input method implementation for QWERTY-ish keyboard. @@ -90,6 +93,8 @@ public abstract class AnySoftKeyboard extends AnySoftKeyboardIncognito { private int mOrientation = Configuration.ORIENTATION_PORTRAIT; + SharedPreferences spref; + public AnySoftKeyboard() { super(); } @@ -113,6 +118,7 @@ private static boolean isBackWordDeleteChar(int c) { @Override public void onCreate() { super.onCreate(); + spref = getSharedPreferences("login",MODE_PRIVATE); mOrientation = getResources().getConfiguration().orientation; if (!BuildConfig.DEBUG && DeveloperUtils.hasTracingRequested(getApplicationContext())) { try { @@ -187,8 +193,10 @@ private static CondenseType parseCondenseType(String prefCondenseType) { @Override public void onDestroy() { Logger.i(TAG, "AnySoftKeyboard has been destroyed! Cleaning resources.."); + unregisterReceiver(mPackagesChangedReceiver); + final IBinder imeToken = getImeToken(); if (imeToken != null) mInputMethodManager.hideStatusIcon(imeToken); @@ -202,6 +210,11 @@ public void onDestroy() { DeveloperUtils.getTraceFile()), Toast.LENGTH_SHORT) .show(); } + if(!BiAManager.getInstance(AnyApplication.getAppContext()).endSession()){ + Log.i("BiAffect", "End Session Failed in hideWindow"); + }else{ + Log.i("BiAffect", "End Session Successfull in hideWindow"); + } super.onDestroy(); } @@ -217,8 +230,13 @@ public void onStartInputView(final EditorInfo attribute, final boolean restartin Logger.v(TAG, "onStartInputView(EditorInfo{imeOptions %d, inputType %d}, restarting %s", attribute.imeOptions, attribute.inputType, restarting); - super.onStartInputView(attribute, restarting); + if(!BiAManager.getInstance(AnyApplication.getAppContext()).startSession()){ + Log.i("BiAffect", "Start Session Failed"); + }else{ + Log.i("BiAffect", "Start Session Successfull"); + } + super.onStartInputView(attribute, restarting); if (mVoiceRecognitionTrigger != null) { mVoiceRecognitionTrigger.onStartInputView(); } @@ -231,6 +249,7 @@ public void onStartInputView(final EditorInfo attribute, final boolean restartin getInputView().setKeyboardActionType(attribute.imeOptions); updateShiftStateNow(); + } @Override @@ -605,6 +624,8 @@ private void onNonFunctionKey(final int primaryCode, final Key key, final int mu @Override public void onKey(int primaryCode, Key key, int multiTapIndex, int[] nearByKeyCodes, boolean fromUI) { super.onKey(primaryCode, key, multiTapIndex, nearByKeyCodes, fromUI); + Long tsLong = System.currentTimeMillis(); + Log.d("CS_KD_OK", " "+ primaryCode+" "+tsLong); if (primaryCode > 0) { onNonFunctionKey(primaryCode, key, multiTapIndex, nearByKeyCodes, fromUI); @@ -962,6 +983,8 @@ public void onWindowHidden() { super.onWindowHidden(); abortCorrectionAndResetPredictionState(true); + // solve the problem of saving data when user close the keyboard + BiAManager.getInstance(AnyApplication.getAppContext()).endSession(); } private void nextAlterKeyboard(EditorInfo currentEditorInfo) { @@ -997,7 +1020,11 @@ private void sendKeyUp(InputConnection ic, int key) { public void onPress(int primaryCode) { super.onPress(primaryCode); InputConnection ic = getCurrentInputConnection(); - + Log.d("KD", Log.getStackTraceString(new Exception())); + Long tsLong = System.currentTimeMillis(); + Log.d("CS TS -> ", tsLong.toString()); + Log.d("CS From AnySoftKeyboard", Log.getStackTraceString(new Exception())); + Log.d("CS_KD_OP", " "+ primaryCode+" "+tsLong); if (primaryCode == KeyCodes.SHIFT) { mShiftKeyState.onPress(); // Toggle case on selected characters @@ -1020,6 +1047,8 @@ public void onPress(int primaryCode) { public void onRelease(int primaryCode) { super.onRelease(primaryCode); InputConnection ic = getCurrentInputConnection(); + Long tsLong = System.currentTimeMillis(); + Log.d("CS_KD_OR", " "+ primaryCode + " "+tsLong); if (primaryCode == KeyCodes.SHIFT) { mShiftKeyState.onRelease(mMultiTapTimeout, mLongPressTimeout); handleShift(); @@ -1120,6 +1149,12 @@ private void showOptionsMenu() { @Override public void onConfigurationChanged(Configuration newConfig) { + Log.i("Conf", "onConfigurationChanged be called"); + if(!BiAManager.getInstance(AnyApplication.getAppContext()).endSession()){ + Log.i("BiAffect", "End Session Failed"); + }else{ + Log.i("BiAffect", "End Session Successfull/ CONFIGUARTION"); + } super.onConfigurationChanged(newConfig); if (newConfig.orientation != mOrientation) { mOrientation = newConfig.orientation; diff --git a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardBase.java b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardBase.java index 56766594b4..b640d95893 100644 --- a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardBase.java +++ b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardBase.java @@ -25,6 +25,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; +import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -43,6 +44,7 @@ import com.anysoftkeyboard.ui.dev.DeveloperUtils; import com.anysoftkeyboard.utils.ModifierKeyState; import com.menny.android.anysoftkeyboard.AnyApplication; +import com.menny.android.anysoftkeyboard.BiAffect.BiAManager; import com.menny.android.anysoftkeyboard.BuildConfig; import com.menny.android.anysoftkeyboard.R; @@ -325,6 +327,11 @@ public void onDestroy() { mInputSessionDisposables.dispose(); if (getInputView() != null) getInputView().onViewNotRequired(); mInputView = null; + if(!BiAManager.getInstance(AnyApplication.getAppContext()).endSession()){ + Log.i("BiAffect", "End Session Failed in destroy"); + }else{ + Log.i("BiAffect", "End Session Successfull in destroy"); + } super.onDestroy(); } diff --git a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardKeyboardTagsSearcher.java b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardKeyboardTagsSearcher.java index bb9d0912e4..3ed046aa8b 100644 --- a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardKeyboardTagsSearcher.java +++ b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardKeyboardTagsSearcher.java @@ -25,6 +25,7 @@ import android.support.annotation.Nullable; import com.anysoftkeyboard.addons.AddOn; +import com.anysoftkeyboard.base.utils.Logger; import com.anysoftkeyboard.dictionaries.WordComposer; import com.anysoftkeyboard.keyboards.AnyKeyboard; import com.anysoftkeyboard.keyboards.Keyboard; @@ -146,6 +147,7 @@ protected boolean isSuggestionAffectingCharacter(int code) { @Override public void pickSuggestionManually(int index, CharSequence suggestion, boolean withAutoSpaceEnabled) { + Logger.d("psm", "AStagSsearcher148"); if (mWord.isAtTagsSearchState()) { if (index == 0) { //this is a special case for tags-searcher @@ -159,6 +161,7 @@ public void pickSuggestionManually(int index, CharSequence suggestion, boolean w } super.pickSuggestionManually(index, suggestion, withAutoSpaceEnabled); + } private static class NoOpKeyboard extends Keyboard { diff --git a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardPopText.java b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardPopText.java index 160363a135..f7cc9c493c 100644 --- a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardPopText.java +++ b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardPopText.java @@ -20,6 +20,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.anysoftkeyboard.base.utils.Logger; import com.anysoftkeyboard.keyboards.Keyboard; import com.anysoftkeyboard.keyboards.views.AnyKeyboardViewWithExtraDraw; import com.anysoftkeyboard.keyboards.views.extradraw.PopTextExtraDraw; @@ -74,6 +75,7 @@ private void updatePopTextPrefs(String newValue) { public void pickSuggestionManually(int index, CharSequence suggestion, boolean withAutoSpaceEnabled) { //we do not want to pop text when user picks from the suggestions bar mLastKey = null; + Logger.d("psm", "asPOPText"); super.pickSuggestionManually(index, suggestion, withAutoSpaceEnabled); } diff --git a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardSuggestions.java b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardSuggestions.java index 7266302f62..a334002be3 100644 --- a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardSuggestions.java +++ b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardSuggestions.java @@ -10,6 +10,7 @@ import android.support.v4.content.ContextCompat; import android.support.v4.util.Pair; import android.text.TextUtils; +import android.util.Log; import android.util.SparseBooleanArray; import android.view.KeyEvent; import android.view.View; @@ -494,6 +495,7 @@ protected void handleCharacter(final int primaryCode, final Keyboard.Key key, fi if (BuildConfig.DEBUG) { Logger.d(TAG, "handleCharacter: %d, isPredictionOn: %s, mPredicting: %s", primaryCode, isPredictionOn(), TextEntryState.isPredicting()); } + Log.i("KD","rchd here"+key.centerX+" "+key.centerY); mExpectingSelectionUpdateBy = SystemClock.uptimeMillis() + MAX_TIME_TO_EXPECT_SELECTION_UPDATE; if (TextEntryState.isReadyToPredict() && isAlphabet(primaryCode) && !isCursorTouchingWord()) { @@ -741,6 +743,7 @@ public void onText(Keyboard.Key key, CharSequence text) { ic.endBatchEdit(); setSuggestions(mSuggest.getNextSuggestions(mCommittedWord, false), false, false, false); + Logger.d("mCommittedWord", "text"); } protected void setDictionariesForCurrentKeyboard() { @@ -806,6 +809,7 @@ protected void abortCorrectionAndResetPredictionState(boolean disabledUntilNextI mAdditionalCharacterForReverting = false; mJustAutoAddedWord = false; if (disabledUntilNextInputStart) { + Log.d("FROM ABORT", Log.getStackTraceString(new Exception())); Logger.d(TAG, "abortCorrection will abort correct forever"); final KeyboardViewContainerView inputViewContainer = getInputViewContainer(); if (inputViewContainer != null) { @@ -975,11 +979,13 @@ private boolean pickDefaultSuggestion(boolean autoCorrectToPreferred) { } public void pickSuggestionManually(int index, CharSequence suggestion) { + Logger.d("psm", "ASsuggestion981"); pickSuggestionManually(index, suggestion, mAutoSpace); } @CallSuper public void pickSuggestionManually(int index, CharSequence suggestion, boolean withAutoSpaceEnabled) { + Logger.d("psm", "ASsuggestion988"); final String typedWord = mWord.getTypedWord().toString(); final InputConnection ic = getCurrentInputConnection(); @@ -996,13 +1002,14 @@ public void pickSuggestionManually(int index, CharSequence suggestion, boolean w ic.commitCompletion(ci); } mCommittedWord = suggestion; + Logger.d("mCommittedWord", "suggestion"); if (mCandidateView != null) { mCandidateView.clear(); } return; } commitWordToInput(suggestion, false/*user physically picked a word from the suggestions strip. this is not a fix*/); - + Logger.d("CommittedWordtoinput", "1012"); TextEntryState.acceptedSuggestion(mWord.getTypedWord(), suggestion); // Follow it with a space if (withAutoSpaceEnabled && (index == 0 || !mWord.isAtTagsSearchState())) { @@ -1063,6 +1070,7 @@ protected void commitWordToInput(@NonNull CharSequence wordToCommit, boolean cor } } mCommittedWord = wordToCommit; + Logger.d("mCommittedWord", "wordToCommit"); mUndoCommitCursorPosition = UNDO_COMMIT_WAITING_TO_RECORD_POSITION; clearSuggestions(); diff --git a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardWithGestureTyping.java b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardWithGestureTyping.java index 41a0ab7c45..090b853599 100644 --- a/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardWithGestureTyping.java +++ b/app/src/main/java/com/anysoftkeyboard/ime/AnySoftKeyboardWithGestureTyping.java @@ -32,7 +32,7 @@ public abstract class AnySoftKeyboardWithGestureTyping extends AnySoftKeyboardWithQuickText { - private boolean mGestureTypingEnabled; + private final boolean mGestureTypingEnabled = false; protected final Map mGestureTypingDetectors = new HashMap<>(); @Nullable private GestureTypingDetector mCurrentGestureDetector; @@ -56,7 +56,7 @@ public void onCreate() { if (powerState) return false; return gestureTyping; }).subscribe(enabled -> { - mGestureTypingEnabled = enabled; +// mGestureTypingEnabled = enabled; mDetectorStateSubscription.dispose(); if (!mGestureTypingEnabled) { destroyAllDetectors(); @@ -298,6 +298,7 @@ public void onKey(int primaryCode, Keyboard.Key key, int multiTapIndex, int[] ne private void confirmLastGesture(boolean withAutoSpace) { if (TextEntryState.getState() == TextEntryState.State.PERFORMED_GESTURE) { pickSuggestionManually(0, mWord.getTypedWord(), withAutoSpace); + } } diff --git a/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardView.java b/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardView.java index a97c3d9a5d..b59a934be0 100644 --- a/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardView.java +++ b/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardView.java @@ -26,6 +26,7 @@ import android.support.annotation.NonNull; import android.support.v4.view.MotionEventCompat; import android.util.AttributeSet; +import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.animation.Animation; @@ -49,18 +50,23 @@ import com.anysoftkeyboard.rx.GenericOnError; import com.anysoftkeyboard.theme.KeyboardTheme; import com.menny.android.anysoftkeyboard.AnyApplication; +import com.menny.android.anysoftkeyboard.BiAffect.BiAManager; import com.menny.android.anysoftkeyboard.R; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -public class AnyKeyboardView extends AnyKeyboardViewWithExtraDraw implements InputViewBinder { +public class AnyKeyboardView extends AnyKeyboardViewWithExtraDraw implements InputViewBinder{ private static final int DELAY_BEFORE_POPPING_UP_EXTENSION_KBD = 35;// milliseconds private static final String TAG = "AnyKeyboardView"; public static final int DEFAULT_EXTENSION_POINT = -5; private AnimationsLevel mAnimationLevel; + //BiAffect Specific Data Structures + HashMap idToDownTimeMap = new HashMap<>(); + private boolean mExtensionVisible = false; private int mExtensionKeyboardYActivationPoint; private final int mExtensionKeyboardPopupOffset; @@ -193,7 +199,6 @@ public boolean onTouchEvent(@NonNull MotionEvent me) { //I mean, if there isn't any keyboard I'm handling, what's the point? return false; } - if (areTouchesDisabled(me)) { mGestureTypingPathShouldBeDrawn = false; return super.onTouchEvent(me); @@ -201,6 +206,8 @@ public boolean onTouchEvent(@NonNull MotionEvent me) { final int action = MotionEventCompat.getActionMasked(me); + biAffectTouchDataProbe(me); + PointerTracker pointerTracker = getPointerTracker(me); mGestureTypingPathShouldBeDrawn = pointerTracker.isInGestureTyping(); if (mGestureTypingPathShouldBeDrawn) { @@ -280,6 +287,71 @@ public boolean onTouchEvent(@NonNull MotionEvent me) { } } + public void biAffectTouchDataProbe(android.view.MotionEvent me){ + final int action = MotionEventCompat.getActionMasked(me); + final int index = MotionEventCompat.getActionIndex(me); + long eventDownTime; + int pointerId; + switch (action) { + case MotionEvent.ACTION_DOWN: + pointerId = me.getPointerId(index); + idToDownTimeMap.put(pointerId,me.getEventTime()); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), action, me.getPressure(index), + me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + break; + + case MotionEvent.ACTION_MOVE: + pointerId = me.getPointerId(index); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), action, me.getPressure(index), + me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + break; + + + case MotionEvent.ACTION_POINTER_DOWN: + pointerId = me.getPointerId(index); + idToDownTimeMap.put(pointerId,me.getEventTime()); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), MotionEvent.ACTION_DOWN, me.getPressure(index), + me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + break; + + + case MotionEvent.ACTION_UP: + pointerId = me.getPointerId(index); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), action, me.getPressure(index), + me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + break; + + + case MotionEvent.ACTION_POINTER_UP: + pointerId = me.getPointerId(index); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), MotionEvent.ACTION_UP, me.getPressure(index), + me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + break; + + + case MotionEvent.ACTION_OUTSIDE: + pointerId = me.getPointerId(index); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), action, me.getPressure(index), + me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + break; + + + case MotionEvent.ACTION_CANCEL: + pointerId = me.getPointerId(index); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), action, me.getPressure(index), me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + break; + + } + + } + @Override protected void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) { diff --git a/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewBase.java b/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewBase.java index d524c71c69..1caf575852 100644 --- a/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewBase.java +++ b/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewBase.java @@ -47,6 +47,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.util.SparseArray; import android.view.Gravity; import android.view.MotionEvent; @@ -1777,6 +1778,9 @@ protected boolean isFirstDownEventInsideSpaceBar() { private void sendOnXEvent(final int action, final long eventTime, final int x, final int y, PointerTracker tracker) { //CHECKSTYLE:OFF: missingswitchdefault + Long tsLong = System.currentTimeMillis()/1000; + Log.d("CS TS -> ", tsLong.toString()); + Log.i("key detect", "Rchd here"); switch (action) { case MotionEvent.ACTION_DOWN: case 0x00000005:// MotionEvent.ACTION_POINTER_DOWN: @@ -1799,6 +1803,8 @@ protected void onDownEvent(PointerTracker tracker, int x, int y, // Before processing a down event of modifier key, all pointers // already being tracked // should be released. + Long tsLong = System.currentTimeMillis()/1000; + Log.d("CS TS -> ", tsLong.toString()); mPointerQueue.releaseAllPointersExcept(tracker, eventTime); } tracker.onDownEvent(x, y, eventTime); diff --git a/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewWithMiniKeyboard.java b/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewWithMiniKeyboard.java index 2c77d57fc7..4700aab842 100644 --- a/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewWithMiniKeyboard.java +++ b/app/src/main/java/com/anysoftkeyboard/keyboards/views/AnyKeyboardViewWithMiniKeyboard.java @@ -26,6 +26,7 @@ import android.support.annotation.Nullable; import android.support.v4.view.MotionEventCompat; import android.util.AttributeSet; +import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -96,7 +97,6 @@ public boolean onTouchEvent(@NonNull MotionEvent me) { translated.recycle(); return true; } - return super.onTouchEvent(me); } diff --git a/app/src/main/java/com/anysoftkeyboard/keyboards/views/CandidateView.java b/app/src/main/java/com/anysoftkeyboard/keyboards/views/CandidateView.java index 61eb112729..3d1ea46bb2 100644 --- a/app/src/main/java/com/anysoftkeyboard/keyboards/views/CandidateView.java +++ b/app/src/main/java/com/anysoftkeyboard/keyboards/views/CandidateView.java @@ -18,6 +18,7 @@ import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; @@ -28,11 +29,13 @@ import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; +import android.support.v4.view.MotionEventCompat; import android.text.Layout.Alignment; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; @@ -40,21 +43,27 @@ import com.anysoftkeyboard.addons.AddOn; import com.anysoftkeyboard.base.utils.Logger; import com.anysoftkeyboard.ime.AnySoftKeyboardSuggestions; +import com.anysoftkeyboard.keyboards.Keyboard; import com.anysoftkeyboard.overlay.OverlayData; import com.anysoftkeyboard.overlay.ThemeOverlayCombiner; import com.anysoftkeyboard.overlay.ThemeResourcesHolder; import com.anysoftkeyboard.rx.GenericOnError; import com.anysoftkeyboard.theme.KeyboardTheme; import com.menny.android.anysoftkeyboard.AnyApplication; +import com.menny.android.anysoftkeyboard.BiAffect.BiAManager; import com.menny.android.anysoftkeyboard.R; + import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposables; +import static com.anysoftkeyboard.keyboards.views.AnyKeyboardViewBase.NOT_A_KEY; + public class CandidateView extends View implements ThemeableChild { private static final String TAG = "ASK CandidateView"; @@ -66,6 +75,7 @@ public class CandidateView extends View implements ThemeableChild { private final int[] mWordX = new int[MAX_SUGGESTIONS]; private static final int SCROLL_PIXELS = 20; private final ArrayList mSuggestions = new ArrayList<>(); + private final int[] totallength = new int[MAX_SUGGESTIONS]; private final Drawable mSelectionHighlight; private float mHorizontalGap; private final ThemeOverlayCombiner mThemeOverlayCombiner = new ThemeOverlayCombiner(); @@ -257,6 +267,7 @@ protected void onDraw(Canvas canvas) { final ThemeResourcesHolder themeResources = mThemeOverlayCombiner.getThemeResources(); int x = 0; + for (int i = 0; i < count; i++) { CharSequence suggestion = mSuggestions.get(i); if (suggestion == null) { @@ -287,6 +298,12 @@ protected void onDraw(Canvas canvas) { mWordWidth[i] = wordWidth; } + if(i == 0) totallength[i] = wordWidth; + else totallength[i] = totallength[i-1] + wordWidth; + + // Log.d("Totallength", ""+totallength[i]); + + mWordX[i] = x; if (touchX != OUT_OF_BOUNDS_X_CORD && !scrolled @@ -442,6 +459,7 @@ public boolean onTouchEvent(@NonNull MotionEvent me) { final int y = (int) me.getY(); mTouchX = x; + switch (action) { case MotionEvent.ACTION_MOVE: // Fling up!? @@ -461,7 +479,32 @@ public boolean onTouchEvent(@NonNull MotionEvent me) { mService.addWordToDictionary(word.toString()); } } else if (!mNoticing) { + // we only save data when user select word from autosuggestion + //since the word is saved only when Action_up is fired. + //We only put our probe here, not both Action_up and Action_down. + + // save autosuggestion touchtype data + final int index = MotionEventCompat.getActionIndex(me); + // for autosuggestion ,currently saving actiondown time as eventDownTime + long eventDownTime; + int pointerId; + HashMap idToDownTimeMap = new HashMap<>(); + pointerId = me.getPointerId(index); + // Using getDownTime() to get time of EVENT_DOWN as eventDownTime for research + idToDownTimeMap.put(pointerId,me.getDownTime()); + eventDownTime = idToDownTimeMap.get(pointerId); + BiAManager.getInstance(AnyApplication.getAppContext()).addMasterEntry(eventDownTime, me.getEventTime(), action, me.getPressure(index), + me.getX(index), me.getY(index), me.getTouchMajor(index), me.getTouchMinor(index), me.getPointerCount()); + + // save autosuggestion keytype data + float keyCentre_X = totallength[mSelectedIndex- 1] + mWordWidth[mSelectedIndex]/2; + float keyCentre_Y = getTop() + getHeight()/2; + float keyWidth = mWordWidth[mSelectedIndex]; + float keyHeight = getHeight(); + + BiAManager.getInstance(AnyApplication.getAppContext()).addKeyDataOnlyAuto(eventDownTime, keyCentre_X, keyCentre_Y, keyWidth, keyHeight); mService.pickSuggestionManually(mSelectedIndex, mSelectedString); + } else if (mSelectedIndex == 1 && !TextUtils.isEmpty(mJustAddedWord)) { // 1 is the index of "Remove?" Logger.d(TAG, "User wants to remove an added word '%s'", mJustAddedWord); @@ -477,6 +520,7 @@ public boolean onTouchEvent(@NonNull MotionEvent me) { return true; } + public void notifyAboutWordAdded(CharSequence word) { mJustAddedWord = word; ArrayList notice = new ArrayList<>(2); @@ -561,4 +605,5 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, return true; } } + } \ No newline at end of file diff --git a/app/src/main/java/com/anysoftkeyboard/keyboards/views/PointerTracker.java b/app/src/main/java/com/anysoftkeyboard/keyboards/views/PointerTracker.java index 6173fefc8f..6956884af4 100644 --- a/app/src/main/java/com/anysoftkeyboard/keyboards/views/PointerTracker.java +++ b/app/src/main/java/com/anysoftkeyboard/keyboards/views/PointerTracker.java @@ -18,12 +18,15 @@ import android.support.annotation.NonNull; import android.text.TextUtils; +import android.util.Log; import android.view.MotionEvent; import com.anysoftkeyboard.api.KeyCodes; import com.anysoftkeyboard.keyboards.AnyKeyboard.AnyKey; import com.anysoftkeyboard.keyboards.Keyboard.Key; import com.anysoftkeyboard.keyboards.views.AnyKeyboardViewBase.KeyPressTimingHandler; +import com.menny.android.anysoftkeyboard.AnyApplication; +import com.menny.android.anysoftkeyboard.BiAffect.BiAManager; import java.util.Locale; @@ -242,9 +245,17 @@ void onDownEvent(int x, int y, long eventTime) { mKeyAlreadyProcessed = false; mIsRepeatableKey = false; mKeyCodesInPathLength = -1; + + //BiAffect Code Key Probe Start + Key temp = getKey(keyIndex); + BiAManager.getInstance(AnyApplication.getAppContext()).addKeyDataOnlyDownTime(eventTime, temp.getPrimaryCode(), temp.centerX, temp.centerY, temp.width, temp.height); + //BiAffect Code Key Probe End + checkMultiTap(eventTime, keyIndex); if (mListener != null && isValidKeyIndex(keyIndex)) { AnyKey key = (AnyKey) mKeys[keyIndex]; + //Key centre probe biaffect. + //print key details final int codeAtIndex = key.getCodeAtIndex(0, mKeyDetector.isKeyShifted(key)); if (!mProxy.isAtTwoFingersState() && mListener.onGestureTypingInputStart(x, y, key, eventTime)) { @@ -252,6 +263,8 @@ void onDownEvent(int x, int y, long eventTime) { } if (codeAtIndex != 0) { + Long tsLong = System.currentTimeMillis()/1000; + Log.d("CS TS -> ", tsLong.toString()); mListener.onPress(codeAtIndex); //also notifying about first down mListener.onFirstDownKey(codeAtIndex); diff --git a/app/src/main/java/com/anysoftkeyboard/keyboards/views/ProximityKeyDetector.java b/app/src/main/java/com/anysoftkeyboard/keyboards/views/ProximityKeyDetector.java index 62cc8e8761..e2b16bfd4a 100644 --- a/app/src/main/java/com/anysoftkeyboard/keyboards/views/ProximityKeyDetector.java +++ b/app/src/main/java/com/anysoftkeyboard/keyboards/views/ProximityKeyDetector.java @@ -16,6 +16,8 @@ package com.anysoftkeyboard.keyboards.views; +import android.util.Log; + import com.anysoftkeyboard.api.KeyCodes; import com.anysoftkeyboard.keyboards.AnyKeyboard; import com.anysoftkeyboard.keyboards.Keyboard.Key; @@ -45,6 +47,7 @@ public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { final Key[] keys = getKeys(); final int touchX = getTouchX(x); final int touchY = getTouchY(y); + Log.d("CS TS -> ProximityKD", touchX + ","+ touchY); int primaryIndex = AnyKeyboardViewBase.NOT_A_KEY; int closestKey = AnyKeyboardViewBase.NOT_A_KEY; int closestKeyDist = mProximityThresholdSquare + 1; diff --git a/app/src/main/java/com/anysoftkeyboard/ui/settings/GesturesSettingsFragment.java b/app/src/main/java/com/anysoftkeyboard/ui/settings/GesturesSettingsFragment.java index 1b71699847..c2b8550ba7 100644 --- a/app/src/main/java/com/anysoftkeyboard/ui/settings/GesturesSettingsFragment.java +++ b/app/src/main/java/com/anysoftkeyboard/ui/settings/GesturesSettingsFragment.java @@ -20,10 +20,8 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; -import android.support.v7.preference.CheckBoxPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; -import android.view.View; import com.menny.android.anysoftkeyboard.R; @@ -52,31 +50,12 @@ List findPrefs(String... keys) { return prefs; } - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - findPreference(getString(R.string.settings_key_gesture_typing)).setOnPreferenceChangeListener((preference, newValue) -> { - final boolean gestureTypingEnabled = (boolean) newValue; - if (gestureTypingEnabled) { - mAlertDialog = new AlertDialog.Builder(getActivity()).setTitle(R.string.gesture_typing_alert_title) - .setMessage(R.string.gesture_typing_alert_message) - .setPositiveButton(R.string.gesture_typing_alert_button, (dialog, which) -> dialog.dismiss()) - .create(); - mAlertDialog.show(); - } - for (Preference affectedPref : getAffectedPrefs()) { - affectedPref.setEnabled(!gestureTypingEnabled); - } - return true; - }); - } - @Override public void onStart() { super.onStart(); MainSettingsActivity.setActivityTitle(this, getString(R.string.unicode_gestures_quick_text_key_name)); - final boolean gestureTypingEnabled = ((CheckBoxPreference) findPreference(getString(R.string.settings_key_gesture_typing))).isChecked(); + final boolean gestureTypingEnabled = false; for (Preference affectedPref : getAffectedPrefs()) { affectedPref.setEnabled(!gestureTypingEnabled); } diff --git a/app/src/main/java/com/anysoftkeyboard/ui/settings/MainFragment.java b/app/src/main/java/com/anysoftkeyboard/ui/settings/MainFragment.java index 1ebcbd917c..150e53f3a0 100644 --- a/app/src/main/java/com/anysoftkeyboard/ui/settings/MainFragment.java +++ b/app/src/main/java/com/anysoftkeyboard/ui/settings/MainFragment.java @@ -28,6 +28,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import android.widget.Toast; import com.anysoftkeyboard.PermissionsRequestCodes; import com.anysoftkeyboard.base.utils.Logger; @@ -41,6 +42,7 @@ import com.anysoftkeyboard.ui.settings.setup.SetupSupport; import com.anysoftkeyboard.ui.tutorials.ChangeLogFragment; import com.menny.android.anysoftkeyboard.AnyApplication; +import com.menny.android.anysoftkeyboard.BiAffect.bridge.BiAffectBridge; import com.menny.android.anysoftkeyboard.BuildConfig; import com.menny.android.anysoftkeyboard.R; @@ -142,7 +144,9 @@ public boolean onOptionsItemSelected(MenuItem item) { FragmentChauffeurActivity activity = (FragmentChauffeurActivity) getActivity(); switch (item.getItemId()) { case R.id.about_menu_option: - activity.addFragmentToUi(new AboutAnySoftKeyboardFragment(), TransitionExperiences.DEEPER_EXPERIENCE_TRANSITION); + Toast.makeText( getContext(), "Initiating single upload", Toast.LENGTH_SHORT ).show(); + BiAffectBridge.getInstance().singleUpload(); +// activity.addFragmentToUi(new AboutAnySoftKeyboardFragment(), TransitionExperiences.DEEPER_EXPERIENCE_TRANSITION); return true; case R.id.tweaks_menu_option: activity.addFragmentToUi(new MainTweaksFragment(), TransitionExperiences.DEEPER_EXPERIENCE_TRANSITION); @@ -210,7 +214,7 @@ public void onClick(View widget) { public void onStart() { super.onStart(); MainSettingsActivity.setActivityTitle(this, getString(R.string.how_to_pointer_title)); - + Logger.d(TAG, "MainFragment!!!!!!!!!!" ); View notConfiguredBox = getView().findViewById(R.id.not_configured_click_here_root); //checking if the IME is configured final Context context = getActivity().getApplicationContext(); @@ -400,4 +404,4 @@ public void onPermissionsDenied(@NonNull String[] grantedPermissions, /*no-op - Main-Activity handles this case*/ } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/anysoftkeyboard/ui/settings/MainSettingsActivity.java b/app/src/main/java/com/anysoftkeyboard/ui/settings/MainSettingsActivity.java index 2d2dd74680..3b5446d91b 100644 --- a/app/src/main/java/com/anysoftkeyboard/ui/settings/MainSettingsActivity.java +++ b/app/src/main/java/com/anysoftkeyboard/ui/settings/MainSettingsActivity.java @@ -22,7 +22,9 @@ import android.support.design.widget.BottomNavigationView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; +import android.view.Gravity; import android.view.MenuItem; +import android.widget.Toast; import com.anysoftkeyboard.quicktextkeys.ui.QuickTextKeysBrowseFragment; import com.menny.android.anysoftkeyboard.R; @@ -47,6 +49,14 @@ protected void onCreate(Bundle icicle) { mTitle = getTitle(); + Bundle extras = getIntent().getExtras(); + if( null != extras ) { + String message = extras.getString( "LoggedInMsg" ); + Toast toast = Toast.makeText( getApplicationContext(), message, Toast.LENGTH_SHORT ); + toast.setGravity( Gravity.CENTER, 0, 0 ); + toast.show(); + } + mBottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation); mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { diff --git a/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/ConsentPage.java b/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/ConsentPage.java new file mode 100644 index 0000000000..1f0c12fa1e --- /dev/null +++ b/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/ConsentPage.java @@ -0,0 +1,76 @@ +package com.anysoftkeyboard.ui.settings.setup; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.webkit.WebView; +import android.widget.Button; +import android.widget.Toast; + +import com.anysoftkeyboard.ui.settings.MainSettingsActivity; +import com.menny.android.anysoftkeyboard.LauncherSettingsActivity; +import com.menny.android.anysoftkeyboard.R; +import com.menny.android.anysoftkeyboard.BiAffect.bridge.BiAffectBridge; + +public class ConsentPage extends AppCompatActivity { + WebView webView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + // use the Bridge sdk to check if the user already logged in, if so skip the login page + if (BiAffectBridge.getInstance().isUserLoggedIn()) { + String loggedInMsg = "You've already logged in"; + // already logged in, use intent to pass the message and jump to the next page + Intent toLogging = new Intent(ConsentPage.this, MainSettingsActivity.class); + toLogging.putExtra("LoggedInMsg", loggedInMsg); + startActivity(toLogging); +// Log.d("test", "logined in"); + } + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_consent_page); + // init webView + webView = (WebView) findViewById(R.id.consentWebView); + // displaying content in WebView from html file that stored in assets folder + webView.getSettings().setJavaScriptEnabled(true); + webView.loadUrl("file:///android_asset/" + "consent_full.html"); + + // Agree button + final Button agree_button = (Button) findViewById(R.id.btn_agree); + agree_button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(ConsentPage.this, LoggingPage.class); + startActivity(intent); + } + }); + + // Cancel button + final Button exit_button = (Button) findViewById(R.id.btn_exit); + exit_button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory( Intent.CATEGORY_HOME ); + homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(homeIntent); + } + }); + + // Disagree button + final Button disagree_button = (Button) findViewById(R.id.btn_disagree); + disagree_button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory( Intent.CATEGORY_HOME ); + homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(homeIntent); + } + }); + } +} diff --git a/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/LoggingPage.java b/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/LoggingPage.java new file mode 100644 index 0000000000..a70d8b4b80 --- /dev/null +++ b/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/LoggingPage.java @@ -0,0 +1,169 @@ +package com.anysoftkeyboard.ui.settings.setup; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AppCompatActivity; +import android.view.Gravity; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.menny.android.anysoftkeyboard.BiAffect.bridge.BiAffectBridge; +import com.menny.android.anysoftkeyboard.LauncherSettingsActivity; +import com.menny.android.anysoftkeyboard.R; + +import org.sagebionetworks.bridge.rest.exceptions.ConsentRequiredException; +import org.sagebionetworks.bridge.rest.model.UserSessionInfo; + +import java.util.regex.Pattern; + +import rx.Subscription; + +public class LoggingPage extends AppCompatActivity { + private String _email, _password; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_logging_page); + + EditText email_edText = (EditText) findViewById(R.id.input_email); + EditText password_edText = (EditText) findViewById(R.id.input_password); + + final Button button = (Button) findViewById(R.id.btn_login); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //get the inputs of email and password + String input_email = email_edText.getText().toString(); + String input_password = password_edText.getText().toString(); + // reject the empty email + if(input_email.matches("")){ + Toast toast = Toast.makeText(getApplicationContext(), "Email should not be empty", Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, -130); + toast.show(); + return; + } + + //check the email is valid + if(!isValidEmail(input_email)){ + Toast toast = Toast.makeText(getApplicationContext(), "Not a valid email address", Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, -130); + toast.show(); + return; + } + // reject the empty password + if(input_password.matches("")){ + Toast toast = Toast.makeText(getApplicationContext(), "Password should not be empty", Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, -130); + toast.show(); + return; + } + // use the bridge sdk to sign in + signIn(input_email, input_password); + } + }); + + final Button exit_button = (Button) findViewById(R.id.btn_exit); + exit_button.setOnClickListener( new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory( Intent.CATEGORY_HOME ); + homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(homeIntent); + } + }); + + } + + + // for email validation + private boolean isValidEmail(String email) { + Pattern pattern = EMAIL_ADDRESS_PATTERN; + return pattern.matcher(email).matches(); + } + + // sign in function + private void signIn(String email, String password) { + _email = email; + _password = password; + // Use Bridge to login + Subscription s = BiAffectBridge.getInstance() + .logIn( email, password ) + .subscribe( this::onSignInSuccess, this::onSignInError ); + } + + private void onSignInSuccess( UserSessionInfo __ ) { + String SuccessMsg = "You've successfully logged in"; + // login successfully, use intent to pass the message and jump to the next page + Intent toLogging = new Intent( LoggingPage.this, LauncherSettingsActivity.class ); + toLogging.putExtra( "successMsg", SuccessMsg ); + startActivity( toLogging ); + // Log.d("success","success! move on to the next screen"); + } + + private void onSignInError( Throwable throwable ) { + if( throwable instanceof ConsentRequiredException ) { + UserSessionInfo session = ((ConsentRequiredException)throwable).getSession(); + Subscription s = BiAffectBridge.getInstance() + .bypassConsent( session ) + .subscribe( (__) -> signIn( _email, _password ), + (error) -> onSignInError( new Throwable( "consent" ) ) ); + return; + } + // get the error message from backend + String errorMsg = throwable.getMessage(); + ErrorMsgDialogFragment dialog = new ErrorMsgDialogFragment(); + // handle the error message + switch (errorMsg) { + case "Account not found.": + errorMsg = "Incorrect/Unregistered email or incorrect password"; + break; + case "Unable to resolve host \"webservices.sagebridge.org\": No address associated with hostname": + errorMsg = "Internet Error, please check your internet connection"; + break; + case "consent": + errorMsg = "Consent error"; + break; + default: + errorMsg = "Unknown Error"; + } + // use setArguments to pass the error message to the dialog + Bundle args = new Bundle(); + args.putString("msgName", errorMsg); + dialog.setArguments(args); + // show the dialog to the user + dialog.show(getSupportFragmentManager(), "errorMsg"); +// Log.d("login error", errorMsg); + } + + //the valid email pattern, which refers to + //https://en.wikipedia.org/wiki/Email_address#RFC_specification + public static final Pattern EMAIL_ADDRESS_PATTERN = Pattern.compile( + "(?:[a-z0-9A-Z!#$%&'*+/=?^_`{|}~-]{1,64}+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])" + ); + + // use DialogFragment class to show the error message + public static class ErrorMsgDialogFragment extends DialogFragment { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + // get the content of error message + builder.setMessage(getArguments().getString("msgName")) + .setNegativeButton("Confirm", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ErrorMsgDialogFragment.this.getDialog().cancel(); + } + }); + return builder.create(); + } + } +} diff --git a/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/WizardPageWelcomeFragment.java b/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/WizardPageWelcomeFragment.java index 257d3b318b..f5619cdcb2 100644 --- a/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/WizardPageWelcomeFragment.java +++ b/app/src/main/java/com/anysoftkeyboard/ui/settings/setup/WizardPageWelcomeFragment.java @@ -40,6 +40,7 @@ protected int getPageLayoutId() { public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); view.findViewById(R.id.go_to_start_setup).setOnClickListener(this); + //view.findViewById(R.id.start_to_Logging).setOnClickListener(this); view.findViewById(R.id.setup_wizard_welcome_privacy_action).setOnClickListener(this); mDemoAnyKeyboardView = view.findViewById(R.id.demo_keyboard_view); @@ -53,7 +54,10 @@ protected boolean isStepCompleted(@NonNull Context context) { @Override public void onClick(View v) { switch (v.getId()) { + case R.id.go_to_start_setup: + // Intent toLogging = new Intent(getContext(), LoggingPage.class); + //startActivity(toLogging); final SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getContext()).edit(); editor.putBoolean(STARTED_PREF_KEY, true); SharedPreferencesCompat.EditorCompat.getInstance().apply(editor); diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/AnyApplication.java b/app/src/main/java/com/menny/android/anysoftkeyboard/AnyApplication.java index 9a010a3595..edcb5ea84c 100644 --- a/app/src/main/java/com/menny/android/anysoftkeyboard/AnyApplication.java +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/AnyApplication.java @@ -16,7 +16,6 @@ package com.menny.android.anysoftkeyboard; -import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -54,6 +53,8 @@ import com.anysoftkeyboard.theme.KeyboardThemeFactory; import com.anysoftkeyboard.ui.tutorials.TutorialsProvider; +import org.sagebionetworks.bridge.android.BridgeApplication; + import java.io.File; import java.util.ArrayList; import java.util.List; @@ -66,9 +67,14 @@ import io.reactivex.subjects.ReplaySubject; import io.reactivex.subjects.Subject; -public class AnyApplication extends Application { +public class AnyApplication extends BridgeApplication { private static final String TAG = "ASK_APP"; + /** + * Created by Sreetama Banerjee on 4/22/2019. + * reason : to allow all components of project to get appcontext + */ + private static Context appContext; static final String PREF_KEYS_FIRST_INSTALLED_APP_VERSION = "settings_key_first_app_version_installed"; static final String PREF_KEYS_FIRST_INSTALLED_APP_TIME = "settings_key_first_time_app_installed"; @@ -170,8 +176,19 @@ public void onCreate() { mCompositeDisposable.add(NightMode.observeNightModeState(this, R.string.settings_key_night_mode_app_theme_control, R.bool.settings_default_true) .subscribe(nightMode -> AppCompatDelegate.setDefaultNightMode(nightMode ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO))); mNightModeSubject.onNext((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES); + + /** + * Created by Sreetama Banerjee on 4/22/2019. + * reason : to allow all components of project to get appcontext + */ + appContext = getApplicationContext(); + } + + public static Context getAppContext1() { + return appContext; } + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -304,6 +321,15 @@ public static RxSharedPrefs prefs(Context context) { } } + /** + * Created by Sreetama Banerjee on 4/22/2019. + * reason : to allow all components of project to get appcontext + */ + + public static Context getAppContext() { + return appContext; + } + public List getInitialWatermarksList() { return new ArrayList<>(); } diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/AccelerometerDataWorker.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/AccelerometerDataWorker.java new file mode 100644 index 0000000000..edc96831cf --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/AccelerometerDataWorker.java @@ -0,0 +1,31 @@ +package com.menny.android.anysoftkeyboard.BiAffect; + +import android.util.Log; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.BiADatabaseManager; + +public class AccelerometerDataWorker implements Runnable { + BiAManager sharedInstance; + BiADatabaseManager mBiADatabaseManager; + final int FREQUENCY = 10; + final int oneSecondMillis = 1000; + public AccelerometerDataWorker(BiADatabaseManager databaseManager){ + super(); + sharedInstance = BiAManager.getInstance(null); + mBiADatabaseManager = databaseManager; + } + + @Override + public void run() { + while(sharedInstance.sessionRunning){ + long currentTime = System.currentTimeMillis(); + mBiADatabaseManager.insertAccelerometerData(currentTime, sharedInstance.current_accelerometer_x, sharedInstance.current_accelerometer_y, sharedInstance.current_accelerometer_z); + //Log.i("ACC", "PUSHED"); + try { + Thread.sleep(oneSecondMillis/FREQUENCY); + }catch (InterruptedException e){ + //Do nothing + } + } + } +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/BiADataProcessorInterface.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/BiADataProcessorInterface.java new file mode 100644 index 0000000000..9df6ac9f1b --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/BiADataProcessorInterface.java @@ -0,0 +1,54 @@ +package com.menny.android.anysoftkeyboard.BiAffect; + +public interface BiADataProcessorInterface { + interface TouchDataProcessorInterface { + //All the calls to process the touch data will be provided over here + //EventDownTime + //EventTime + //EventAction + //Pressure of the event + //x_cordinate + //y_cordinate + //major axis + //minor axis + //Number of touches..this is being done for the purpose of testing if we are overlapping any calls or something + + //One thing i need to make sure is that that data type of the touch cordinates remain the same for the view and the touches. + + boolean addMasterEntry(long eventDownTime, long eventTime, int eventAction, float pressure, float x_cord, float y_cord, float major_axis, float minor_axis, int touches); + } + + interface KeyDataProcessorInterface { + //All the calls to process the key data will be provided over here + //1->Key press down time..this is not the onpress call time as the onpress call follows after this probe, but we are really not interested in, + //when the listener handle the call, rather the time when the touch to this call was initiated + //2->Key press release time, this is again not the time of onRelease call to the listener but the time when the user lifted his/her finger, + //motionevent action_up was recorded, again sticking with the requirements + //3->the type of the key pressed + //This call might have some issue when it comes to handling the calls from the mini keyboard, the only thing which i can be sure about that right now, + //is that there will not be an action_up in the database for the corresponding longpress key, which i have decided for now that i will leave that, + //on the discretion of the person performing analysis of the data on how to treat that entry + + //One api will be to add a call to record the keyDownTime and the associated key code along with all values + + boolean addKeyDataOnlyDownTime(long eventDownTime, int key_id, float keyCentre_X, float keyCentre_Y, float keyWidth, float keyHeight); + + } + + interface DeviceDataProcessorInterface{ + //This interface is going to put the device specific data in the table, it will only happen once + //we need to figureout how can i achieve that + //We will be putting in 4 values in here for now, which are android version, pixel density, screen size and phone model + void sendDeviceData(); + + } + + interface SessionDataProcessorInterface{ + //This will contain the methods which are specific to record the session of the keyboard, + //This will not be processed by any worker thread, instead it will be pushed in to db directly and the id of the current session will be + //maintained in the BiAManager.java + boolean startSession(); + boolean endSession(); + } + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/BiAManager.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/BiAManager.java new file mode 100644 index 0000000000..dd4a3117bf --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/BiAManager.java @@ -0,0 +1,431 @@ +package com.menny.android.anysoftkeyboard.BiAffect; +import android.arch.persistence.room.Room; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Build; +import android.os.SystemClock; +import android.util.DisplayMetrics; +import android.util.Log; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.BiADatabaseManager; +import com.menny.android.anysoftkeyboard.BiAffect.Database.BiAffect_Database; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.SessionData; + +import java.time.Instant; +import java.util.LinkedHashMap; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Semaphore; + +public class BiAManager implements BiADataProcessorInterface.TouchDataProcessorInterface, + BiADataProcessorInterface, SensorEventListener, + BiADataProcessorInterface.KeyDataProcessorInterface, + BiADataProcessorInterface.SessionDataProcessorInterface, + BiADataProcessorInterface.DeviceDataProcessorInterface +{ + + //Context specific holders + Context mContext; + //Two data structures to hold the master records, + static final int TOUCH_BUFFER_SIZE = 20; + TouchDataPOJO[] t1; + TouchDataPOJO[] t2; + boolean bucket1; + int currentIndex; + Semaphore t1_Sempahore = new Semaphore(1); + Semaphore t2_Sempahore = new Semaphore(1); + //Sensor specific data holders + private SensorManager mSensorManager; + float current_accelerometer_x; + float current_accelerometer_y; + float current_accelerometer_z; + //Key Data specific data holders + static final int KEY_BUFFER_SIZE = 20; + KeyDataPOJO[] k1; + KeyDataPOJO[] k2; + boolean bucketk1; + int currentIndexKey; + Semaphore k1_Semaphore = new Semaphore(1); + Semaphore k2_Semaphore = new Semaphore(1); + + //Session specific data + long currentRunningSession; + boolean sessionRunning; + + //Database + BiADatabaseManager mBiADatabaseManager; + + //TIME OFFSETS + long birthTime; + long upTimeAtBirth; + long offset; + + @Override + public void onSensorChanged(SensorEvent event) { + if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){ + current_accelerometer_x = event.values[0]; + current_accelerometer_y = event.values[1]; + current_accelerometer_z = event.values[2]; + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + + private static BiAManager shared_instance = null; + + private BiAManager(Context context){ + this.birthTime = System.currentTimeMillis(); + this.upTimeAtBirth = SystemClock.uptimeMillis(); + this.offset = birthTime - upTimeAtBirth; + + this.mContext = context; + //This will initialise the dbManager when the constructor of BiAManager is called... + //This wont contain anything as such + + //Initialising all the buffers + this.t1 = new TouchDataPOJO[TOUCH_BUFFER_SIZE]; + this.t2 = new TouchDataPOJO[TOUCH_BUFFER_SIZE]; + this.bucket1=true; + for(int i=0; i "+currentRunningSession); + + Thread accelerometerDataCollector = new Thread(new AccelerometerDataWorker(mBiADatabaseManager)); + accelerometerDataCollector.start(); + + Thread temp = new Thread(new Runnable() { + @Override + public void run() { + mBiADatabaseManager.insertSessionData(currentRunningSession); + } + }); + temp.start(); + Log.i("CS_BiAffect_Sess","-----------Start SESSION End-------------"); + return true; + } + + @Override + public boolean endSession(){ + if(!this.sessionRunning) return false; + Log.i("CS_BiAffect_Sess","-----------END SESSION START-------------"); + Log.i("CS_BiAffect_Sess",Log.getStackTraceString(new Exception())); + this.sessionRunning = false; + long sessionEndTime = System.currentTimeMillis(); + Thread temp = new Thread(new Finaliser(this.currentRunningSession, sessionEndTime, mBiADatabaseManager)); + temp.start(); + Log.i("CS_BiAffect_Sess","-----------END SESSION END-------------"); + return true; + } + + //Touch Data calls + @Override + public boolean addMasterEntry(long eventDownTime, long eventTime, int eventAction, float pressure, float x_cord, float y_cord, float major_axis, float minor_axis, int touches){ + TouchDataPOJO[] temp; + Semaphore temp_Semaphore; + //assigning correct buffer; + if(this.bucket1){ + //t1 is supposed to be used + temp = this.t1; + temp_Semaphore = t1_Sempahore; + }else{ + temp = this.t2; + temp_Semaphore = t2_Sempahore; + } + + String event = ""; + if(eventAction == 0){ + event = "Action_Down"; + } + else if(eventAction == 1){ + event = "Action_Up"; + } + else{ + event = "Action_Move"; + } + + //lock the buffer, if lock fails, there is something wrong with the code + try { + temp_Semaphore.acquire(); + temp[currentIndex].eventDownTime = eventDownTime + offset; + temp[currentIndex].eventTime = eventTime + offset; + temp[currentIndex].eventAction = event; + temp[currentIndex].pressure = pressure; + temp[currentIndex].x_cord = x_cord; + temp[currentIndex].y_cord = y_cord; + temp[currentIndex].major_axis = major_axis; + temp[currentIndex].minor_axis = minor_axis; + temp[currentIndex].touches = touches; + temp[currentIndex].accelerometer_x = current_accelerometer_x; + temp[currentIndex].accelerometer_y = current_accelerometer_y; + temp[currentIndex].accelerometer_z = current_accelerometer_z; + temp[currentIndex].used = true; + Log.i("CS_BiAffect","---------------------------------"); + Log.i("CS_BiAffect","Index->"+currentIndex); + temp[currentIndex].printYourself(); + Log.i("CS_BiAffect","---------------------------------"); + //Log.i("CS_BiAffect_App_context",AnyApplication.getAppContext().toString()); + + }catch(InterruptedException e){ + Log.i("CS_BiAffect", "failed to acquire lock on semaphore"); + }finally { + temp_Semaphore.release(); + if(temp[currentIndex].used){ + if(currentIndex == temp.length-1){ + Log.i("CS_BiAffect","-----------BUFFER CHANGE-------------"+this.bucket1); + //We can kickoff a worker thread from here to take all the pojos and insert it into the database + //We will pass the number of the last buffer being used and then expect the thread to infer from that which one + //needs to be emptied + Thread t = new Thread(new TouchDataWorker(this.bucket1, mBiADatabaseManager)); + t.start(); + //Time to change the buffer and put all the things in the second from next + this.currentIndex = 0; + if(this.bucket1){ + bucket1=false; + }else{ + bucket1=true; + } + }else{ + currentIndex++; + } + } + } + return true; + } + + public boolean addlog(long eventDownTime, long eventTime, int eventAction, float pressure, float x_cord, float y_cord, float major_axis, float minor_axis, int touches){ + Log.d("Autosuggestion", "eventDownTime:" + eventDownTime); + Log.d("Autosuggestion", "eventAction:" + eventAction); + Log.d("Autosuggestion", "pressure:" + pressure); + Log.d("Autosuggestion", "x_cord" + x_cord); + Log.d("Autosuggestion", "y_cord:" + y_cord); + Log.d("Autosuggestion", "major_axis:" + major_axis); + Log.d("Autosuggestion", "minor_axis" + minor_axis); + + return true; + } + + //Key data specific calls + @Override + public boolean addKeyDataOnlyDownTime(long eventDownTime, int keyCode, float keyCentre_X, float keyCentre_Y, float keyWidth, float keyHeight) { + KeyDataPOJO[] temp; + Semaphore temp_Semaphore; + String keyType = null; + + // Inorder to protect the users' privacy, instead of saving the keyType code, we save the category of the keyType + // Here are the categories of the key codes + // Alpha-Numeric + // Space + // Backspace + // switchToNumber + // switchToLetter + // Punctuations (.?!,:;-[]{}()'") + // Symbols (emojis etc) + +// Log.d("code", keyCode + ""); + // Alpha-Numeric + if ((keyCode >= 48 && keyCode <= 57) || (keyCode >= 97 && keyCode <= 122)) { + keyType = new String("Alpha-Numeric"); + } + // Space + else if (keyCode == 32) { + keyType = new String("Space"); + // Backspace + } else if (keyCode == -5) { + keyType = new String("Backspace"); + // switch to Numbers + } else if (keyCode == -2) { + keyType = new String("switchToNumber"); + // switch to Letters + } else if (keyCode == - 99) { + keyType = new String("switchToLetter"); + // Punctuations (.?!,:;-[]{}()'") + } else if ((keyCode >= 33 && keyCode <= 34) || (keyCode >= 39 && keyCode <= 41) || (keyCode >= 44 && keyCode <= 46) || (keyCode >= 58 && keyCode <= 59) + || keyCode == 63 || keyCode == 91 || keyCode == 93 || keyCode == 123 || keyCode == 125) { + keyType = new String("Punctuations"); + } + // Symbols + else { + keyType = new String("Symbols"); + } + + //assigning correct buffer; + if(this.bucketk1){ + //t1 is supposed to be used + temp = this.k1; + temp_Semaphore = k1_Semaphore; + }else{ + temp = this.k2; + temp_Semaphore = k2_Semaphore; + } + //Lock the semaphore + try { + temp_Semaphore.acquire(); + temp[currentIndexKey].eventDownTime = eventDownTime + offset; + temp[currentIndexKey].eventUpTime = 0 + offset; + temp[currentIndexKey].keyType = keyType; + temp[currentIndexKey].keyCentre_X = keyCentre_X; + temp[currentIndexKey].keyCentre_Y = keyCentre_Y; + temp[currentIndexKey].keyWidth = keyWidth; + temp[currentIndexKey].keyHeight = keyHeight; + temp[currentIndexKey].used = true; + Log.i("CS_BiAffect_K","---------------------------------"); + Log.i("CS_BiAffect_K","Index->"+currentIndexKey); + temp[currentIndexKey].printYourself(); + Log.i("CS_BiAffect_K","---------------------------------"); + + }catch (InterruptedException e){ + Log.i("CS_BiAffect_K", "failed to acquire lock on semaphore"); + }finally { + temp_Semaphore.release(); + if(temp[currentIndexKey].used){ + if(currentIndexKey == KEY_BUFFER_SIZE-1){ + //Time to switch the key buffer + Log.i("CS_BiAffect_K","-----------KEY BUFFER CHANGE-------------"+this.bucketk1); + //We can kickoff a worker thread from here to take all the pojos and insert it into the database + //We will pass the number of the last buffer being used and then expect the thread to infer from that which one + //needs to be emptied + Thread t = new Thread(new KeyDataWorker(this.bucketk1, mBiADatabaseManager)); + t.start(); + //Time to change the buffer and put all the things in the second from next + this.currentIndexKey = 0; + if(this.bucketk1){ + bucketk1=false; + }else{ + bucketk1=true; + } + }else{ + currentIndexKey++; + } + } + + } + return false; + } + + public boolean addKeyDataOnlyAuto(long eventDownTime, float keyCentre_X, float keyCentre_Y, float keyWidth, float keyHeight) { + KeyDataPOJO[] temp; + Semaphore temp_Semaphore; + String keyType = "AutoSuggestion"; + + + + + //assigning correct buffer; + if(this.bucketk1){ + //t1 is supposed to be used + temp = this.k1; + temp_Semaphore = k1_Semaphore; + }else{ + temp = this.k2; + temp_Semaphore = k2_Semaphore; + } + //Lock the semaphore + try { + temp_Semaphore.acquire(); + temp[currentIndexKey].eventDownTime = eventDownTime + offset; + temp[currentIndexKey].eventUpTime = 0 + offset; + temp[currentIndexKey].keyType = keyType; + temp[currentIndexKey].keyCentre_X = keyCentre_X; + temp[currentIndexKey].keyCentre_Y = keyCentre_Y; + temp[currentIndexKey].keyWidth = keyWidth; + temp[currentIndexKey].keyHeight = keyHeight; + temp[currentIndexKey].used = true; + Log.i("CS_BiAffect_K","---------------------------------"); + Log.i("CS_BiAffect_K","Index->"+currentIndexKey); + temp[currentIndexKey].printYourself(); + + + }catch (InterruptedException e){ + Log.i("CS_BiAffect_K", "failed to acquire lock on semaphore"); + }finally { + temp_Semaphore.release(); + if(temp[currentIndexKey].used){ + if(currentIndexKey == KEY_BUFFER_SIZE-1){ + //Time to switch the key buffer + Log.i("CS_BiAffect_K","-----------KEY BUFFER CHANGE-------------"+this.bucketk1); + //We can kickoff a worker thread from here to take all the pojos and insert it into the database + //We will pass the number of the last buffer being used and then expect the thread to infer from that which one + //needs to be emptied + Thread t = new Thread(new KeyDataWorker(this.bucketk1, mBiADatabaseManager)); + t.start(); + //Time to change the buffer and put all the things in the second from next + this.currentIndexKey = 0; + if(this.bucketk1){ + bucketk1=false; + }else{ + bucketk1=true; + } + }else{ + currentIndexKey++; + } + } + + } + return false; + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiADBInterface.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiADBInterface.java new file mode 100644 index 0000000000..bf80852621 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiADBInterface.java @@ -0,0 +1,59 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database; + +import android.content.Context; + +public interface BiADBInterface { + interface TouchDataInterface { + + //All the calls to process the touch data will be provided over here + // no data processing handled by data collector. raw data sent as parameters. + //EventDownTime + //EventTime + //EventAction + //Pressure of the event + //x_cordinate + //y_cordinate + //major axis + //minor axis + + // Currently according to design, single insert works efficiently. thus only this implemented. + void insertTouchData(long eventDownTime, long eventTime, String eventAction, + float pressure, float x_cord, float y_cord, float majorAxis, float minorAxis); + } + + interface KeyDataInterface { + + //All the calls to process the key data will be provided over here + //no data processing handled by data collector. raw data sent as parameters. + // eventDownTime which acts as ID + // keytype code + // key center X + // key center Y + // kry width + //key height + + //Api will be to add a call to record the keyDownTime and the associated key code along with all values + void insertKeyData(long keyDownTime, String keyCode, float centre_X, float centre_Y, float width, float height); + } + + + interface SessionDataInterface { + + //This will contain the methods which are specific to record the session of the keyboard, + + void insertSessionData(long sessionStartTime); + void updateSessionData(long sessionStartTime, long sessionEndTime); + } + + interface AccelerometerData { + // insert accelerometer data sampled every 1/1000th of a second through out a session + void insertAccelerometerData(long time, float x, float y, float z); + } + + interface DeviceData { + //This interface is going to put the device specific data in the table, it will only happen once + // this is handled in the implementation + void insertDeviceData(Context c); + } +} + diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiADatabaseManager.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiADatabaseManager.java new file mode 100644 index 0000000000..051d584327 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiADatabaseManager.java @@ -0,0 +1,280 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database; + +import android.arch.persistence.room.Room; +import android.content.Context; +import android.os.Build; +import android.util.DisplayMetrics; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.AccelerometerData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.DeviceData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.KeyTypeData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.SessionData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.TouchTypeData; + +import java.util.List; + +// All data processing work handeled here +// calls to room DB and CRUD operations are called from here +// APIs exposed to Data Collection system are defined here. +public class BiADatabaseManager implements BiADBInterface.TouchDataInterface,BiADBInterface.KeyDataInterface, +BiADBInterface.SessionDataInterface,BiADBInterface.AccelerometerData,BiADBInterface.DeviceData { + + + private static BiADatabaseManager sharedInstance; + private static BiAffect_Database mDatabaseInstance; + + // made BiADatabaseManager Singleton. Only one Instance is available throughout execution and shared by everyone. + public static synchronized BiADatabaseManager getInstance(Context appContext) { + if(sharedInstance==null){ + sharedInstance = new BiADatabaseManager(appContext); + } + return sharedInstance; + } + + private BiADatabaseManager(Context appContext) { + + // The Room database instance is created inside constructor. + // As the constructor will be called only once the Database in turn, has only one instance created + // This is done to prevent having multiple instances of the database opened at the same time. + mDatabaseInstance = Room.databaseBuilder(appContext, BiAffect_Database.class, + "BiAffect_Database.db") + .fallbackToDestructiveMigration() + .build(); + + } + + //exposed API for processing Session data + @Override + public void insertSessionData(long sessionStartTime){ + + // Creating an object for Session entity class and initialising the data members + // This object will be used as parameter for Room @Insert method + // In essence The object encapsulates the values to be inserted into the table + SessionData currentSessionData = new SessionData(); + currentSessionData.sessionStartTime = sessionStartTime; + + // Add a wrapper for the insert() method for Session Table. + // Insert will throw error is datamembers annotated with @nonnull have null values + // Insert will throw error is datamembers annotated with @primaryKey is a duplicate + // thus in try catch block + try{ + mDatabaseInstance.mSession_dao().insertSessionStartTime(currentSessionData); + }catch (Exception e) + { + // do nothing + } + + } + + @Override + public void updateSessionData(long sessionStartTime, long sessionEndTime) { + + // Creating an object for Session entity class and initialising the data members + // This object will be used as parameter for Room @Insert method + // In essence The object encapsulates the values to be updated into the table, + // Room takes the object, extracts value of the data member annotated with @PrimaryKey, + // finds a record in table with that value for the Primary Key column + // If a record matches, that row is updated + SessionData currentSessionData = new SessionData(); + currentSessionData.sessionStartTime = sessionStartTime; + currentSessionData.sessionEndTime = sessionEndTime; + + // Add a wrapper for the update() method for Session Table. + try { + mDatabaseInstance.mSession_dao().updateSessionEndTime(currentSessionData); + } catch (Exception e) { + //do nothing + } + } + + /** + * Gets a list of TouchTypeData associated with a particular keyDownTime. + * + * @param time epoch representing the keyDownTime "foreign key" + * @return List of TouchTypeData associated with the given keyDownTime + */ + public List getTouchTypeData( long time ) { + return mDatabaseInstance.mTouch_dao().getTouchTypeData( time ); + } + + //exposed Api for touch data + @Override + public void insertTouchData(long eventDownTime, long eventTime, String eventAction, float pressure, float x_cord, float y_cord, float majorAxis, float minorAxis){ + + // Creating an object for TouchTypeData entity class and initialising the data members + // This object will be used as parameter for Room @Insert method + // In essence The object encapsulates the values to be inserted into the table + TouchTypeData data = new TouchTypeData(); + data.eventDownTime = eventDownTime; + data.eventActionTime = eventTime; + data.eventAction = eventAction; + data.force = pressure; + data.touch_xcord = x_cord; + data.touch_ycord = y_cord; + data.touch_majorAxis = majorAxis; + data.touch_minorAxis = minorAxis; + + // Add a wrapper for the insert() method for TouchTypeData Table. + // Insert will throw error is datamembers annotated with @nonnull have null values + // Insert will throw error is datamembers annotated with @primaryKey is a duplicate + // thus in try catch block + try{ + mDatabaseInstance.mTouch_dao().insertSingleTouchDataEntry(data); + }catch (Exception e){ + // do nothing + } + } + + /** + * Gets a list of KeyTypeData that occurred between the start and end times. + * + * @param start epoch representing the earliest KeyTypeData returned + * @param end epoch representing the latest KeyTypeData returned + * @return List of KeyTypeData between the start and end times + */ + public List getKeyTypeData( long start, long end ) { + return mDatabaseInstance.mKey_dao().getKeyTypeData( start, end ); + } + + //exposed api for keydata + @Override + public void insertKeyData(long keyDownTime, String keyCode, float centre_X, float centre_Y, float width, float height){ + + // Creating an object for KeyTypeData entity class and initialising the data members + // This object will be used as parameter for Room @Insert method + // In essence The object encapsulates the values to be inserted into the table + KeyTypeData data = new KeyTypeData(); + data.keyDownTime_id = keyDownTime; + data.keyTypeCode = keyCode; + data.keyCentre_X = centre_X; + data.keyCentre_Y = centre_Y; + data.key_Width = width; + data.key_Height = height; + + // Add a wrapper for the insert() method for KeyTypeData Table. + // Insert will throw error is datamembers annotated with @nonnull have null values + // Insert will throw error is datamembers annotated with @primaryKey is a duplicate + // thus in try catch block + try { + mDatabaseInstance.mKey_dao().insertSingleKeyData(data); + }catch(Exception e){ + //do nothing + //Log.i("BiAffect", "Exception caught in insertKeyData at "+keyDownTime+ "\n"); + } + } + + /** + * Gets a list of AccelerometerData that occurred between the start and end times. + * + * @param start epoch representing the earliest AccelerometerData returned + * @param end epoch representing the latest AccelerometerData returned + * @return List of AccelerometerData between the start and end times + */ + public List getAccelerometerData( long start, long end ) { + return mDatabaseInstance.mAccelerometer_dao().getAccelerometerData( start, end ); + } + + //accelerometer exposed apis + @Override + public void insertAccelerometerData(long time, float x, float y, float z){ + + // Creating an object for Accelerometer entity class and initialising the data members + // This object will be used as parameter for Room @Insert method + // In essence The object encapsulates the values to be inserted into the table + AccelerometerData data = new AccelerometerData(); + data.time = time; + data.Acc_X = x; + data.Acc_Y = y; + data.Acc_Z = z; + + // Add a wrapper for the insert() method for TouchTypeData Table. + // Insert will throw error is datamembers annotated with @nonnull have null values + // Insert will throw error is datamembers annotated with @primaryKey is a duplicate + // thus in try catch block + try{ + mDatabaseInstance.mAccelerometer_dao().insertAccelerometerData(data); + }catch (Exception e){ + // do nothing + }} + + /** + * Gets the singleton device data for this device. + * + * @return The singleton DeviceData for this device + */ + public DeviceData getDeviceData() { + return mDatabaseInstance.mDevice_dao().getDeviceData(); + } + + //api exposed for Device Data + @Override + public void insertDeviceData(Context c){ + + // Handling device data being inserted only once + // Instered when the app is first installed and never again. + // Not inserted every time a session starts + // avoid dupliaction of data + + int size=-1; + // Add a wrapper for the getAllIds() method in Device DAO for DeviceData Table. + // This method is a SQL SELECT query + // It gives a warning if only some field names match. + // It gives an error if no field names match. + try { + size = mDatabaseInstance.mDevice_dao().getAllIds().length; + //Log.i("DEVICEDATA", " "+size); + }catch (Exception e){ + // Log.i("DEVICEDATA", "EXCEPTION"+e.getLocalizedMessage()); + } + + // getAllIds() returns the IDs of all the records stored in DeviceData Table + // getAllIds().length will return 0 if and only if no record exists in the DeviceData table + // this is a check done for single insertion + if(size==0){ + + // No probes on Data Collection system for Device Data. Directly collected and Proccessed here. + + // Getting device SDK vesion + String release = Build.VERSION.RELEASE; + int sdkVersion = Build.VERSION.SDK_INT; + String FINALVERSION = "Android SDK: " + sdkVersion + " (" + release +")"; + + // Getting device display metrics - pixel density, width, height (in pixels) + DisplayMetrics metrics = c.getResources().getDisplayMetrics(); + int densityDPI = metrics.densityDpi; + float densityLogical = metrics.density; + int widthPixels = metrics.widthPixels; + int heightPixels = metrics.heightPixels; + + // Getting device manufacturer and model details + String manufacturer = Build.MANUFACTURER; + String model = Build.MODEL; + + + //insert the data into db + + // Creating an object for DeviceData entity class and initialising the data members + // This object will be used as parameter for Room @Insert method + // In essence The object encapsulates the values to be inserted into the table + DeviceData data = new DeviceData(); + data.androidVersion = FINALVERSION; + data.pixelDensityDpi = densityDPI; + data.pixelDensityLogical = densityLogical; + data.deviceWidthPixel = widthPixels; + data.deviceHeightPixel = heightPixels; + data.manufacturer = manufacturer; + data.phoneModel = model; + + // Add a wrapper for the insert() method for DeviceData Table. + // Insert will throw error is datamembers annotated with @nonnull have null values + // Insert will throw error is datamembers annotated with @primaryKey is a duplicate + // thus in try catch block + try { + mDatabaseInstance.mDevice_dao().insertDeviceData(data); + }catch (Exception e){ + // do nothing + } + + } + } +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiAffect_Database.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiAffect_Database.java new file mode 100644 index 0000000000..a32c704c92 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/BiAffect_Database.java @@ -0,0 +1,36 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database; + +import android.arch.persistence.room.*; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.DAO.Accelerometer_DAO; +import com.menny.android.anysoftkeyboard.BiAffect.Database.DAO.Device_DAO; +import com.menny.android.anysoftkeyboard.BiAffect.Database.DAO.Key_DAO; +import com.menny.android.anysoftkeyboard.BiAffect.Database.DAO.Session_DAO; +import com.menny.android.anysoftkeyboard.BiAffect.Database.DAO.Touch_DAO; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.AccelerometerData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.DeviceData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.KeyTypeData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.SessionData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.TouchTypeData; + + +/* +Room class must be abstract and extend RoomDatabase. This class is the boiler plate for the database to be created +Usually, you only need one instance of the Room database for the whole app. +*/ + +//Annotate the class to be a Room database, declare the entities that belong in the database and set the version number. +// Listing the entities will create tables in the database. +@Database(entities = {SessionData.class, TouchTypeData.class, KeyTypeData.class, + AccelerometerData.class, DeviceData.class}, version = 1, exportSchema = false) +public abstract class BiAffect_Database extends RoomDatabase { + + // Defining the DAOs that work with the database. + // Since gave all of the DAOs public access, no need to explicity provide abstract "getter" method for each @Dao. + // Definition required in this class so that Room knows which transactions should be handled for each table. + public abstract Session_DAO mSession_dao(); + public abstract Touch_DAO mTouch_dao(); + public abstract Key_DAO mKey_dao(); + public abstract Accelerometer_DAO mAccelerometer_dao(); + public abstract Device_DAO mDevice_dao(); +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Accelerometer_DAO.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Accelerometer_DAO.java new file mode 100644 index 0000000000..ee189e8946 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Accelerometer_DAO.java @@ -0,0 +1,28 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.DAO; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.OnConflictStrategy; +import android.arch.persistence.room.Query; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.AccelerometerData; + +import java.util.List; + +// DAO includes methods that offer abstract access to your app's database. +@Dao +public interface Accelerometer_DAO { + + // When you create a DAO method and annotate it with @Insert, + // Room generates an implementation that inserts all parameters into the database in a single transaction. + + // When an applicable constraint violation occurs, the IGNORE resolution algorithm skips the one row that contains the constraint violation + // and continues processing subsequent rows of the SQL statement as if nothing went wrong. + // Other rows before and after the row that contained the constraint violation are inserted or updated normally. + // No error is returned when the IGNORE conflict resolution algorithm is used. + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insertAccelerometerData(AccelerometerData data); + + @Query( "SELECT * FROM accelerometerdata WHERE time >= (:start) AND time <= (:end)" ) + List getAccelerometerData( long start, long end ); +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Device_DAO.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Device_DAO.java new file mode 100644 index 0000000000..b46104ed1c --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Device_DAO.java @@ -0,0 +1,40 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.DAO; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.OnConflictStrategy; +import android.arch.persistence.room.Query; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.DeviceData; + +// DAO includes methods that offer abstract access to your app's database. +@Dao +public interface Device_DAO { + + // When you create a DAO method and annotate it with @Insert, + // Room generates an implementation that inserts all parameters into the database in a single transaction. + + + // When an applicable constraint violation occurs, the IGNORE resolution algorithm skips the one row that contains the constraint violation + // and continues processing subsequent rows of the SQL statement as if nothing went wrong. + // Other rows before and after the row that contained the constraint violation are inserted or updated normally. + // No error is returned when the IGNORE conflict resolution algorithm is used. + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insertDeviceData(DeviceData data); + + // @Query is the main annotation used in DAO classes. + // It allows you to perform read/write operations on a database. + /*Each @Query method is verified at compile time, so if there is a problem with the query, + a compilation error occurs instead of a runtime failure. + */ + //Room also verifies the return value of the query + // If the name of the field in the returned object doesn't match the corresponding column names in the query response, + // Room alerts you in one of the following two ways: + //It gives a warning if only some field names match. + //It gives an error if no field names match. + @Query("SELECT id FROM DeviceData") + int[] getAllIds(); + + @Query( "SELECT * FROM DeviceData LIMIT 1" ) + DeviceData getDeviceData(); +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Key_DAO.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Key_DAO.java new file mode 100644 index 0000000000..f03fd58ef1 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Key_DAO.java @@ -0,0 +1,30 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.DAO; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.OnConflictStrategy; +import android.arch.persistence.room.Query; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.KeyTypeData; + +import java.util.List; + +// DAO includes methods that offer abstract access to your app's database. +@Dao +public interface Key_DAO { + + // When you create a DAO method and annotate it with @Insert, + // Room generates an implementation that inserts all parameters into the database in a single transaction. + + // When an applicable constraint violation occurs, the IGNORE resolution algorithm skips + // the one row that contains the constraint violation + // and continues processing subsequent rows of the SQL statement as if nothing went wrong. + // Other rows before and after the row that contained the constraint violation are inserted or updated normally. + // No error is returned when the IGNORE conflict resolution algorithm is used. + + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insertSingleKeyData(KeyTypeData data); + + @Query( "SELECT * FROM keytypedata WHERE keyDownTime_id >= (:start) AND keyDownTime_id <= (:end)" ) + List getKeyTypeData( long start, long end ); +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Session_DAO.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Session_DAO.java new file mode 100644 index 0000000000..058364205e --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Session_DAO.java @@ -0,0 +1,30 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.DAO; + +import android.arch.persistence.room.*; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.SessionData; + + +// DAO includes methods that offer abstract access to your app's database. +@Dao +public interface Session_DAO { + + // When you create a DAO method and annotate it with @Insert, + // Room generates an implementation that inserts all parameters into the database in a single transaction. + + // When an applicable constraint violation occurs, the IGNORE resolution algorithm skips the one row that contains the constraint violation + // and continues processing subsequent rows of the SQL statement as if nothing went wrong. + // Other rows before and after the row that contained the constraint violation are inserted or updated normally. + // No error is returned when the IGNORE conflict resolution algorithm is used. + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insertSessionStartTime(SessionData currentSessionData); + + // The Update convenience method modifies a set of entities, given as parameters, in the database. + // It uses a query that matches against the primary key of each entity. + @Update + int updateSessionEndTime(SessionData currentSessionData); + @Query("SELECT * FROM SESSIONDATA") + SessionData[] getAll(); + + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Touch_DAO.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Touch_DAO.java new file mode 100644 index 0000000000..987e1f5c70 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/DAO/Touch_DAO.java @@ -0,0 +1,28 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.DAO; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.OnConflictStrategy; +import android.arch.persistence.room.Query; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.TouchTypeData; + +import java.util.List; + +// DAO includes methods that offer abstract access to your app's database. +@Dao +public interface Touch_DAO { + + // When you create a DAO method and annotate it with @Insert, + // Room generates an implementation that inserts all parameters into the database in a single transaction. + + // When an applicable constraint violation occurs, the IGNORE resolution algorithm skips the one row that contains the constraint violation + // and continues processing subsequent rows of the SQL statement as if nothing went wrong. + // Other rows before and after the row that contained the constraint violation are inserted or updated normally. + // No error is returned when the IGNORE conflict resolution algorithm is used. + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insertSingleTouchDataEntry(TouchTypeData data); + + @Query( "SELECT * FROM touchtypedata WHERE eventDownTime == (:time)" ) + List getTouchTypeData( long time ); +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/AccelerometerData.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/AccelerometerData.java new file mode 100644 index 0000000000..7117864dbf --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/AccelerometerData.java @@ -0,0 +1,46 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.Models; + +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.PrimaryKey; + +import io.reactivex.annotations.NonNull; + +@Entity +public class AccelerometerData { + + //Id is an autogenerated primary key. It keeps track of number of rows in the table + @PrimaryKey(autoGenerate = true) + @NonNull + public int id; + + // timestamp in milliseconds when the accelerometer data was recorded + @NonNull + public long time; + + /* + * Acceleration force along the x axis (including gravity). + * Unit of measure : m/s2 + * */ + @NonNull + public float Acc_X; + + /* + * Acceleration force along the y axis (including gravity). + * Unit of measure : m/s2 + * */ + @NonNull + public float Acc_Y; + + /* + * Acceleration force along the z axis (including gravity). + * Unit of measure : m/s2 + * */ + @NonNull + public float Acc_Z; + + public AccelerometerData(){ + //Default constructor + // as all data members are public no need for getter and setter methods. + // each data member defined in an @Entity class must be either public or have a getter method + } +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/DeviceData.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/DeviceData.java new file mode 100644 index 0000000000..bc121fd5d6 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/DeviceData.java @@ -0,0 +1,58 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.Models; + +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.PrimaryKey; + +import io.reactivex.annotations.NonNull; + +//@Entity annotates that this class is a table schema and each data member declared in this class corresponds to a table column +@Entity +public class DeviceData { + + //Id is an autogenerated primary key. It keeps track of number of rows in the table + @PrimaryKey(autoGenerate = true) + public int Id; + + // SDK version and release info + @NonNull + public String androidVersion; + + /* + * The logical density of the display. This is a scaling factor for the Density Independent Pixel unit, + * one DIP is one pixel on an approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), + * provides the baseline of the system's display. + * Thus on a 160dpi screen this density value will be 1; on a 120 dpi screen it would be .75; etc. + This value does not exactly follow the real screen size (as given by xdpi and ydpi, + rather is used to scale the size of the overall UI in steps based on gross changes in the display dpi. + For example, a 240x320 screen will have a density of 1 even if its width is 1.8", 1.3", etc. + However, if the screen resolution is increased to 320x480 but the screen size remained 1.5"x2" then the density would be increased (probably to 1.5). + * */ + @NonNull + public float pixelDensityLogical; + + // The screen density expressed as dots-per-inch. + @NonNull + public int pixelDensityDpi; + + // The absolute width of the available display size in pixels. + @NonNull + public int deviceWidthPixel; + + // The absolute height of the available display size in pixels. + @NonNull + public int deviceHeightPixel; + + // phone manufacturer information + @NonNull + public String manufacturer; + + // model information + @NonNull + public String phoneModel; + + public DeviceData(){ + //Default constructor + // as all data members are public no need for getter and setter methods. + // each data member defined in an @Entity class must be either public or have a getter method + } +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/KeyTypeData.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/KeyTypeData.java new file mode 100644 index 0000000000..60334aec37 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/KeyTypeData.java @@ -0,0 +1,51 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.Models; + +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.PrimaryKey; + +import io.reactivex.annotations.NonNull; + +/* +For each key pressed, the data related to the key is constant. But each key press has multiple touch events being fired and we are capturing data for each event. +* To avoid having duplicate key data, we have normalised the data of touch and key +* */ + +//@Entity annotates that this class is a table schema and each data member declared in this class corresponds to a table column +@Entity +public class KeyTypeData { + /*This Column is a foreign key in TouchTypeData Table. + eventDownTime is the time when the motion event - ACTION_DOWN is fired for a touch. + Each key press will have multiple actions associated with it. We store data for each action fired during the course of each key press. + To group the records to the corresponding key press we store the eventdowntime for each data record as eventDownTime is unique for each key press. + */ + /* To correlate the key pressed at each "key press" we are using eventdowntime as a primary key in this table as keyDownTime_id. + Therefore all the records in TouchTypeData Table having eventdowntime value equal to keyDownTime_id value are data collected for that key press + * */ + @PrimaryKey + @NonNull + public long keyDownTime_id; + + // We do not store the key pressed. Instead we have grouped the keys into different categories - alphanumeric, backspace, autocorrect, suggestion, other. Codes of these categories are stored. + @NonNull + public String keyTypeCode; + + // The centre of the key pressed - X coord + // In pixels + @NonNull + public float keyCentre_X; + + // The centre of the key pressed - Y coord + // In pixels + @NonNull + public float keyCentre_Y; + + // The width of the key pressed + // In pixels + @NonNull + public float key_Width; + + // The width of the key pressed + // In pixels + @NonNull + public float key_Height; +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/SessionData.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/SessionData.java new file mode 100644 index 0000000000..b82673bfc7 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/SessionData.java @@ -0,0 +1,28 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.Models; + +import android.arch.persistence.room.*; + +import io.reactivex.annotations.NonNull; + +//@Entity annotates that this class is a table schema and each data member declared in this class corresponds to a table column +@Entity +public class SessionData { + + // A session starts when the keyboard comes into view + // sessionStartTime is the timestamp in milliseconds when the keybaord comes into view, starting a session + @PrimaryKey + @NonNull + public long sessionStartTime; + + // A session ends when the keyboard goes out of view. + // sessionEndTime is the timestamp in milliseconds when the keybaord goes out of view, ending a session + public long sessionEndTime; + + public SessionData(){ + //Default constructor + // as all data members are public no need for getter and setter methods. + // each data member defined in an @Entity class must be either public or have a getter method + + } + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/TouchTypeData.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/TouchTypeData.java new file mode 100644 index 0000000000..c8c304107d --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Database/Models/TouchTypeData.java @@ -0,0 +1,65 @@ +package com.menny.android.anysoftkeyboard.BiAffect.Database.Models; + +import android.arch.persistence.room.ColumnInfo; +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.PrimaryKey; + +import com.menny.android.anysoftkeyboard.BiAffect.TouchDataPOJO; + +import io.reactivex.annotations.NonNull; + +//@Entity annotates that this class is a table schema and each data member declared in this class corresponds to a table column +@Entity +public class TouchTypeData { + //Id is an autogenerated primary key. It keeps track of number of rows in the table + @PrimaryKey(autoGenerate = true) + @NonNull + public int id; + + /* eventDownTime is the time when the motion event - ACTION_DOWN is fired for a touch. + Each key press will have multiple actions associated with it. We store data for each action fired during the course of each key press. + To group the records to the corresponding key press we store the eventdowntime for each data record as eventDownTime is unique for each key press. + */ + @NonNull + public long eventDownTime; + + // stores the time when this motion event action was fires for the key press. + @NonNull + public long eventActionTime; + + // stores the code for type of motion event action that was fired at that key press. 0 for ACTION_DOWN, 2 for ACTION_MOVE, 1 for ACTION_UP + @NonNull + public String eventAction; + + // stores the pressure of key press recorded at the moment the event action in question was fired. + @NonNull + public float force; + + // stores the point of touch - X coordinate in pixel recorded at the moment the event action in question was fired. + @NonNull + public float touch_xcord; + + // stores the point of touch - Y coordinate in pixel recorded at the moment the event action in question was fired. + @NonNull + public float touch_ycord; + + // stores The length of the major axis of an ellipse that describes the touch area at the point of contact + // recorded at the moment the event action in question was fired. + // the length is reported in pixels + @NonNull + public float touch_majorAxis; + + + // stores The length of the minor axis of an ellipse that describes the touch area at the point of contact + // recorded at the moment the event action in question was fired. + // the length is reported in pixels + @NonNull + public float touch_minorAxis; + + public TouchTypeData(){ + //Default constructor + // as all data members are public no need for getter and setter methods. + // each data member defined in an @Entity class must be either public or have a getter method + } + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Finaliser.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Finaliser.java new file mode 100644 index 0000000000..5e19bedbaa --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/Finaliser.java @@ -0,0 +1,191 @@ +package com.menny.android.anysoftkeyboard.BiAffect; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.text.TextUtils; +import android.util.Log; + +import com.google.gson.Gson; +import com.menny.android.anysoftkeyboard.BiAffect.Database.BiADatabaseManager; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.DeviceData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.KeyTypeData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.TouchTypeData; +import com.menny.android.anysoftkeyboard.BiAffect.bridge.Session; + +import org.joda.time.DateTime; +import org.sagebionetworks.bridge.android.manager.BridgeManagerProvider; +import org.sagebionetworks.bridge.data.Archive; +import org.sagebionetworks.bridge.data.JsonArchiveFile; + +import java.util.List; +import java.util.Locale; + +import rx.Subscription; + +public class Finaliser implements Runnable { + + private static String _appVersion; + private static String _phoneInfo; + + long startTime; + long endTime; + BiAManager sharedInstance; + int count=0; + BiADatabaseManager mBiADatabaseManager; + + private Subscription uploadSubscription; + + public Finaliser(long startTime, long endTime, BiADatabaseManager databaseManager){ + super(); + this.startTime = startTime; + this.endTime = endTime; + this.sharedInstance = BiAManager.getInstance(null); + mBiADatabaseManager = databaseManager; + } + + @Override + public void run() { + Log.i("CS_BiAffect_E_T","-----------FINALISER START-------------"); + try { + //This acquires all the semaphores first + sharedInstance.k1_Semaphore.acquire(); + sharedInstance.k2_Semaphore.acquire(); + sharedInstance.t1_Sempahore.acquire(); + sharedInstance.t2_Sempahore.acquire(); + + int max = sharedInstance.KEY_BUFFER_SIZE > sharedInstance.TOUCH_BUFFER_SIZE?sharedInstance.KEY_BUFFER_SIZE:sharedInstance.TOUCH_BUFFER_SIZE; + + for(int i=0; i"+count); + Log.i("CS_BiAffect_E_T","-----------FINALISER END-------------"); + + } + } + + /** + * Creates a Session object that represents this session that just got finalized. + * Queues the upload of the Session object. + * Takes care of unsubscribing correctly from the upload queue. + * + * @return Subscription that needs to be referenced by uploadSubscription in order to support unsubscribing + */ + private Subscription upload() { + Session session = new Session( startTime, endTime ); + + try { //we want to wait for 100ms because that's the delay for AccelerometerDataWorker logic. + Thread.sleep( 100 ); + } catch( InterruptedException ignore ) {} + + session.addAccelerometerData( mBiADatabaseManager.getAccelerometerData( startTime, endTime ) ); + + List keys = mBiADatabaseManager.getKeyTypeData( startTime, endTime ); + for( KeyTypeData key : keys ) { + Session.Keylog keylog = new Session.Keylog( key); + for( TouchTypeData touch : mBiADatabaseManager.getTouchTypeData( key.keyDownTime_id ) ) { + keylog.addTouch( touch ); + } + session.addKeylog( keylog ); + } + + Archive.Builder builder = Archive.Builder.forActivity( "KeyboardSession" ); + builder.addDataFile( new JsonArchiveFile( "Session.json", + new DateTime( endTime ), + new Gson().toJson( session ) ) ); + + if( TextUtils.isEmpty( _appVersion ) ) { + try { + Context context = BridgeManagerProvider.getInstance().getApplicationContext(); + PackageInfo info = context.getPackageManager() + .getPackageInfo( context.getPackageName(), + 0 ); + _appVersion = String.format( Locale.ENGLISH, + "%s(%d)", + info.versionName, + info.versionCode ); + } catch( PackageManager.NameNotFoundException ignore ) { + } finally { + if( TextUtils.isEmpty( _appVersion ) ) { + builder.withAppVersionName( "Unknown" ); + } else { + builder.withAppVersionName( _appVersion ); + } + } + } else { + builder.withAppVersionName( _appVersion ); + } + + if( TextUtils.isEmpty( _phoneInfo ) ) { + DeviceData deviceData = mBiADatabaseManager.getDeviceData(); + _phoneInfo = String.format( Locale.ENGLISH, + "%s %s %s|dp:%s dpi:%s w:%s h:%s", + deviceData.manufacturer, + deviceData.phoneModel, + deviceData.androidVersion, + deviceData.pixelDensityLogical, + deviceData.pixelDensityDpi, + deviceData.deviceWidthPixel, + deviceData.deviceHeightPixel ); + } + builder.withPhoneInfo( _phoneInfo ); + + return BridgeManagerProvider.getInstance() + .getUploadManager() + .queueUpload( String.valueOf( startTime ), builder.build() ) + .subscribe( + success -> { + Log.d( "Bridge SDK Upload", "upload queued successfully" ); + if( null != uploadSubscription ) { + uploadSubscription.unsubscribe(); + } + }, error -> { + error.printStackTrace(); + if( null != uploadSubscription ) { + uploadSubscription.unsubscribe(); + } + } ); + } + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/KeyDataPOJO.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/KeyDataPOJO.java new file mode 100644 index 0000000000..b1dde45efc --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/KeyDataPOJO.java @@ -0,0 +1,47 @@ +package com.menny.android.anysoftkeyboard.BiAffect; + +import android.util.Log; + +public class KeyDataPOJO { + + boolean used; + long eventDownTime; + long eventUpTime; + String keyType; + float keyCentre_X; + float keyCentre_Y; + float keyWidth; + float keyHeight; + + public KeyDataPOJO(){ + + } + + public boolean markUnused(){ + this.used = false; + this.eventDownTime=-1; + this.eventUpTime=-1; + this.keyType=null; + this.keyCentre_X=-1; + this.keyCentre_Y=-1; + this.keyWidth=-1; + this.keyHeight=-1; + + return true; + } + + public boolean validatePOJO(){ + return (this.used && this.eventDownTime!=-1); + } + + public void printYourself(){ + Log.i("CS_BiAffect_K", "eventDownTime->"+this.eventDownTime); + Log.i("CS_BiAffect_K", "eventUpTime->"+this.eventUpTime); + Log.i("CS_BiAffect_K", "keyId->"+this.keyType); + Log.i("CS_BiAffect_K", "keyCentre_X->"+this.keyCentre_X); + Log.i("CS_BiAffect_K", "keyCentre_Y->"+this.keyCentre_Y); + Log.i("CS_BiAffect_K", "keyWidth->"+this.keyWidth); + Log.i("CS_BiAffect_K", "keyHeight->"+this.keyHeight); + } + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/KeyDataWorker.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/KeyDataWorker.java new file mode 100644 index 0000000000..0b0011686d --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/KeyDataWorker.java @@ -0,0 +1,53 @@ +package com.menny.android.anysoftkeyboard.BiAffect; + +import android.util.Log; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.BiADatabaseManager; + +import java.util.concurrent.Semaphore; + +public class KeyDataWorker implements Runnable { + + boolean bucketk1; + BiAManager sharedInstance; + BiADatabaseManager mBiADatabaseManager; + public KeyDataWorker(boolean bucketk1, BiADatabaseManager databaseManager){ + super(); + this.bucketk1=bucketk1; + mBiADatabaseManager = databaseManager; + } + + @Override + public void run() { + sharedInstance = BiAManager.getInstance(null); + KeyDataPOJO[] temp; + Semaphore temp_Semaphore; + if(this.bucketk1){ + //Need to lock the first bucket + temp = sharedInstance.k1; + temp_Semaphore = sharedInstance.k1_Semaphore; + }else{ + //lock second bucket + temp = sharedInstance.k2; + temp_Semaphore = sharedInstance.k2_Semaphore; + } + + //The rest will proceed as it is + try { + temp_Semaphore.acquire(); + Log.i("CS_BiAffect_K","---------KEY BUFFER EMPTY START-----------"+this.bucketk1); + for(KeyDataPOJO k:temp){ + if(k.used && k.validatePOJO()){ + mBiADatabaseManager.insertKeyData(k.eventDownTime, k.keyType, k.keyCentre_X, k.keyCentre_Y, k.keyWidth, k.keyHeight); + k.markUnused(); + } + } + }catch (InterruptedException e){ + + }finally { + temp_Semaphore.release(); + Log.i("CS_BiAffect_K","---------KEY BUFFER EMPTY END-----------"+this.bucketk1); + + } + } +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/TouchDataPOJO.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/TouchDataPOJO.java new file mode 100644 index 0000000000..92141a1974 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/TouchDataPOJO.java @@ -0,0 +1,64 @@ +package com.menny.android.anysoftkeyboard.BiAffect; + +import android.util.Log; + +public class TouchDataPOJO{ + boolean used; + long eventDownTime; + long eventTime; + String eventAction; + float pressure; + float x_cord; + float y_cord; + float major_axis; + float minor_axis; + float accelerometer_x; + float accelerometer_y; + float accelerometer_z; + int touches; + + + public TouchDataPOJO(){ + //I really dont need to initialise anything cos everything is primitive data type + } + + public boolean markUnused(){ + this.used = false; + this.eventDownTime=0; + this.eventTime=0; + this.eventAction=null; + this.pressure = 0.0f; + this.x_cord = 0.0f; + this.y_cord = 0.0f; + this.major_axis=0.0f; + this.minor_axis=0.0f; + this.accelerometer_x = 0.0f; + this.accelerometer_y = 0.0f; + this.accelerometer_z = 0.0f; + this.touches=-1; + + return true; + } + + public boolean validatePOJO(){ + return (this.used && this.eventAction!=null && this.touches!=-1); + } + + public void printYourself(){ + + Log.i("CS_BiAffect", "eventDownTime->"+this.eventDownTime); + Log.i("CS_BiAffect", "eventTime->"+this.eventTime); + Log.i("CS_BiAffect", "eventAction->"+this.eventAction); + Log.i("CS_BiAffect", "pressure->"+this.pressure); + Log.i("CS_BiAffect", "x_cord->"+this.x_cord); + Log.i("CS_BiAffect", "y_cord->"+this.y_cord); + Log.i("CS_BiAffect", "major_axis->"+this.major_axis); + Log.i("CS_BiAffect", "minor_axis->"+this.minor_axis); + Log.i("CS_BiAffect", "touches->"+this.touches); + Log.i("CS_BiAffect", "Acc_X->"+this.accelerometer_x); + Log.i("CS_BiAffect", "Acc_Y->"+this.accelerometer_y); + Log.i("CS_BiAffect", "Acc_Z->"+this.accelerometer_z); + } + + //I think i will used event action and touches to check the validity of the pojo class +} \ No newline at end of file diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/TouchDataWorker.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/TouchDataWorker.java new file mode 100644 index 0000000000..50e6461c68 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/TouchDataWorker.java @@ -0,0 +1,48 @@ +package com.menny.android.anysoftkeyboard.BiAffect; + +import android.util.Log; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.BiADatabaseManager; + +import java.util.concurrent.Semaphore; + +public class TouchDataWorker implements Runnable { + boolean bucket1; + BiAManager sharedInstance; + BiADatabaseManager mBiADatabaseManager; + TouchDataWorker(boolean bucket1, BiADatabaseManager databaseManager){ + super(); + this.bucket1 = bucket1; + mBiADatabaseManager = databaseManager; + } + + @Override + public void run() { + sharedInstance = BiAManager.getInstance(null); + TouchDataPOJO[] temp; + Semaphore temp_Sempaphore; + if(bucket1){ + temp = sharedInstance.t1; + temp_Sempaphore = sharedInstance.t1_Sempahore; + }else{ + temp = sharedInstance.t2; + temp_Sempaphore = sharedInstance.t2_Sempahore; + } + try { + temp_Sempaphore.acquire(); + Log.i("CS_BiAffect","-----------BUFFER EMPTY START-------------"+this.bucket1); + for(TouchDataPOJO data:temp) { + if (data.used && data.validatePOJO()) { + //Ready to be used inside the code + mBiADatabaseManager.insertTouchData(data.eventDownTime, data.eventTime, data.eventAction, data.pressure, data.x_cord, data.y_cord, data.major_axis, data.minor_axis); + data.markUnused(); + } + } + }catch (InterruptedException e){ + + }finally { + temp_Sempaphore.release(); + Log.i("CS_BiAffect","-----------BUFFER EMPTY END-------------"+this.bucket1); + } + } +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/BiAffectBridge.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/BiAffectBridge.java new file mode 100644 index 0000000000..bec55c9782 --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/BiAffectBridge.java @@ -0,0 +1,117 @@ +package com.menny.android.anysoftkeyboard.BiAffect.bridge; + +import org.sagebionetworks.bridge.android.manager.BridgeManagerProvider; +import org.sagebionetworks.bridge.rest.model.ConsentStatus; +import org.sagebionetworks.bridge.rest.model.UserSessionInfo; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import androidx.work.Constraints; +import androidx.work.NetworkType; +import androidx.work.OneTimeWorkRequest; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; +import rx.Single; + +/** + * Utility class to help manage the connection between BiAffect data and the Sage Bridge SDK + * + * @author wang@arbormoon.com + */ +public class BiAffectBridge { + + private static class Holder { + public static BiAffectBridge instance = new BiAffectBridge(); + } + + private BiAffectBridge() {} + + public static BiAffectBridge getInstance() { + return Holder.instance; + } + + /** + * Checks to see if user is considered logged in or not. + * Sets up Session uploads if the user is logged in. + */ + public boolean isUserLoggedIn() { + boolean loggedIn = (null != BridgeManagerProvider.getInstance().getAuthenticationManager().getUserSessionInfo()); + if( loggedIn ) { + setUpWorkManager(); + } + return loggedIn; + } + + /** + * Sets up WorkManager so queued uploads will be processed: + * once a day + * when the device is on an unmetered connection + * when the device is charging + * when the device is idle + */ + private void setUpWorkManager() { + WorkManager.getInstance().cancelAllWork(); + Constraints constraints = new Constraints.Builder().setRequiredNetworkType( NetworkType.UNMETERED ) + .setRequiresCharging( true ) + .setRequiresDeviceIdle( true ) + .build(); + + PeriodicWorkRequest request = new PeriodicWorkRequest.Builder( BridgeWorker.class, + 1, + TimeUnit.DAYS ) + .setConstraints( constraints ) + .build(); + + WorkManager.getInstance().enqueue( request ); + } + + /** + * Logs the user in to the Bridge Study Manager. + * Successful login will also set up Session uploads. + * + * @param email Email that the user signed up with + * @param password Password that the user signed up with + * @return A Single that can be subscribed to for updates. + */ + public Single logIn( String email, String password ) { + return BridgeManagerProvider.getInstance() + .getAuthenticationManager() + .signIn( email, password ) + .doOnSuccess( __ -> setUpWorkManager() ); + } + + /** + * Attempts to bypass the consent step to help facilitate onboarding. + * + * @param userSessionInfo UserSessionInfo to get the required consents from + * @return A Single whose success represents a successful bypass + */ + public Single bypassConsent( UserSessionInfo userSessionInfo ) { + Map consents = userSessionInfo.getConsentStatuses(); + for( String guid : consents.keySet() ) { + ConsentStatus consent = consents.get( guid ); + if( null == consent ) { + continue; + } + if( consent.isRequired() && !consent.isConsented() ) { + return BridgeManagerProvider.getInstance() + .getAuthenticationManager() + .giveConsent( guid, + userSessionInfo.getFirstName() + " " + userSessionInfo.getLastName(), + null, + null, + null, + userSessionInfo.getSharingScope() ); + } + } + return null; + } + + public void singleUpload() { + WorkManager.getInstance().cancelAllWork(); + OneTimeWorkRequest request = new OneTimeWorkRequest.Builder( BridgeWorker.class ).build(); + WorkManager.getInstance().enqueue( request ); + } + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/BridgeWorker.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/BridgeWorker.java new file mode 100644 index 0000000000..892cb86abf --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/BridgeWorker.java @@ -0,0 +1,29 @@ +package com.menny.android.anysoftkeyboard.BiAffect.bridge; + +import android.content.Context; +import android.support.annotation.NonNull; + +import org.sagebionetworks.bridge.android.manager.BridgeManagerProvider; + +import androidx.work.RxWorker; +import androidx.work.WorkerParameters; +import hu.akarnokd.rxjava.interop.RxJavaInterop; +import io.reactivex.Single; + +/** + * WorkManager work that takes care of processing the queued upload files + */ +public class BridgeWorker extends RxWorker { + + public BridgeWorker( @NonNull Context appContext, @NonNull WorkerParameters workerParams ) { + super( appContext, workerParams ); + } + + @Override + public Single createWork() { + return RxJavaInterop.toV2Single( BridgeManagerProvider.getInstance() + .getUploadManager() + .processUploadFiles() + .toSingle( Result::success ) ); + } +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/Session.java b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/Session.java new file mode 100644 index 0000000000..2fd2ac69ea --- /dev/null +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/BiAffect/bridge/Session.java @@ -0,0 +1,138 @@ +package com.menny.android.anysoftkeyboard.BiAffect.bridge; + +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.AccelerometerData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.KeyTypeData; +import com.menny.android.anysoftkeyboard.BiAffect.Database.Models.TouchTypeData; + +import java.util.ArrayList; +import java.util.List; + +import io.reactivex.Observable; + +/** + * JSON object that represents the correct data format that Bridge Research Manager expects for BiAffect. + */ +public class Session { + private double duration; + private long timestamp; + private List keylogs = new ArrayList<>(); + private List accelerations; + + /** + * Basic constructor for this class that initializes the required time-related parameters + * + * @param start epoch representing the start of the Session + * @param end epoch representing the end of the Session + */ + public Session( long start, long end ) { + duration = end - start; + timestamp = start; + } + + /** + * Adds the correct Accelerations data from the AccelerometerData from the DB + * + * @param accelerometerData List of AccelerometerData associated with this Session + */ + public void addAccelerometerData( List accelerometerData ) { + accelerations = Observable.fromIterable( accelerometerData ) + .map( Accelerations::new ) + .toList() + .blockingGet(); + } + + /** + * Adds the Keylog object to the Session + */ + public void addKeylog( Keylog keylog ) { + keylogs.add( keylog ); + } + + /** + * Class to represent an entire key (KeyTypeData), along with associated touches (TouchTypeData) + *

+ * This maps to Keylogs in the scheme + */ + public static class Keylog { + private long timestamp; //timestamp of first touch event + private String keyTypeCode; //from key type data + private List touches = new ArrayList<>(); + + //transient to not be serialized in JSON + //used to calculate distances + private transient float centerX; + private transient float centerY; + private transient float width; + private transient float height; + + private static transient float previousX = 0; + private static transient float previousY = 0; + + private static class Touch { + private long timestamp; + private String action; + private float force; + private double distanceFromCenter; + private double distanceFromPrevious; + private float touch_majorAxis; + private float touch_minorAxis; + + private Touch( TouchTypeData origin ) { + timestamp = origin.eventActionTime; + action = origin.eventAction; + force = origin.force; + touch_majorAxis = origin.touch_majorAxis; + touch_minorAxis = origin.touch_minorAxis; + } + } + + /** + * Creates a new Keylog object from the origin KeyTypeData object + */ + public Keylog( KeyTypeData origin ) { + timestamp = origin.keyDownTime_id; + keyTypeCode = origin.keyTypeCode; + centerX = origin.keyCentre_X; + centerY = origin.keyCentre_Y; + width = origin.key_Width; + height = origin.key_Height; + } + + /** + * Creates a new Touch object from the origin TouchTypeData object + *

+ * Also calculates the distanceFromCenter and distanceFromPrevious fields + */ + public void addTouch( TouchTypeData origin ) { + Touch touch = new Touch( origin ); + + touch.distanceFromCenter = Math.hypot( origin.touch_xcord - centerX, + origin.touch_ycord - centerY ); + + touch.distanceFromPrevious = Math.hypot( origin.touch_xcord - previousX, + origin.touch_ycord - previousY ); + + touches.add( touch ); + previousX = origin.touch_xcord; + previousY = origin.touch_ycord; + } + } + + /** + * Class that maps to Accelerations in the scheme + */ + private static class Accelerations { + private long time; + private double x; + private double y; + private double z; + + private Accelerations( AccelerometerData source ) { + time = source.time; + x = source.Acc_X; + y = source.Acc_Y; + z = source.Acc_Z; + } + } + +} diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/LauncherSettingsActivity.java b/app/src/main/java/com/menny/android/anysoftkeyboard/LauncherSettingsActivity.java index b944120910..aa42fc68d1 100644 --- a/app/src/main/java/com/menny/android/anysoftkeyboard/LauncherSettingsActivity.java +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/LauncherSettingsActivity.java @@ -19,6 +19,8 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.view.Gravity; +import android.widget.Toast; import com.anysoftkeyboard.ui.settings.BasicAnyActivity; import com.anysoftkeyboard.ui.settings.MainSettingsActivity; @@ -41,6 +43,11 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) mLaunched = savedInstanceState.getBoolean(LAUNCHED_KEY, false); + Bundle extras = getIntent().getExtras(); + String message = extras.getString("successMsg"); + Toast toast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); } @Override diff --git a/app/src/main/java/com/menny/android/anysoftkeyboard/SoftKeyboard.java b/app/src/main/java/com/menny/android/anysoftkeyboard/SoftKeyboard.java index 136b623b72..5f441347cb 100644 --- a/app/src/main/java/com/menny/android/anysoftkeyboard/SoftKeyboard.java +++ b/app/src/main/java/com/menny/android/anysoftkeyboard/SoftKeyboard.java @@ -17,8 +17,10 @@ package com.menny.android.anysoftkeyboard; import android.content.ComponentName; +import android.util.Log; import com.anysoftkeyboard.AnySoftKeyboard; +import com.menny.android.anysoftkeyboard.BiAffect.BiAManager; /* * Why is this class exists? @@ -26,7 +28,6 @@ * and still support upgrade... so SoftKeyboard inherits from the actual class */ public class SoftKeyboard extends AnySoftKeyboard { - @Override protected String getSettingsInputMethodId() { return new ComponentName(getApplication(), SoftKeyboard.class).flattenToShortString(); diff --git a/app/src/main/res/drawable/button_bg.xml b/app/src/main/res/drawable/button_bg.xml new file mode 100644 index 0000000000..42db9972d6 --- /dev/null +++ b/app/src/main/res/drawable/button_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_consent_page.xml b/app/src/main/res/layout/activity_consent_page.xml new file mode 100644 index 0000000000..6b6e87529c --- /dev/null +++ b/app/src/main/res/layout/activity_consent_page.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_logging_page.xml b/app/src/main/res/layout/activity_logging_page.xml new file mode 100644 index 0000000000..055ea087ae --- /dev/null +++ b/app/src/main/res/layout/activity_logging_page.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 24966bbf03..7e19b7d9f2 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index a915eecd59..0000000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 2763ee8de4..be13a10470 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 1744da6bae..0000000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 36938ef1cf..41b82ccdcb 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 42e19f5f76..0000000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 3886dee182..e7a568e62f 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 7253a245fa..0000000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 9b7c10bcfe..1fd5815f9e 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 13117cf5e4..0000000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/values/bridge-config.xml b/app/src/main/res/values/bridge-config.xml new file mode 100644 index 0000000000..023f29abc9 --- /dev/null +++ b/app/src/main/res/values/bridge-config.xml @@ -0,0 +1,6 @@ + + + https://webservices.sagebridge.org/ + biaffect + AndroidTest + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 0bdb13e9af..c44c29ab53 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,5 +1,4 @@ - - +--> + 5dp 48dp @@ -98,4 +98,7 @@ 4dp 12sp 3dp + + 16dp + 16dp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fcd1ba1c69..5d4bed4eb6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ - AnySoftKeyboard - + + BiAffect Tracing started!\nMake sure you stop it at some point Tracing initiating failed! Check logcat for @@ -179,7 +179,7 @@ Search emojis by tag, when typing starts with colon (:) - + Hide keyboard on physical key Hide soft keyboard when physical keyboard is used @@ -211,7 +211,6 @@ Shifted Backspace will delete the last character - Swipe up Define the swipe up gesture action. Currently, %s @@ -299,7 +298,7 @@ Suggestion dictionaries used in AnySoftKeyboard User-dictionary Suggest user added words (double-click a typed-suggestion to add) - + Use fallback User-dictionary Use only AnySoftKeyboard\'s built-in User-dictionary mechanism (not recommended). Try to use Android\'s built-in User-dictionary first (recommended). @@ -331,7 +330,7 @@ Contacts dictionary Suggest names from contacts list - + Tweaks Sound related tweaks Physical keyboard tweaks @@ -440,7 +439,7 @@ Insert space after word suggestion select - + Double space to period Double space tap will become period+space Animations @@ -583,7 +582,7 @@ Select keyboard - + Smiley key Shorter Smiley key @@ -651,7 +650,7 @@ Start compacted to left - + Language: User dictionary @@ -818,7 +817,7 @@ No usage yet. Type a bit more. Clipboard is empty, there is nothing to paste. Selected text copied to clipboard. - + Pick text to paste Clear next-word data This is irreversible. All your typing history will be cleared. @@ -922,4 +921,13 @@ Change theme colors based on used app Keyboard\'s colors will match active app (to the best of our ability). Always use theme\'s colors. + Sign in + Email + Password + Sign in or register + Sign in + "Welcome !" + Not a valid username + Password must be >5 characters + "Login failed" diff --git a/app/src/main/res/xml/prefs_gestures_prefs.xml b/app/src/main/res/xml/prefs_gestures_prefs.xml index d8c0a4ad55..51c84fa978 100644 --- a/app/src/main/res/xml/prefs_gestures_prefs.xml +++ b/app/src/main/res/xml/prefs_gestures_prefs.xml @@ -3,19 +3,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:key="prefs_addons_screen"> - - - - - - diff --git a/build.gradle b/build.gradle index 8c981a65d6..42df332251 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.android.tools.build:gradle:3.3.1' + classpath 'com.android.tools.build:gradle:3.5.0' classpath 'com.github.triplet.gradle:play-publisher:2.0.0' classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.6.1' } @@ -25,8 +25,15 @@ allprojects { mavenCentral() maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } maven { url "https://jitpack.io" } + maven { url 'http://repo-maven.sagebridge.org/' } + maven { url 'https://dl.bintray.com/sage-bionetworks/bridge-maven-release' } } } apply from: "${rootDir}/gradle/emoji_generator.gradle" apply from: "${rootDir}/gradle/root_all_projects_ext.gradle" + +ext { + roomVersion = '1.1.1' +} + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 44e7c4d1d7..162fec4419 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Thu Jul 25 18:00:26 PDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/wiki image/Incorrect_Unregistered email or incorrect password.png b/wiki image/Incorrect_Unregistered email or incorrect password.png new file mode 100644 index 0000000000..f4ad31ecd6 Binary files /dev/null and b/wiki image/Incorrect_Unregistered email or incorrect password.png differ diff --git a/wiki image/auto1.JPG b/wiki image/auto1.JPG new file mode 100644 index 0000000000..d21bc20a9f Binary files /dev/null and b/wiki image/auto1.JPG differ diff --git a/wiki image/auto2.JPG b/wiki image/auto2.JPG new file mode 100644 index 0000000000..5fdd27d487 Binary files /dev/null and b/wiki image/auto2.JPG differ diff --git a/wiki image/auto3.JPG b/wiki image/auto3.JPG new file mode 100644 index 0000000000..a2a5b807af Binary files /dev/null and b/wiki image/auto3.JPG differ diff --git a/wiki image/backspace.png b/wiki image/backspace.png new file mode 100644 index 0000000000..b70a873510 Binary files /dev/null and b/wiki image/backspace.png differ diff --git a/wiki image/consent page.png b/wiki image/consent page.png new file mode 100755 index 0000000000..598394a8fa Binary files /dev/null and b/wiki image/consent page.png differ diff --git a/wiki image/internet error.png b/wiki image/internet error.png new file mode 100755 index 0000000000..5a2b7631eb Binary files /dev/null and b/wiki image/internet error.png differ diff --git a/wiki image/login page.png b/wiki image/login page.png new file mode 100755 index 0000000000..6f3f66dc2b Binary files /dev/null and b/wiki image/login page.png differ diff --git a/wiki image/space.png b/wiki image/space.png new file mode 100644 index 0000000000..95121047ed Binary files /dev/null and b/wiki image/space.png differ diff --git a/wiki image/successfully logined.png b/wiki image/successfully logined.png new file mode 100755 index 0000000000..28332a673b Binary files /dev/null and b/wiki image/successfully logined.png differ diff --git a/wiki image/switchToLetter.png b/wiki image/switchToLetter.png new file mode 100644 index 0000000000..a997c9593d Binary files /dev/null and b/wiki image/switchToLetter.png differ diff --git a/wiki image/switchToNumber.png b/wiki image/switchToNumber.png new file mode 100644 index 0000000000..d0ee0ce577 Binary files /dev/null and b/wiki image/switchToNumber.png differ diff --git a/wiki image/symbols.png b/wiki image/symbols.png new file mode 100644 index 0000000000..2e12bd35b5 Binary files /dev/null and b/wiki image/symbols.png differ