├── .github └── workflows │ └── main.yml ├── .gitignore ├── .idea └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── ru │ │ └── ibakaidov │ │ └── distypepro │ │ └── ExampleInstrumentedTest.java │ ├── debug │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ └── mipmap-xxxhdpi │ │ └── ic_launcher.png │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── ru │ │ │ └── ibakaidov │ │ │ ├── .DS_Store │ │ │ └── distypepro │ │ │ ├── .DS_Store │ │ │ ├── components │ │ │ ├── BankGroup.java │ │ │ ├── Component.java │ │ │ └── InputGroup.java │ │ │ ├── data │ │ │ ├── CategoryManager.java │ │ │ ├── Manager.java │ │ │ └── StatementManager.java │ │ │ ├── dialogs │ │ │ ├── ConfirmDialog.java │ │ │ ├── ContextDialog.java │ │ │ └── InputDialog.java │ │ │ ├── screens │ │ │ ├── AuthActivity.java │ │ │ ├── MainActivity.java │ │ │ └── SpotlightActivity.java │ │ │ ├── structures │ │ │ ├── Category.java │ │ │ └── Statement.java │ │ │ └── utils │ │ │ ├── Callback.java │ │ │ ├── HashMapAdapter.java │ │ │ ├── ProgressState.java │ │ │ └── TTS.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_baseline_add_24.xml │ │ ├── ic_baseline_arrow_back_24.xml │ │ ├── ic_baseline_clear_24.xml │ │ ├── ic_baseline_lock_24.xml │ │ ├── ic_baseline_lock_open_24.xml │ │ ├── ic_baseline_remove_red_eye_24.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_auth.xml │ │ ├── activity_main.xml │ │ ├── activity_spotlight.xml │ │ ├── bank_group.xml │ │ ├── input_group.xml │ │ └── input_prompt.xml │ │ ├── menu │ │ └── main_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-ru-rRU │ │ └── strings.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── ru │ └── ibakaidov │ └── distypepro │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Andriod CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: set up JDK 20 | uses: actions/setup-java@v3 21 | with: 22 | java-version: '23' 23 | distribution: 'temurin' 24 | cache: gradle 25 | 26 | - name: Grant execute permission for gradlew 27 | run: chmod +x gradlew 28 | - name: Build with Gradle 29 | run: ./gradlew build 30 | - name: Archive Artifact 31 | uses: actions/upload-artifact@master 32 | with: 33 | name: apk 34 | path: app/build/outputs/apk/release/ 35 | signin: 36 | runs-on: ubuntu-latest 37 | needs: build 38 | steps: 39 | - uses: actions/checkout@v4 40 | - name: Download Artifact 41 | uses: actions/download-artifact@master 42 | with: 43 | name: apk 44 | path: app/build/outputs/apk/release/ 45 | 46 | - uses: r0adkll/sign-android-release@v1 47 | name: Sign app APK 48 | id: sign_app 49 | with: 50 | releaseDirectory: app/build/outputs/apk/release 51 | signingKeyBase64: ${{ secrets.SIGNING_KEY }} 52 | alias: main key 53 | keyStorePassword: ${{ secrets.KEYSTOREPASSWORD }} 54 | keyPassword: ${{ secrets.KEYPASSWORD }} 55 | env: 56 | BUILD_TOOLS_VERSION: "35.0.1" 57 | 58 | - uses: actions/upload-artifact@v4 59 | name: Upload signed build 60 | with: 61 | name: Signed app bundle 62 | path: ${{steps.sign_app.outputs.signedReleaseFile}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/ 39 | 40 | # Keystore files 41 | *.jks 42 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LINKa. напиши 2 | 3 | Программа, переводящая напечатанный текст в речь и сохраняющая частые фразы. Подходит для людей, которые легко печатают на клавиатуре или экране планшета, но имеют проблемы с речью. 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'com.google.gms.google-services' 4 | } 5 | 6 | android { 7 | namespace 'ru.ibakaidov.distypepro' 8 | compileSdk 35 9 | 10 | defaultConfig { 11 | applicationId "ru.ibakaidov.distypepro" 12 | minSdk 21 13 | targetSdk 35 14 | versionCode 28 15 | versionName "3.2" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | buildFeatures { 31 | viewBinding true 32 | } 33 | buildToolsVersion '35.0.1' 34 | } 35 | 36 | dependencies { 37 | 38 | implementation 'androidx.appcompat:appcompat:1.5.1' 39 | implementation 'com.google.android.material:material:1.7.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 41 | implementation "com.github.permissions-dispatcher:permissionsdispatcher:4.8.0" 42 | implementation 'androidx.navigation:navigation-fragment:2.5.3' 43 | implementation 'androidx.navigation:navigation-ui:2.5.3' 44 | //noinspection GradleDynamicVersion 45 | implementation 'androidx.preference:preference:1.2.0' 46 | 47 | implementation 'com.afollestad.material-dialogs:input:3.3.0' 48 | implementation 'com.google.firebase:firebase-analytics:21.2.0' 49 | 50 | implementation 'com.github.bumptech.glide:glide:4.11.0' 51 | testImplementation 'junit:junit:4.13.2' 52 | androidTestImplementation 'androidx.test.ext:junit:1.1.4' 53 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' 54 | implementation 'com.google.firebase:firebase-database:20.1.0' 55 | implementation 'com.google.firebase:firebase-auth:21.1.0' 56 | implementation 'com.firebaseui:firebase-ui-auth:8.0.2' 57 | } 58 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "800888317067", 4 | "firebase_url": "https://distypepro-android.firebaseio.com", 5 | "project_id": "distypepro-android", 6 | "storage_bucket": "distypepro-android.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:800888317067:android:06e954d4007fb515", 12 | "android_client_info": { 13 | "package_name": "ru.ibakaidov.distypepro" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "800888317067-g2fiacadak4adl89duvh7a5nabdhnlb6.apps.googleusercontent.com", 19 | "client_type": 1, 20 | "android_info": { 21 | "package_name": "ru.ibakaidov.distypepro", 22 | "certificate_hash": "46c6b789f46199df6b7706029ee7d0c72c1674ff" 23 | } 24 | }, 25 | { 26 | "client_id": "800888317067-i91mbc3etv34tu07k5n8h4l58f0kohuj.apps.googleusercontent.com", 27 | "client_type": 1, 28 | "android_info": { 29 | "package_name": "ru.ibakaidov.distypepro", 30 | "certificate_hash": "bc06b3414b7fab8d58e8a710ee23e0c6e2a3963a" 31 | } 32 | }, 33 | { 34 | "client_id": "800888317067-nbq3g1ghp4gsn2rmg5hanhckb6glafjn.apps.googleusercontent.com", 35 | "client_type": 3 36 | } 37 | ], 38 | "api_key": [ 39 | { 40 | "current_key": "AIzaSyCNJUzqwMjvMKsFPxB9L1VbHHvO-4qi7IQ" 41 | } 42 | ], 43 | "services": { 44 | "appinvite_service": { 45 | "other_platform_oauth_client": [ 46 | { 47 | "client_id": "800888317067-nbq3g1ghp4gsn2rmg5hanhckb6glafjn.apps.googleusercontent.com", 48 | "client_type": 3 49 | }, 50 | { 51 | "client_id": "800888317067-gm7td75tp9i7o1bn94cskpibetbs9r59.apps.googleusercontent.com", 52 | "client_type": 2, 53 | "ios_info": { 54 | "bundle_id": "com.linka.linkaType" 55 | } 56 | } 57 | ] 58 | } 59 | } 60 | } 61 | ], 62 | "configuration_version": "1" 63 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/ru/ibakaidov/distypepro/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("ru.ibakaidov.distypepro", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-android/c2ebe9f66bb88f1612820d3968dcfad3d22b4fb4/app/src/debug/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-android/c2ebe9f66bb88f1612820d3968dcfad3d22b4fb4/app/src/debug/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-android/c2ebe9f66bb88f1612820d3968dcfad3d22b4fb4/app/src/debug/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-android/c2ebe9f66bb88f1612820d3968dcfad3d22b4fb4/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-android/c2ebe9f66bb88f1612820d3968dcfad3d22b4fb4/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 16 | 21 | 24 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-android/c2ebe9f66bb88f1612820d3968dcfad3d22b4fb4/app/src/main/java/ru/ibakaidov/.DS_Store -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-android/c2ebe9f66bb88f1612820d3968dcfad3d22b4fb4/app/src/main/java/ru/ibakaidov/distypepro/.DS_Store -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/components/BankGroup.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.components; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.AdapterView; 8 | import android.widget.ArrayAdapter; 9 | import android.widget.Button; 10 | import android.widget.GridView; 11 | import android.widget.ImageButton; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.annotation.Nullable; 15 | import androidx.appcompat.widget.AppCompatButton; 16 | import androidx.recyclerview.widget.ListAdapter; 17 | 18 | import com.google.firebase.auth.FirebaseAuth; 19 | import com.google.firebase.database.DataSnapshot; 20 | import com.google.firebase.database.DatabaseError; 21 | import com.google.firebase.database.FirebaseDatabase; 22 | import com.google.firebase.database.ValueEventListener; 23 | 24 | import java.util.ArrayList; 25 | import java.util.HashMap; 26 | import java.util.LinkedHashMap; 27 | import java.util.MissingResourceException; 28 | import java.util.function.Consumer; 29 | 30 | import ru.ibakaidov.distypepro.R; 31 | import ru.ibakaidov.distypepro.data.CategoryManager; 32 | import ru.ibakaidov.distypepro.data.StatementManager; 33 | import ru.ibakaidov.distypepro.dialogs.ConfirmDialog; 34 | import ru.ibakaidov.distypepro.dialogs.ContextDialog; 35 | import ru.ibakaidov.distypepro.dialogs.InputDialog; 36 | import ru.ibakaidov.distypepro.structures.Category; 37 | import ru.ibakaidov.distypepro.utils.Callback; 38 | import ru.ibakaidov.distypepro.utils.HashMapAdapter; 39 | import ru.ibakaidov.distypepro.utils.TTS; 40 | 41 | public class BankGroup extends Component { 42 | 43 | private CategoryManager cm; 44 | private StatementManager sm; 45 | private GridView gridView; 46 | private ImageButton backButton; 47 | private ImageButton addButton; 48 | 49 | private TTS tts; 50 | private boolean state = false; 51 | 52 | 53 | 54 | public BankGroup(Context context, @Nullable AttributeSet attrs) { 55 | super(context, attrs); 56 | } 57 | 58 | @Override 59 | protected int getLayoutId() { 60 | return R.layout.bank_group; 61 | } 62 | 63 | @Override 64 | protected void initUI() { 65 | gridView = findViewById(R.id.gridview); 66 | addButton = findViewById(R.id.add_button); 67 | backButton = findViewById(R.id.back_button); 68 | 69 | backButton.setOnClickListener(new OnClickListener() { 70 | @Override 71 | public void onClick(View view) { 72 | setState(false); 73 | } 74 | }); 75 | 76 | addButton.setOnClickListener( 77 | new OnClickListener() { 78 | @Override 79 | public void onClick(View view) { 80 | add(); 81 | } 82 | } 83 | ); 84 | 85 | gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 86 | @Override 87 | public void onItemClick(AdapterView adapterView, View view, int i, long l) { 88 | HashMapAdapter adapter = (HashMapAdapter) adapterView.getAdapter(); 89 | String key = adapter.getKey(i); 90 | String value = adapter.getItem(i); 91 | onItemSelected(key, value); 92 | } 93 | 94 | }); 95 | 96 | gridView 97 | .setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { 98 | @Override 99 | public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { 100 | HashMapAdapter adapter = (HashMapAdapter) adapterView.getAdapter(); 101 | String key = adapter.getKey(i); 102 | String value = adapter.getItem(i); 103 | onLongItemClick(key, value); 104 | 105 | return false; 106 | } 107 | }); 108 | cm = new CategoryManager(); 109 | 110 | showCategories(); 111 | 112 | } 113 | 114 | private void add() { 115 | InputDialog 116 | .showDialog(getContext(), R.string.create, new Callback() { 117 | @Override 118 | public void onDone(String res) { 119 | if(state){ 120 | sm.create(res, new Callback() { 121 | @Override 122 | public void onDone(Object res) { 123 | 124 | } 125 | }); 126 | } else { 127 | cm.create(res, new Callback() { 128 | @Override 129 | public void onDone(Object res) { 130 | 131 | } 132 | }); 133 | } 134 | } 135 | }); 136 | } 137 | 138 | private void onLongItemClick(String key, String value) { 139 | ContextDialog 140 | .show(getContext(), value, new Callback() { 141 | @Override 142 | public void onDone(ContextDialog.ContextDialogActions res) { 143 | if(res== ContextDialog.ContextDialogActions.edit){ 144 | edit(); 145 | } else { 146 | remove(); 147 | } 148 | } 149 | 150 | private void remove() { 151 | ConfirmDialog 152 | .showConfirmDialog(getContext(), R.string.remove, new Callback() { 153 | @Override 154 | public void onDone(Object res) { 155 | if(getState()){ 156 | sm.remove(key, new Callback() { 157 | @Override 158 | public void onDone(Object res) { 159 | 160 | } 161 | }); 162 | } else { 163 | cm.remove(key, new Callback() { 164 | @Override 165 | public void onDone(Object res) { 166 | 167 | } 168 | }); 169 | } 170 | } 171 | }); 172 | } 173 | 174 | private void edit() { 175 | InputDialog 176 | .showDialog(getContext(), R.string.edit, value, new Callback() { 177 | @Override 178 | public void onDone(String res) { 179 | if(getState()){ 180 | sm.edit(key, res, new Callback() { 181 | @Override 182 | public void onDone(Object res) { 183 | 184 | } 185 | }); 186 | } else { 187 | cm.edit(key, res, new Callback() { 188 | @Override 189 | public void onDone(Object res) { 190 | 191 | } 192 | }); 193 | } 194 | } 195 | }); 196 | } 197 | }); 198 | } 199 | 200 | private void onItemSelected(String key, String value) { 201 | if(getState()){ 202 | 203 | tts.speak(value); 204 | } else { 205 | // categories 206 | sm = new StatementManager(key); 207 | setState(true); 208 | 209 | } 210 | } 211 | 212 | 213 | private boolean getState(){ 214 | return state; 215 | } 216 | 217 | private void setState(boolean value){ 218 | state = value; 219 | if(value){ 220 | showStatements(); 221 | backButton.setVisibility(VISIBLE); 222 | } else { 223 | showCategories(); 224 | backButton.setVisibility(GONE); 225 | } 226 | } 227 | 228 | private void showCategories() { 229 | 230 | cm.getList(new Callback>() { 231 | @Override 232 | public void onDone(HashMap res) { 233 | if(getState()) return; 234 | 235 | HashMapAdapter adapter = new HashMapAdapter(getContext(), res); 236 | gridView.setAdapter(adapter); 237 | } 238 | }); 239 | 240 | } 241 | 242 | private void showStatements() { 243 | if(!getState()) return; 244 | sm.getList(new Callback>() { 245 | @Override 246 | public void onDone(HashMap res) { 247 | HashMapAdapter adapter = new HashMapAdapter(getContext(), res); 248 | gridView.setAdapter(adapter); 249 | } 250 | }); 251 | } 252 | 253 | public void setTts(TTS tts) { 254 | this.tts = tts; 255 | } 256 | 257 | public void back() { 258 | setState(false); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/components/Component.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.components; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.LinearLayout; 9 | 10 | import androidx.annotation.Nullable; 11 | 12 | import ru.ibakaidov.distypepro.R; 13 | 14 | public abstract class Component extends LinearLayout { 15 | 16 | protected int layoutId; 17 | 18 | public Component(Context context, @Nullable AttributeSet attrs) { 19 | super(context, attrs); 20 | inflate(context); 21 | } 22 | 23 | private void inflate(Context context) { 24 | LayoutInflater layoutInflater = (LayoutInflater) context 25 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 26 | layoutInflater.inflate(getLayoutId(), this, true); 27 | initUI(); 28 | } 29 | 30 | protected abstract int getLayoutId(); 31 | 32 | protected abstract void initUI(); 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/components/InputGroup.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.components; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.AdapterView; 8 | import android.widget.ArrayAdapter; 9 | import android.widget.Button; 10 | import android.widget.EditText; 11 | import android.widget.ImageButton; 12 | import android.widget.Spinner; 13 | 14 | import androidx.annotation.Nullable; 15 | 16 | import com.google.firebase.analytics.FirebaseAnalytics; 17 | 18 | import ru.ibakaidov.distypepro.R; 19 | import ru.ibakaidov.distypepro.screens.SpotlightActivity; 20 | import ru.ibakaidov.distypepro.utils.Callback; 21 | import ru.ibakaidov.distypepro.utils.ProgressState; 22 | import ru.ibakaidov.distypepro.utils.TTS; 23 | 24 | public class InputGroup extends Component { 25 | private TTS tts; 26 | private EditText ttsEditText; 27 | private Button sayButton; 28 | private ImageButton spotlightButton; 29 | private Spinner chatSpinner; 30 | private int prevSpinnerValue = 0; 31 | private String[] textCache = new String[] {"", "", ""}; 32 | 33 | public InputGroup(Context context, @Nullable AttributeSet attrs) { 34 | super(context, attrs); 35 | } 36 | 37 | public void setTts(TTS tts) { 38 | this.tts = tts; 39 | 40 | tts.setOnPlayCallback(new Callback() { 41 | @Override 42 | public void onDone(ProgressState res) { 43 | sayButton.setText(res == ProgressState.START?R.string.stop:R.string.say); 44 | } 45 | }); 46 | } 47 | 48 | @Override 49 | protected void initUI() { 50 | ttsEditText = findViewById(R.id.text_to_speech_edittext); 51 | sayButton = findViewById(R.id.say_button); 52 | spotlightButton = findViewById(R.id.spotlight_button); 53 | 54 | 55 | setOnClickListener(new OnClickListener() { 56 | @Override 57 | public void onClick(View view) { 58 | if(ttsEditText.hasFocus()){ 59 | ttsEditText.clearFocus(); 60 | } 61 | } 62 | }); 63 | View v = this; 64 | ttsEditText.setOnFocusChangeListener(new OnFocusChangeListener() { 65 | @Override 66 | public void onFocusChange(View view, boolean b) { 67 | ViewGroup.LayoutParams params = v.getLayoutParams(); 68 | params.height = b?LayoutParams.MATCH_PARENT: ViewGroup.LayoutParams.WRAP_CONTENT; 69 | v.setLayoutParams(params); 70 | } 71 | }); 72 | 73 | sayButton.setOnClickListener(new OnClickListener() { 74 | @Override 75 | public void onClick(View view) { 76 | say(); 77 | } 78 | }); 79 | 80 | spotlightButton.setOnClickListener(new OnClickListener() { 81 | @Override 82 | public void onClick(View view) { 83 | spotlight(); 84 | } 85 | }); 86 | 87 | } 88 | 89 | private void setText(String s) { 90 | ttsEditText.setText(s); 91 | } 92 | 93 | private void spotlight() { 94 | SpotlightActivity 95 | .show(getContext(), getText()); 96 | 97 | FirebaseAnalytics 98 | .getInstance(getContext()) 99 | .logEvent("spotlight", null); 100 | } 101 | 102 | @Override 103 | protected int getLayoutId() { 104 | return R.layout.input_group; 105 | } 106 | 107 | private void say() { 108 | String text = getText(); 109 | tts.speak(text); 110 | FirebaseAnalytics 111 | .getInstance(getContext()) 112 | .logEvent("say", null); 113 | } 114 | 115 | private String getText() { 116 | return ttsEditText.getText().toString(); 117 | } 118 | 119 | public void setChatSpinner(Spinner chatSpinner) { 120 | this.chatSpinner = chatSpinner; 121 | ArrayAdapter adapter = new ArrayAdapter(getContext(), androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, new String[]{"1", "2", "3"}); 122 | chatSpinner.setAdapter(adapter); 123 | 124 | chatSpinner 125 | .setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 126 | @Override 127 | public void onItemSelected(AdapterView adapterView, View view, int i, long l) { 128 | textCache[prevSpinnerValue] = getText(); 129 | setText(textCache[i]); 130 | prevSpinnerValue = i; 131 | 132 | } 133 | 134 | @Override 135 | public void onNothingSelected(AdapterView adapterView) { 136 | 137 | } 138 | }); 139 | } 140 | 141 | public void clear() { 142 | ttsEditText.setText(""); 143 | } 144 | 145 | public void back() { 146 | ttsEditText.clearFocus(); 147 | } 148 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/data/CategoryManager.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.data; 2 | 3 | import android.os.Build; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | import androidx.annotation.RequiresApi; 8 | 9 | import com.google.firebase.auth.FirebaseAuth; 10 | import com.google.firebase.database.DataSnapshot; 11 | import com.google.firebase.database.DatabaseError; 12 | import com.google.firebase.database.DatabaseReference; 13 | import com.google.firebase.database.FirebaseDatabase; 14 | import com.google.firebase.database.ValueEventListener; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Comparator; 18 | import java.util.Date; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.function.Consumer; 22 | 23 | import ru.ibakaidov.distypepro.structures.Category; 24 | import ru.ibakaidov.distypepro.utils.Callback; 25 | 26 | public class CategoryManager extends Manager { 27 | 28 | @Override 29 | public void getList(Callback> callback) { 30 | getRoot() 31 | .orderByChild("created") 32 | .addValueEventListener(new ValueEventListener() { 33 | @RequiresApi(api = Build.VERSION_CODES.N) 34 | @Override 35 | public void onDataChange(@NonNull DataSnapshot snapshot) { 36 | Iterable children = snapshot.getChildren(); 37 | HashMap res = new HashMap<>(); 38 | ArrayList list = new ArrayList(); 39 | while( children.iterator().hasNext()){ 40 | DataSnapshot s = children.iterator().next(); 41 | Category category = Category.fromHashMap((HashMap) s.getValue()); 42 | list.add(category); 43 | } 44 | list.sort(new Comparator() { 45 | @Override 46 | public int compare(Category t1, Category t2) { 47 | return (int) (t2.created-t1.created); 48 | } 49 | }); 50 | list.forEach(new Consumer() { 51 | @Override 52 | public void accept(Category category) { 53 | res.put(category.id, category.label); 54 | } 55 | }); 56 | callback.onDone(res); 57 | } 58 | 59 | @Override 60 | public void onCancelled(@NonNull DatabaseError error) { 61 | callback.onError(error.toException()); 62 | } 63 | }); 64 | } 65 | 66 | @Override 67 | public DatabaseReference getRoot() { 68 | return FirebaseDatabase 69 | .getInstance() 70 | .getReference("users/"+ FirebaseAuth.getInstance().getCurrentUser().getUid()) 71 | .child("Category"); 72 | } 73 | 74 | @Override 75 | public void edit(String key, String value, Callback callback) { 76 | Map update = new HashMap<>(); 77 | update.put("label", value); 78 | getRoot() 79 | .child(key) 80 | .updateChildren(update); 81 | } 82 | 83 | @Override 84 | public void create(String res, Callback callback) { 85 | DatabaseReference r = getRoot() 86 | .push(); 87 | Map update = new HashMap<>(); 88 | update.put("label", res); 89 | update.put("id", r.getKey()); 90 | update.put("created", new Date().getTime()); 91 | r.updateChildren(update, new DatabaseReference.CompletionListener() { 92 | @Override 93 | public void onComplete(@Nullable DatabaseError error, @NonNull DatabaseReference ref) { 94 | callback.onDone(null); 95 | } 96 | }); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/data/Manager.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.data; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.google.firebase.database.DatabaseError; 7 | import com.google.firebase.database.DatabaseReference; 8 | 9 | import java.util.HashMap; 10 | 11 | import ru.ibakaidov.distypepro.utils.Callback; 12 | 13 | public abstract class Manager { 14 | 15 | public abstract void getList(Callback> callback); 16 | public abstract DatabaseReference getRoot(); 17 | 18 | public void remove(String key, Callback callback) { 19 | getRoot().child(key).removeValue(new DatabaseReference.CompletionListener() { 20 | @Override 21 | public void onComplete(@Nullable DatabaseError error, @NonNull DatabaseReference ref) { 22 | if(error!=null){ 23 | callback.onError(error.toException()); 24 | return; 25 | } 26 | callback.onDone(null); 27 | } 28 | }); 29 | } 30 | 31 | public abstract void edit(String key, String value, Callback callback); 32 | 33 | public abstract void create(String res, Callback callback); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/data/StatementManager.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.data; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.google.firebase.auth.FirebaseAuth; 7 | import com.google.firebase.database.DataSnapshot; 8 | import com.google.firebase.database.DatabaseError; 9 | import com.google.firebase.database.DatabaseReference; 10 | import com.google.firebase.database.FirebaseDatabase; 11 | import com.google.firebase.database.ValueEventListener; 12 | 13 | import java.util.Date; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | import ru.ibakaidov.distypepro.structures.Category; 18 | import ru.ibakaidov.distypepro.structures.Statement; 19 | import ru.ibakaidov.distypepro.utils.Callback; 20 | 21 | public class StatementManager extends Manager { 22 | 23 | 24 | private String categoryId; 25 | 26 | public StatementManager(String categoryId){ 27 | this.categoryId = categoryId; 28 | } 29 | 30 | @Override 31 | public void getList(Callback> callback) { 32 | getRoot() 33 | .orderByChild("created") 34 | .addValueEventListener(new ValueEventListener() { 35 | @Override 36 | public void onDataChange(@NonNull DataSnapshot snapshot) { 37 | Iterable children = snapshot.getChildren(); 38 | HashMap res = new HashMap<>(); 39 | while( children.iterator().hasNext()){ 40 | DataSnapshot s = children.iterator().next(); 41 | Statement statement = Statement.fromHashMap((HashMap) s.getValue()); 42 | res.put(statement.id, statement.text); 43 | } 44 | callback.onDone(res); 45 | } 46 | 47 | @Override 48 | public void onCancelled(@NonNull DatabaseError error) { 49 | 50 | } 51 | }); 52 | } 53 | 54 | @Override 55 | public DatabaseReference getRoot() { 56 | return FirebaseDatabase 57 | .getInstance() 58 | .getReference("users/"+ FirebaseAuth.getInstance().getCurrentUser().getUid()+"/Category/"+categoryId+"/statements/"); 59 | } 60 | 61 | @Override 62 | public void edit(String key, String value, Callback callback) { 63 | Map update = new HashMap<>(); 64 | update.put("text", value); 65 | getRoot() 66 | .child(key) 67 | .updateChildren(update); 68 | } 69 | 70 | @Override 71 | public void create(String res, Callback callback) { 72 | DatabaseReference r = getRoot() 73 | .push(); 74 | Map update = new HashMap<>(); 75 | update.put("text", res); 76 | update.put("id", r.getKey()); 77 | update.put("categoryId", categoryId); 78 | update.put("created", new Date().getTime()); 79 | 80 | r.updateChildren(update, new DatabaseReference.CompletionListener() { 81 | @Override 82 | public void onComplete(@Nullable DatabaseError error, @NonNull DatabaseReference ref) { 83 | callback.onDone(null); 84 | } 85 | }); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/dialogs/ConfirmDialog.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.dialogs; 2 | 3 | 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | 8 | import androidx.annotation.StringRes; 9 | 10 | import ru.ibakaidov.distypepro.R; 11 | import ru.ibakaidov.distypepro.utils.Callback; 12 | 13 | public class ConfirmDialog { 14 | public static void showConfirmDialog(Context context, @StringRes int title, Callback callback){ 15 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 16 | builder 17 | .setTitle(title) 18 | .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 19 | @Override 20 | public void onClick(DialogInterface dialog, int which) { 21 | callback.onError(null); 22 | } 23 | }) 24 | .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 25 | @Override 26 | public void onClick(DialogInterface dialog, int which) { 27 | callback.onDone(null); 28 | } 29 | }) 30 | .show(); 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/dialogs/ContextDialog.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.dialogs; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Context; 5 | import android.content.DialogInterface; 6 | 7 | import ru.ibakaidov.distypepro.R; 8 | import ru.ibakaidov.distypepro.utils.Callback; 9 | 10 | public class ContextDialog { 11 | 12 | public static void show(Context context, String title, Callback callback){ 13 | // setup the alert builder 14 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 15 | builder.setTitle(title); 16 | 17 | builder.setItems(R.array.set_context_actions, new DialogInterface.OnClickListener() { 18 | @Override 19 | public void onClick(DialogInterface dialog, int which) { 20 | callback.onDone(which==0?ContextDialogActions.edit:ContextDialogActions.remove); 21 | } 22 | }) 23 | .setNegativeButton(R.string.cancel, null); 24 | 25 | // create and show the alert dialog 26 | AlertDialog dialog = builder.create(); 27 | dialog.show(); 28 | } 29 | 30 | public static enum ContextDialogActions{ 31 | edit, 32 | remove 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/dialogs/InputDialog.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.dialogs; 2 | 3 | 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.text.InputType; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.widget.EditText; 11 | 12 | import androidx.annotation.Nullable; 13 | import androidx.annotation.StringRes; 14 | 15 | import ru.ibakaidov.distypepro.R; 16 | import ru.ibakaidov.distypepro.utils.Callback; 17 | 18 | public class InputDialog { 19 | public static void showDialog(Context context, @StringRes int title, int type, Callback listener) { 20 | showDialog(context, title, type, null, listener); 21 | } 22 | public static void showDialog(Context context, @StringRes int title, Callback listener) { 23 | showDialog(context, title, null, listener); 24 | } 25 | 26 | public static void showDialog(Context context, @StringRes int title, @Nullable String currentValue, Callback listener) { 27 | showDialog(context, title, InputType.TYPE_CLASS_TEXT, currentValue, listener); 28 | } 29 | 30 | public static void showDialog(Context context, @StringRes int title, int type, @Nullable String currentValue, Callback listener) { 31 | LayoutInflater li = LayoutInflater.from(context); 32 | View promptsView = li.inflate(R.layout.input_prompt, null); 33 | final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); 34 | alertDialogBuilder.setView(promptsView); 35 | 36 | final EditText userInput = (EditText) promptsView 37 | .findViewById(R.id.input_prompt); 38 | if (currentValue != null) { 39 | userInput.setText(currentValue); 40 | } 41 | userInput.setInputType(type); 42 | alertDialogBuilder 43 | .setTitle(title) 44 | .setPositiveButton(R.string.ok, 45 | new DialogInterface.OnClickListener() { 46 | public void onClick(DialogInterface dialog, int id) { 47 | /** DO THE METHOD HERE WHEN PROCEED IS CLICKED*/ 48 | String user_text = (userInput.getText()).toString().trim(); 49 | if (!user_text.equals("")) { 50 | listener.onDone(user_text); 51 | } else { 52 | listener.onError(null); 53 | } 54 | dialog.dismiss(); 55 | } 56 | 57 | }) 58 | .setNegativeButton(R.string.cancel, 59 | new DialogInterface.OnClickListener() { 60 | public void onClick(DialogInterface dialog, int id) { 61 | dialog.dismiss(); 62 | listener.onError(null); 63 | } 64 | 65 | } 66 | 67 | ).setOnCancelListener(new DialogInterface.OnCancelListener() { 68 | @Override 69 | public void onCancel(DialogInterface dialog) { 70 | listener.onError(null); 71 | } 72 | }); 73 | 74 | // create alert dialog 75 | AlertDialog alertDialog = alertDialogBuilder.create(); 76 | alertDialog.setOnShowListener(new DialogInterface.OnShowListener() { 77 | @Override 78 | public void onShow(DialogInterface dialog) { 79 | userInput.requestFocus(); 80 | } 81 | }); 82 | // show it 83 | alertDialog.show(); 84 | 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/screens/AuthActivity.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.screens; 2 | 3 | import androidx.activity.result.ActivityResultCallback; 4 | import androidx.activity.result.ActivityResultLauncher; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import android.content.Intent; 8 | import android.os.Bundle; 9 | 10 | import com.firebase.ui.auth.AuthUI; 11 | import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract; 12 | import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult; 13 | import com.google.firebase.auth.FirebaseAuth; 14 | import com.google.firebase.auth.FirebaseUser; 15 | 16 | import java.util.Arrays; 17 | import java.util.List; 18 | 19 | import ru.ibakaidov.distypepro.R; 20 | 21 | public class AuthActivity extends AppCompatActivity { 22 | private final ActivityResultLauncher signInLauncher = registerForActivityResult( 23 | new FirebaseAuthUIActivityResultContract(), 24 | new ActivityResultCallback() { 25 | @Override 26 | public void onActivityResult(FirebaseAuthUIAuthenticationResult result) { 27 | onSignInResult(result); 28 | } 29 | } 30 | ); 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_auth); 36 | 37 | 38 | if(getCurrentUser()==null) { 39 | auth(); 40 | } else { 41 | initUI(); 42 | } 43 | 44 | } 45 | 46 | private void initUI() { 47 | Intent intent = new Intent(this, MainActivity.class); 48 | startActivity(intent); 49 | finish(); 50 | } 51 | 52 | private FirebaseUser getCurrentUser() { 53 | return FirebaseAuth.getInstance().getCurrentUser(); 54 | } 55 | 56 | private void auth() { 57 | List providers = Arrays.asList( 58 | new AuthUI.IdpConfig.EmailBuilder().build()); 59 | 60 | Intent signInIntent = AuthUI.getInstance() 61 | .createSignInIntentBuilder() 62 | .setAvailableProviders(providers) 63 | .setIsSmartLockEnabled(false) 64 | .build(); 65 | signInLauncher.launch(signInIntent); 66 | } 67 | 68 | private void onSignInResult(FirebaseAuthUIAuthenticationResult result) { 69 | if(result.getResultCode() == RESULT_OK){ 70 | initUI(); 71 | } 72 | 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/screens/MainActivity.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.screens; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.provider.Settings; 9 | import android.view.ContextMenu; 10 | import android.view.Menu; 11 | import android.view.MenuInflater; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | import android.widget.Spinner; 15 | 16 | import com.google.firebase.analytics.FirebaseAnalytics; 17 | import com.google.firebase.database.FirebaseDatabase; 18 | 19 | import ru.ibakaidov.distypepro.R; 20 | import ru.ibakaidov.distypepro.components.BankGroup; 21 | import ru.ibakaidov.distypepro.components.InputGroup; 22 | import ru.ibakaidov.distypepro.utils.TTS; 23 | 24 | public class MainActivity extends AppCompatActivity { 25 | 26 | 27 | private InputGroup inputGroup; 28 | private BankGroup bankGroup; 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | FirebaseDatabase 33 | .getInstance() 34 | .setPersistenceEnabled(true); 35 | FirebaseAnalytics 36 | .getInstance(this); 37 | setContentView(R.layout.activity_main); 38 | TTS tts = new TTS(this); 39 | inputGroup = findViewById(R.id.input_group); 40 | inputGroup.setTts(tts); 41 | bankGroup = findViewById(R.id.bank_group); 42 | bankGroup.setTts(tts); 43 | 44 | 45 | } 46 | @Override 47 | public boolean onCreateOptionsMenu(Menu menu) { 48 | MenuInflater inflater = getMenuInflater(); 49 | inflater.inflate(R.menu.main_menu, menu); 50 | Spinner spinner = (Spinner) menu.findItem(R.id.chats_spinner).getActionView(); 51 | inputGroup.setChatSpinner(spinner); 52 | return true; 53 | } 54 | 55 | @Override 56 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 57 | switch (item.getItemId()){ 58 | case R.id.clear_menu_item: 59 | inputGroup.clear(); 60 | break; 61 | case R.id.settings_menu_item: 62 | Intent intent = new Intent(); 63 | intent.setAction("com.android.settings.TTS_SETTINGS"); 64 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 65 | startActivity(intent); 66 | 67 | break; 68 | } 69 | return super.onOptionsItemSelected(item); 70 | } 71 | 72 | @Override 73 | public void onBackPressed() { 74 | inputGroup.back(); 75 | bankGroup.back(); 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/screens/SpotlightActivity.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.screens; 2 | 3 | import android.annotation.SuppressLint; 4 | 5 | import androidx.appcompat.app.ActionBar; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.os.Build; 11 | import android.os.Bundle; 12 | import android.os.Handler; 13 | import android.os.Looper; 14 | import android.view.MotionEvent; 15 | import android.view.View; 16 | import android.view.WindowInsets; 17 | import android.widget.TextView; 18 | 19 | import ru.ibakaidov.distypepro.databinding.ActivitySpotlightBinding; 20 | import ru.ibakaidov.distypepro.R; 21 | /** 22 | * An example full-screen activity that shows and hides the system UI (i.e. 23 | * status bar and navigation/system bar) with user interaction. 24 | */ 25 | public class SpotlightActivity extends AppCompatActivity { 26 | /** 27 | * Whether or not the system UI should be auto-hidden after 28 | * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds. 29 | */ 30 | private static final boolean AUTO_HIDE = true; 31 | 32 | /** 33 | * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after 34 | * user interaction before hiding the system UI. 35 | */ 36 | private static final int AUTO_HIDE_DELAY_MILLIS = 3000; 37 | 38 | /** 39 | * Some older devices needs a small delay between UI widget updates 40 | * and a change of the status and navigation bar. 41 | */ 42 | private static final int UI_ANIMATION_DELAY = 300; 43 | private final Handler mHideHandler = new Handler(Looper.myLooper()); 44 | private View mContentView; 45 | private final Runnable mHidePart2Runnable = new Runnable() { 46 | @SuppressLint("InlinedApi") 47 | @Override 48 | public void run() { 49 | // Delayed removal of status and navigation bar 50 | if (Build.VERSION.SDK_INT >= 30) { 51 | mContentView.getWindowInsetsController().hide( 52 | WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars()); 53 | } else { 54 | // Note that some of these constants are new as of API 16 (Jelly Bean) 55 | // and API 19 (KitKat). It is safe to use them, as they are inlined 56 | // at compile-time and do nothing on earlier devices. 57 | mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE 58 | | View.SYSTEM_UI_FLAG_FULLSCREEN 59 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 60 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 61 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 62 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); 63 | } 64 | } 65 | }; 66 | private View mControlsView; 67 | private final Runnable mShowPart2Runnable = new Runnable() { 68 | @Override 69 | public void run() { 70 | // Delayed display of UI elements 71 | ActionBar actionBar = getSupportActionBar(); 72 | if (actionBar != null) { 73 | actionBar.show(); 74 | } 75 | mControlsView.setVisibility(View.VISIBLE); 76 | } 77 | }; 78 | private boolean mVisible; 79 | private ActivitySpotlightBinding binding; 80 | 81 | @Override 82 | protected void onCreate(Bundle savedInstanceState) { 83 | super.onCreate(savedInstanceState); 84 | 85 | binding = ActivitySpotlightBinding.inflate(getLayoutInflater()); 86 | setContentView(binding.getRoot()); 87 | 88 | 89 | 90 | TextView textView = (TextView) findViewById(R.id.fullscreen_content); 91 | 92 | Bundle bundle = getIntent().getExtras(); 93 | 94 | String text = bundle.getString("text"); 95 | textView.setText(text); 96 | 97 | 98 | } 99 | 100 | public static void show(Context cxt, String text) { 101 | Intent intent = new Intent(cxt, SpotlightActivity.class); 102 | Bundle bundle = new Bundle(); 103 | bundle.putString("text", text); 104 | intent.putExtras(bundle); 105 | cxt.startActivity(intent); 106 | } 107 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/structures/Category.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.structures; 2 | 3 | import java.util.HashMap; 4 | 5 | public class Category { 6 | 7 | public long created = 0; 8 | public String id = null; 9 | public String label = null; 10 | 11 | public Category( String id, String label, long created) { 12 | 13 | this.created = created; 14 | this.id = id; 15 | this.label = label; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return label; 21 | } 22 | 23 | public static Category fromHashMap(HashMap hashMap){ 24 | long created = hashMap.containsKey("created")? (long) hashMap.get("created"):0; 25 | String id = (String) hashMap.get("id"); 26 | String label = (String) hashMap.get("label"); 27 | 28 | return new Category(id, label, created); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/structures/Statement.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.structures; 2 | 3 | import java.util.HashMap; 4 | 5 | public class Statement { 6 | public String categoryId; 7 | public long created; 8 | public String id; 9 | public String text; 10 | 11 | public Statement(String id, String categoryId, String text, long created){ 12 | 13 | this.id = id; 14 | this.categoryId = categoryId; 15 | this.text = text; 16 | this.created = created; 17 | } 18 | 19 | 20 | public static Statement fromHashMap(HashMap hashMap){ 21 | long created = hashMap.containsKey("created")? (long) hashMap.get("created"):0; 22 | String id = (String) hashMap.get("id"); 23 | String categoryId = (String) hashMap.get("categoryId"); 24 | String text = (String) hashMap.get("text"); 25 | 26 | return new Statement(id,categoryId, text, created); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/utils/Callback.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.utils; 2 | 3 | public abstract class Callback { 4 | public abstract void onDone(T res); 5 | public void onError(Exception e){} 6 | 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/utils/HashMapAdapter.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.utils; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | import java.util.HashMap; 11 | 12 | public class HashMapAdapter extends BaseAdapter { 13 | private Context context; 14 | private HashMap mData; 15 | private String[] mKeys; 16 | public HashMapAdapter(Context context, HashMap data){ 17 | this.context = context; 18 | mData = data; 19 | mKeys = mData.keySet().toArray(new String[data.size()]); 20 | } 21 | 22 | @Override 23 | public int getCount() { 24 | return mData.size(); 25 | } 26 | 27 | public String[] getEntry(int position){ 28 | return new String[] {mKeys[position], getItem(position)}; 29 | } 30 | 31 | @Override 32 | public String getItem(int position) { 33 | return mData.get(mKeys[position]); 34 | } 35 | 36 | @Override 37 | public long getItemId(int arg0) { 38 | return arg0; 39 | } 40 | 41 | @Override 42 | public View getView(int pos, View convertView, ViewGroup parent) { 43 | if (convertView == null) { 44 | convertView = LayoutInflater.from(context). 45 | inflate(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, parent, false); 46 | } 47 | String value = getItem(pos); 48 | ((TextView) convertView).setText(value); 49 | return convertView; 50 | } 51 | 52 | public String getKey(int position) { 53 | return mKeys[position]; 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/utils/ProgressState.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.utils; 2 | 3 | public enum ProgressState { 4 | START, 5 | STOP 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/java/ru/ibakaidov/distypepro/utils/TTS.java: -------------------------------------------------------------------------------- 1 | package ru.ibakaidov.distypepro.utils; 2 | 3 | import android.content.Context; 4 | import android.speech.tts.TextToSpeech; 5 | import android.speech.tts.UtteranceProgressListener; 6 | import android.speech.tts.Voice; 7 | 8 | import java.io.File; 9 | import java.util.Set; 10 | import java.util.UUID; 11 | 12 | public class TTS { 13 | 14 | private final Context context; 15 | private final TextToSpeech tts; 16 | private Callback onInitCallback; 17 | private Callback onPlayCallback; 18 | 19 | public TTS(Context context){ 20 | this.context = context; 21 | tts = new TextToSpeech(context, new TextToSpeech.OnInitListener() { 22 | @Override 23 | public void onInit(int status) { 24 | if(onInitCallback!=null){ 25 | onInitCallback.onDone(status); 26 | } 27 | } 28 | }); 29 | } 30 | 31 | public void setOnPlayCallback(Callback onPlayCallback) { 32 | 33 | this.onPlayCallback = onPlayCallback; 34 | 35 | tts.setOnUtteranceProgressListener(new UtteranceProgressListener() { 36 | @Override 37 | public void onStart(String s) { 38 | onPlayCallback.onDone(ProgressState.START); 39 | } 40 | 41 | @Override 42 | public void onDone(String s) { 43 | onPlayCallback.onDone(ProgressState.STOP); 44 | } 45 | 46 | @Override 47 | public void onError(String s) { 48 | 49 | } 50 | }); 51 | } 52 | 53 | public void setOnInitCallback(Callback onInitCallback) { 54 | this.onInitCallback = onInitCallback; 55 | } 56 | 57 | public Set getVoices(){ 58 | return tts.getVoices(); 59 | } 60 | public void speak(String text){ 61 | if(tts.isSpeaking()){ 62 | tts.stop(); 63 | onPlayCallback.onDone(ProgressState.STOP); 64 | return; 65 | } 66 | 67 | tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, "1"); 68 | } 69 | 70 | public File speakToBuffer(String text) throws Exception { 71 | File file = new File(context.getCacheDir(), UUID.randomUUID().toString()+".wav"); 72 | 73 | int res = tts.synthesizeToFile(text, null, file,null); 74 | if(res!= TextToSpeech.SUCCESS) throw new Exception("synth error"); 75 | return file; 76 | 77 | } 78 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_clear_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_lock_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_lock_open_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_remove_red_eye_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_auth.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 14 | 15 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_spotlight.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/bank_group.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 18 | 26 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/input_group.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 13 | 17 |