diff --git a/.gitignore b/.gitignore index 39fb081..5561991 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,10 @@ *.iml .gradle +.idea /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures -.externalNativeBuild +.externalNativeBuild \ No newline at end of file diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml deleted file mode 100644 index e3206c4..0000000 --- a/.idea/assetWizardSettings.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser deleted file mode 100644 index 8bc5559..0000000 Binary files a/.idea/caches/build_file_checksums.ser and /dev/null differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 30aa626..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 019957e..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 635999d..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 2da5b66..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 148e79f..4a5447f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,19 +1,22 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + id 'com.android.application' + id 'kotlin-android' +} android { - compileSdkVersion 27 + compileSdkVersion 34 buildToolsVersion '28.0.3' defaultConfig { applicationId "org.phenoapps.verify" minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 34 versionCode 2 versionName "1.1" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' multiDexEnabled = true vectorDrawables.useSupportLibrary = true } + namespace "org.phenoapps.verify" buildTypes { release { shrinkResources false @@ -41,17 +44,17 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { + androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) implementation 'com.journeyapps:zxing-android-embedded:3.6.0' - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.android.support:design:27.1.1' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation 'com.android.support:support-v4:27.1.1' - implementation 'com.android.support:support-vector-drawable:27.1.1' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.10.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.vectordrawable:vectordrawable:1.1.0' implementation 'com.github.apl-devs:appintro:v4.2.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' implementation files('libs/poi-3.12-android-a.jar') implementation files('libs/poi-ooxml-schemas-3.12-20150511-a.jar') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e6fcf4b..b290ec8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,9 +2,9 @@ - + + android:windowSoftInputMode="adjustPan" + android:exported="true"> - + + android:name="org.phenoapps.verify.ScanActivity" + android:exported="true"/> - - + - diff --git a/app/src/main/java/org/phenoapps/verify/CompareActivity.kt b/app/src/main/java/org/phenoapps/verify/CompareActivity.kt index 775d565..1a066d8 100644 --- a/app/src/main/java/org/phenoapps/verify/CompareActivity.kt +++ b/app/src/main/java/org/phenoapps/verify/CompareActivity.kt @@ -2,8 +2,8 @@ package org.phenoapps.verify import android.app.Activity import android.os.Bundle -import android.support.v7.app.AlertDialog -import android.support.v7.app.AppCompatActivity +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity import android.text.Editable import android.text.TextWatcher import android.util.LayoutDirection diff --git a/app/src/main/java/org/phenoapps/verify/IntroActivity.java b/app/src/main/java/org/phenoapps/verify/IntroActivity.java index 0622a82..e2be2a5 100644 --- a/app/src/main/java/org/phenoapps/verify/IntroActivity.java +++ b/app/src/main/java/org/phenoapps/verify/IntroActivity.java @@ -2,8 +2,8 @@ import android.graphics.Color; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import com.github.paolorotolo.appintro.AppIntro2; import com.github.paolorotolo.appintro.AppIntroFragment; diff --git a/app/src/main/java/org/phenoapps/verify/LoaderDBActivity.java b/app/src/main/java/org/phenoapps/verify/LoaderDBActivity.java index 189da69..249f417 100644 --- a/app/src/main/java/org/phenoapps/verify/LoaderDBActivity.java +++ b/app/src/main/java/org/phenoapps/verify/LoaderDBActivity.java @@ -1,24 +1,16 @@ package org.phenoapps.verify; -import android.content.ContentUris; import android.content.ContentValues; -import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.net.Uri; -import android.os.Build; import android.os.Bundle; -import android.os.Environment; -import android.provider.DocumentsContract; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.MenuItem; import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -43,8 +35,7 @@ import java.io.InputStreamReader; import java.util.HashSet; import java.util.Iterator; - -import org.phenoapps.verify.R; +import java.util.Objects; public class LoaderDBActivity extends AppCompatActivity { @@ -53,7 +44,6 @@ public class LoaderDBActivity extends AppCompatActivity { private Uri mFileUri; private String mDelimiter; private String mFileExtension; - private String mFilePath; private String mFileName; private Workbook mCurrentWorkbook; @@ -105,7 +95,12 @@ protected void onCreate(Bundle savedInstanceState) { mFileUri = getIntent().getData(); - int lastSlash = mFileUri.getPath().lastIndexOf('/'); + if (mFileUri == null ){ + Toast.makeText(this, "There was a problem reading this file", Toast.LENGTH_LONG).show(); + return; + } + + int lastSlash = Objects.requireNonNull(mFileUri.getPath()).lastIndexOf('/'); if (lastSlash != -1) { mFileName = mFileUri.getPath().substring(lastSlash + 1); } else mFileName = ""; @@ -135,16 +130,17 @@ private void parseHeaders(Uri data) { try { //query file path type - mFilePath = getPath(mFileUri); - int lastDot = mFileUri.toString().lastIndexOf("."); +// mFilePath = getPath(LoaderDBActivity.this ,mFileUri); + String mFilePath = UriHandler.getPath(LoaderDBActivity.this, mFileUri); + int lastDot = mFilePath.lastIndexOf("."); // changed from mFileUri to mFilePath due to the files in download folder have URI without extension if (lastDot == -1) { Toast.makeText(this, "Imported file must have an extension. (e.g: .csv, .tsv)", Toast.LENGTH_LONG).show(); finish(); } - mFileExtension = mFileUri.toString().substring(lastDot + 1); + mFileExtension = mFilePath.substring(lastDot + 1); StringBuilder header = new StringBuilder(); //xls library support @@ -531,59 +527,4 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } } - - - //based on https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java - public String getPath(Uri uri) { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - - if (DocumentsContract.isDocumentUri(LoaderDBActivity.this, uri)) { - - if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { - final String[] doc = DocumentsContract.getDocumentId(uri).split(":"); - final String documentType = doc[0]; - - if ("primary".equalsIgnoreCase(documentType)) { - return Environment.getExternalStorageDirectory() + "/" + doc[1]; - } - } - else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { - final String id = DocumentsContract.getDocumentId(uri); - if (!id.isEmpty()) { - if (id.startsWith("raw:")) { - return id.replaceFirst("raw:", ""); - } - } - final Uri contentUri = ContentUris.withAppendedId( - Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); - return getDataColumn(LoaderDBActivity.this, contentUri, null, null); - } - } - else if ("file".equalsIgnoreCase(uri.getScheme())) { - return uri.getPath(); - } else if ("com.estrongs.files".equals(uri.getAuthority())) { - return uri.getPath(); - } - } - return null; - } - - public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { - - Cursor cursor = null; - final String column = "_data"; - final String[] projection = { column }; - try { - cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); - if (cursor != null && cursor.moveToFirst()) { - final int index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(index); - } - } finally { - if (cursor != null) - cursor.close(); - } - return null; - } } diff --git a/app/src/main/java/org/phenoapps/verify/MainActivity.java b/app/src/main/java/org/phenoapps/verify/MainActivity.java index 4a26fab..c9d424b 100644 --- a/app/src/main/java/org/phenoapps/verify/MainActivity.java +++ b/app/src/main/java/org/phenoapps/verify/MainActivity.java @@ -11,22 +11,20 @@ import android.database.sqlite.SQLiteStatement; import android.media.MediaPlayer; import android.media.MediaScannerConnection; -import android.media.Ringtone; -import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.os.Environment; -import android.os.Parcelable; +import android.os.storage.StorageManager; import android.preference.PreferenceManager; -import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.design.widget.NavigationView; -import android.support.v4.app.ActivityCompat; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; + +import androidx.annotation.NonNull; +import com.google.android.material.navigation.NavigationView; +import androidx.core.app.ActivityCompat; +import androidx.core.view.GravityCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import android.text.InputType; import android.text.method.ScrollingMovementMethod; import android.util.Log; @@ -40,9 +38,9 @@ import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.Button; import android.widget.EditText; import android.widget.ListView; -import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; @@ -51,15 +49,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.HashSet; import java.util.Locale; -import java.util.Timer; -import java.util.TimerTask; - -import org.phenoapps.verify.R; public class MainActivity extends AppCompatActivity { @@ -563,78 +558,168 @@ private synchronized void askUserExportFileName() { if (lastDot != -1) { mFileName = mFileName.substring(0, lastDot); } - input.setText(mFileName + "_" + sdf.format(c.getTime())); + input.setText("Verify_"+ sdf.format(c.getTime())); input.setInputType(InputType.TYPE_CLASS_TEXT); builder.setView(input); - builder.setPositiveButton("Export", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String value = input.getText().toString(); - if (!value.isEmpty()) { - if (isExternalStorageWritable()) { - try { - File verifyDirectory = new File(Environment.getExternalStorageDirectory().getPath() + "/Verify"); - final File output = new File(verifyDirectory, value + ".csv"); - final FileOutputStream fstream = new FileOutputStream(output); - final SQLiteDatabase db = mDbHelper.getReadableDatabase(); - final String table = IdEntryContract.IdEntry.TABLE_NAME; - final Cursor cursor = db.query(table, null, null, null, null, null, null); - //final Cursor cursor = db.rawQuery("SElECT * FROM VERIFY", null); - - //first write header line - final String[] headers = cursor.getColumnNames(); + builder.setPositiveButton("Export", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int which) { + String value = input.getText().toString(); + mFileName = value; + final Intent i; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + i = new Intent(Intent.ACTION_CREATE_DOCUMENT); + i.setType("*/*"); + i.putExtra(Intent.EXTRA_TITLE, value+".csv"); + startActivityForResult(Intent.createChooser(i, "Choose folder to export file."), VerifyConstants.PICK_CUSTOM_DEST); + }else{ + writeToExportPath(); + } + } + }); + builder.show(); + } + + public void writeToExportPath(){ + String value = mFileName; + + if (!value.isEmpty()) { + if (isExternalStorageWritable()) { + try { + File verifyDirectory = new File(Environment.getExternalStorageDirectory().getPath() + "/Verify"); + final File output = new File(verifyDirectory, value + ".csv"); + final FileOutputStream fstream = new FileOutputStream(output); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); + final String table = IdEntryContract.IdEntry.TABLE_NAME; + final Cursor cursor = db.query(table, null, null, null, null, null, null); + //final Cursor cursor = db.rawQuery("SElECT * FROM VERIFY", null); + + //first write header line + final String[] headers = cursor.getColumnNames(); + for (int i = 0; i < headers.length; i++) { + if (i != 0) fstream.write(",".getBytes()); + fstream.write(headers[i].getBytes()); + } + fstream.write(line_separator.getBytes()); + //populate text file with current database values + if (cursor.moveToFirst()) { + do { for (int i = 0; i < headers.length; i++) { if (i != 0) fstream.write(",".getBytes()); - fstream.write(headers[i].getBytes()); + final String val = cursor.getString( + cursor.getColumnIndexOrThrow(headers[i]) + ); + if (val == null) fstream.write("null".getBytes()); + else fstream.write(val.getBytes()); } fstream.write(line_separator.getBytes()); - //populate text file with current database values - if (cursor.moveToFirst()) { - do { - for (int i = 0; i < headers.length; i++) { - if (i != 0) fstream.write(",".getBytes()); - final String val = cursor.getString( - cursor.getColumnIndexOrThrow(headers[i]) - ); - if (val == null) fstream.write("null".getBytes()); - else fstream.write(val.getBytes()); - } - fstream.write(line_separator.getBytes()); - } while (cursor.moveToNext()); - } + } while (cursor.moveToNext()); + } - cursor.close(); - fstream.flush(); - fstream.close(); - scanFile(MainActivity.this, output); + cursor.close(); + fstream.flush(); + fstream.close(); + scanFile(MainActivity.this, output); /*MediaScannerConnection.scanFile(MainActivity.this, new String[] {output.toString()}, null, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(String path, Uri uri) { Log.v("scan complete", path); } });*/ - } catch (SQLiteException e) { - e.printStackTrace(); - Toast.makeText(MainActivity.this, "Error exporting file, is your table empty?", Toast.LENGTH_SHORT).show(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException io) { - io.printStackTrace(); - } - } else { - Toast.makeText(MainActivity.this, - "External storage not writable.", Toast.LENGTH_SHORT).show(); - } - } else { - Toast.makeText(MainActivity.this, - "Must enter a file name.", Toast.LENGTH_SHORT).show(); + }catch (NullPointerException npe){ + npe.printStackTrace(); + Toast.makeText(this, "Error in opening the Specified file", Toast.LENGTH_LONG).show(); + } + catch (SQLiteException e) { + e.printStackTrace(); + Toast.makeText(MainActivity.this, "Error exporting file, is your table empty?", Toast.LENGTH_SHORT).show(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException io) { + io.printStackTrace(); } + } else { + Toast.makeText(MainActivity.this, + "External storage not writable.", Toast.LENGTH_SHORT).show(); } - }); + } else { + Toast.makeText(MainActivity.this, + "Must enter a file name.", Toast.LENGTH_SHORT).show(); + } + } - builder.show(); + public void writeToExportPath(Uri uri){ + + String value = mFileName; + + if (uri == null){ + Toast.makeText(this, "Unable to open the Specified file", Toast.LENGTH_LONG).show(); + return; + } + + if (!value.isEmpty()) { + if (isExternalStorageWritable()) { + try { + final File output = new File(uri.getPath()); + final OutputStream fstream = getContentResolver().openOutputStream(uri); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); + final String table = IdEntryContract.IdEntry.TABLE_NAME; + final Cursor cursor = db.query(table, null, null, null, null, null, null); + //final Cursor cursor = db.rawQuery("SElECT * FROM VERIFY", null); + + //first write header line + final String[] headers = cursor.getColumnNames(); + for (int i = 0; i < headers.length; i++) { + if (i != 0) fstream.write(",".getBytes()); + fstream.write(headers[i].getBytes()); + } + fstream.write(line_separator.getBytes()); + //populate text file with current database values + if (cursor.moveToFirst()) { + do { + for (int i = 0; i < headers.length; i++) { + if (i != 0) fstream.write(",".getBytes()); + final String val = cursor.getString( + cursor.getColumnIndexOrThrow(headers[i]) + ); + if (val == null) fstream.write("null".getBytes()); + else fstream.write(val.getBytes()); + } + fstream.write(line_separator.getBytes()); + } while (cursor.moveToNext()); + } + cursor.close(); + fstream.flush(); + fstream.close(); + scanFile(MainActivity.this, output); + /*MediaScannerConnection.scanFile(MainActivity.this, new String[] {output.toString()}, null, new MediaScannerConnection.OnScanCompletedListener() { + @Override + public void onScanCompleted(String path, Uri uri) { + Log.v("scan complete", path); + } + });*/ + }catch (NullPointerException npe){ + npe.printStackTrace(); + Toast.makeText(this, "Error in opening the Specified file", Toast.LENGTH_LONG).show(); + } + catch (SQLiteException e) { + e.printStackTrace(); + Toast.makeText(MainActivity.this, "Error exporting file, is your table empty?", Toast.LENGTH_SHORT).show(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException io) { + io.printStackTrace(); + } + } else { + Toast.makeText(MainActivity.this, + "External storage not writable.", Toast.LENGTH_SHORT).show(); + } + } else { + Toast.makeText(MainActivity.this, + "Must enter a file name.", Toast.LENGTH_SHORT).show(); + } } //returns index of table with identifier = id, returns -1 if not found @@ -728,30 +813,30 @@ final public boolean onCreateOptionsMenu(Menu m) { @Override final public boolean onOptionsItemSelected(MenuItem item) { - - DrawerLayout dl = (DrawerLayout) findViewById(org.phenoapps.verify.R.id.drawer_layout); + DrawerLayout dl = (DrawerLayout) findViewById(R.id.drawer_layout); + int actionCamera = R.id.action_camera; + int actionCompare = R.id.action_compare; if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } - switch (item.getItemId()) { - case android.R.id.home: - dl.openDrawer(GravityCompat.START); - break; - case org.phenoapps.verify.R.id.action_camera: - final Intent cameraIntent = new Intent(this, ScanActivity.class); - startActivityForResult(cameraIntent, VerifyConstants.CAMERA_INTENT_REQ); - break; - case R.id.action_compare: - final Intent compareIntent = new Intent(MainActivity.this, CompareActivity.class); - runOnUiThread(new Runnable() { - @Override public void run() { - startActivity(compareIntent); - } - }); - break; - default: - return super.onOptionsItemSelected(item); + if (item.getItemId() == android.R.id.home){ + dl.openDrawer(GravityCompat.START); + } + else if(item.getItemId() == actionCamera){ + final Intent cameraIntent = new Intent(this, ScanActivity.class); + startActivityForResult(cameraIntent, VerifyConstants.CAMERA_INTENT_REQ); + } + else if(item.getItemId() == actionCompare){ + final Intent compareIntent = new Intent(MainActivity.this, CompareActivity.class); + runOnUiThread(new Runnable() { + @Override public void run() { + startActivity(compareIntent); + } + }); + } + else{ + return super.onOptionsItemSelected(item); } return true; } @@ -765,6 +850,9 @@ final protected void onActivityResult(int requestCode, int resultCode, Intent in if (intent != null) { switch (requestCode) { + case VerifyConstants.PICK_CUSTOM_DEST: + writeToExportPath(intent.getData()); + break; case VerifyConstants.DEFAULT_CONTENT_REQ: Intent i = new Intent(this, LoaderDBActivity.class); i.setData(intent.getData()); @@ -878,37 +966,75 @@ public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { } private void selectDrawerItem(MenuItem menuItem) { - switch (menuItem.getItemId()) { - - case org.phenoapps.verify.R.id.nav_import: - final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this); - final int scanMode = Integer.valueOf(sharedPref.getString(SettingsActivity.SCAN_MODE_LIST, "-1")); - - final Intent i = new Intent(Intent.ACTION_GET_CONTENT); - i.setType("*/*"); - startActivityForResult(Intent.createChooser(i, "Choose file to import."), VerifyConstants.DEFAULT_CONTENT_REQ); - - break; - case org.phenoapps.verify.R.id.nav_settings: - final Intent settingsIntent = new Intent(this, SettingsActivity.class); - startActivityForResult(settingsIntent, VerifyConstants.SETTINGS_INTENT_REQ); - break; - case org.phenoapps.verify.R.id.nav_export: - askUserExportFileName(); - break; - case org.phenoapps.verify.R.id.nav_about: - showAboutDialog(); - break; - case org.phenoapps.verify.R.id.nav_intro: - final Intent intro_intent = new Intent(MainActivity.this, IntroActivity.class); - runOnUiThread(new Runnable() { - @Override public void run() { - startActivity(intro_intent); - } - }); - break; - } + int itemId = menuItem.getItemId(); + // constants like id in R class are no longer final, thus can't use switch here + + if (itemId == R.id.nav_import){ + final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this); + final int scanMode = Integer.valueOf(sharedPref.getString(SettingsActivity.SCAN_MODE_LIST, "-1")); + final Intent i; + File verifyDirectory = new File(getExternalFilesDir(null), "/Verify"); + + File[] files = verifyDirectory.listFiles(); + + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Select files from?"); + builder.setPositiveButton("Storage", + new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int id) + { + Intent i; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + i = new Intent(Intent.ACTION_OPEN_DOCUMENT); + }else{ + i = new Intent(Intent.ACTION_GET_CONTENT); + } + i.setType("*/*"); + startActivityForResult(Intent.createChooser(i, "Choose file to import."), VerifyConstants.DEFAULT_CONTENT_REQ); + } + }); + + builder.setNegativeButton("Verify Directory", + new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int id) + { + + AlertDialog.Builder fileBuilder = new AlertDialog.Builder(MainActivity.this); + fileBuilder.setTitle("Select the sample file"); + final int[] checkedItem = {-1}; + String[] listItems = verifyDirectory.list(); + fileBuilder.setSingleChoiceItems(listItems, checkedItem[0],(fileDialog, which) -> { + checkedItem[0] = which; + Intent i = new Intent(MainActivity.this, LoaderDBActivity.class); + i.setData(Uri.fromFile(files[which])); + startActivityForResult(i, VerifyConstants.LOADER_INTENT_REQ); + fileDialog.dismiss(); + }); + + fileBuilder.show(); + + } + }); + builder.show(); + } else if (itemId == R.id.nav_settings) { + final Intent settingsIntent = new Intent(this, SettingsActivity.class); + startActivityForResult(settingsIntent, VerifyConstants.SETTINGS_INTENT_REQ); + } else if (itemId == R.id.nav_export) { + askUserExportFileName(); + } else if (itemId == R.id.nav_about) { + showAboutDialog(); + } else if (itemId == R.id.nav_intro) { + final Intent intro_intent = new Intent(MainActivity.this, IntroActivity.class); + runOnUiThread(new Runnable() { + @Override public void run() { + startActivity(intro_intent); + } + }); + } DrawerLayout dl = (DrawerLayout) findViewById(org.phenoapps.verify.R.id.drawer_layout); dl.closeDrawers(); } @@ -1042,6 +1168,7 @@ final public void onDestroy() { @Override public void onRequestPermissionsResult(int resultCode, String[] permissions, int[] granted) { + super.onRequestPermissionsResult(resultCode, permissions, granted); boolean externalWriteAccept = false; if (resultCode == VerifyConstants.PERM_REQ) { for (int i = 0; i < permissions.length; i++) { @@ -1051,7 +1178,9 @@ public void onRequestPermissionsResult(int resultCode, String[] permissions, int } } if (externalWriteAccept && isExternalStorageWritable()) { - File verifyDirectory = new File(Environment.getExternalStorageDirectory().getPath() + "/Verify"); + + File verifyDirectory = new File(getExternalFilesDir(null), "/Verify"); + if (!verifyDirectory.isDirectory()) { final boolean makeDirsSuccess = verifyDirectory.mkdirs(); if (!makeDirsSuccess) Log.d("Verify Make Directory", "failed"); diff --git a/app/src/main/java/org/phenoapps/verify/ScanActivity.java b/app/src/main/java/org/phenoapps/verify/ScanActivity.java index fe10476..fc05790 100644 --- a/app/src/main/java/org/phenoapps/verify/ScanActivity.java +++ b/app/src/main/java/org/phenoapps/verify/ScanActivity.java @@ -3,7 +3,7 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.view.KeyEvent; import android.view.MenuItem; import android.widget.ImageView; diff --git a/app/src/main/java/org/phenoapps/verify/SettingsActivity.java b/app/src/main/java/org/phenoapps/verify/SettingsActivity.java index 4379f06..fb5a18f 100644 --- a/app/src/main/java/org/phenoapps/verify/SettingsActivity.java +++ b/app/src/main/java/org/phenoapps/verify/SettingsActivity.java @@ -1,9 +1,8 @@ package org.phenoapps.verify; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.Preference; -import android.support.v7.app.AppCompatActivity; + +import androidx.appcompat.app.AppCompatActivity; import android.view.MenuItem; public class SettingsActivity extends AppCompatActivity { diff --git a/app/src/main/java/org/phenoapps/verify/UriHandler.java b/app/src/main/java/org/phenoapps/verify/UriHandler.java new file mode 100644 index 0000000..51c7d5f --- /dev/null +++ b/app/src/main/java/org/phenoapps/verify/UriHandler.java @@ -0,0 +1,261 @@ +package org.phenoapps.verify; + +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.OpenableColumns; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + + + + +public class UriHandler { + + /** + * Resolve the file name from the content Uri. + * @param context + * @param uri + * @return + */ + public static String getFileName(@NonNull Context context, Uri uri) { + String mimeType = context.getContentResolver().getType(uri); + String fileName = null; + + if (mimeType == null && context != null) { + String path = getPath(context, uri); + if (path == null) { + fileName = getName(uri.toString()); + } else { + File file = new File(path); + fileName = file.getName(); + } + } else { + Cursor returnCursor = context.getContentResolver().query(uri, null, + null, null, null); + if (returnCursor != null) { + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + returnCursor.moveToFirst(); + fileName = returnCursor.getString(nameIndex); + returnCursor.close(); + } + } + + return fileName; + } +//test + /** + * Returns the effective file name from the provided Uri. + * @param fileName + * @return + */ + public static String getName(String fileName) { + if (fileName == null) { + return null; + } + int index = fileName.lastIndexOf('/'); + return fileName.substring(index + 1); + } + + /** + * Returns the documents directory in the storage. + * @param context + * @return + */ + public static File getDocumentCacheDir(@NonNull Context context) { + File dir = new File(context.getCacheDir(), "documents"); + if (!dir.exists()) { + dir.mkdirs(); + } + return dir; + } + + /** + * Generates new file in the specified cached directory. + * @param name + * @param directory + * @return + */ + public static File generateFileName(@Nullable String name, File directory) { + if (name == null) { + return null; + } + + File file = new File(directory, name); + + if (file.exists()) { + String fileName = name; + String extension = ""; + int dotIndex = name.lastIndexOf('.'); + if (dotIndex > 0) { + fileName = name.substring(0, dotIndex); + extension = name.substring(dotIndex); + } + + int index = 0; + + while (file.exists()) { + index++; + name = fileName + '(' + index + ')' + extension; + file = new File(directory, name); + } + } + + try { + if (!file.createNewFile()) { + return null; + } + } catch (IOException e) { + return null; + } + + return file; + } + + /** + * creates a locally cached file for content from any cloud provider. + * @param context + * @param uri + * @param destinationPath + */ + + private static void saveFileFromUri(Context context, Uri uri, String destinationPath) { + InputStream uriInputStream = null; + BufferedOutputStream bufferStream = null; + try { + uriInputStream = context.getContentResolver().openInputStream(uri); + bufferStream = new BufferedOutputStream(new FileOutputStream(destinationPath, false)); + byte[] buf = new byte[1024]; + uriInputStream.read(buf); + do { + bufferStream.write(buf); + } while (uriInputStream.read(buf) != -1); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (uriInputStream != null) uriInputStream.close(); + if (bufferStream != null) bufferStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + final int index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + /** + * The utility method helps to resolve the local path of a file from a content Uri. + * @param context + * @param uri + * @return + */ + public static String getLocalPath(final Context context, Uri uri) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (DocumentsContract.isDocumentUri(context, uri)) { + + if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { + final String[] doc = DocumentsContract.getDocumentId(uri).split(":"); + final String documentType = doc[0]; + + if ("primary".equalsIgnoreCase(documentType)) { + return Environment.getExternalStorageDirectory() + "/" + doc[1]; + } + } else if ("com.android.providers.media.documents".equals(uri.getAuthority()) || + "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || + "com.microsoft.skydrive.content.StorageAccessProvider".equals(uri.getAuthority())) { + String fileName = getFileName(context, uri); + File cacheDir = getDocumentCacheDir(context); + File file = generateFileName(fileName, cacheDir); + String destinationPath = null; + if (file != null) { + destinationPath = file.getAbsolutePath(); + saveFileFromUri(context, uri, destinationPath); + } + return destinationPath; + } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { + final String id = DocumentsContract.getDocumentId(uri); + if (!id.isEmpty()) { + if (id.startsWith("raw:")) { + return id.replaceFirst("raw:", ""); + } + } + String[] contentUriPrefixesToTry = new String[]{ + "content://downloads/public_downloads", + "content://downloads/my_downloads", + "content://downloads/all_downloads" + }; + + for (String contentUriPrefix : contentUriPrefixesToTry) { + try { + Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id)); + String path = getDataColumn(context, contentUri, null, null); + if (path != null) { + return path; + } + } catch (Exception e) { + } + } + + String fileName = getFileName(context, uri); + File cacheDir = getDocumentCacheDir(context); + File file = generateFileName(fileName, cacheDir); + String destinationPath = null; + if (file != null) { + destinationPath = file.getAbsolutePath(); + saveFileFromUri(context, uri, destinationPath); + } + return destinationPath; + } + } else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } else if ("com.estrongs.files".equals(uri.getAuthority())) { + return uri.getPath(); + } + } + return null; + } + + +// All the class methods implemented in reference to https://github.com/coltoscosmin/FileUtils/blob/master/FileUtils.java#L290 + + /** + * + * @param context the context from which the utility method is called in + * @param uri uri of the file to access + * @return path of the file to be accessed + */ + public static String getPath(final Context context, final Uri uri) { + String absolutePath = getLocalPath(context, uri); + return absolutePath != null ? absolutePath : uri.toString(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/phenoapps/verify/VerifyConstants.java b/app/src/main/java/org/phenoapps/verify/VerifyConstants.java index ed8b26f..cfc395c 100644 --- a/app/src/main/java/org/phenoapps/verify/VerifyConstants.java +++ b/app/src/main/java/org/phenoapps/verify/VerifyConstants.java @@ -15,6 +15,7 @@ class VerifyConstants { final static int CAMERA_INTENT_REQ = 102; final static int SETTINGS_INTENT_REQ = 103; final static int DEFAULT_CONTENT_REQ = 104; + final static int PICK_CUSTOM_DEST = 105; //extras final static String CSV_URI = "org.phenoapps.verify.CSV_URI"; diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml index 4521506..08a56bf 100644 --- a/app/src/main/res/layout-land/activity_main.xml +++ b/app/src/main/res/layout-land/activity_main.xml @@ -1,12 +1,12 @@ - - @@ -95,9 +95,9 @@ - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout-xlarge/activity_main.xml b/app/src/main/res/layout-xlarge/activity_main.xml index 8f633d7..bf2bde7 100644 --- a/app/src/main/res/layout-xlarge/activity_main.xml +++ b/app/src/main/res/layout-xlarge/activity_main.xml @@ -1,12 +1,12 @@ - - @@ -95,9 +95,9 @@ - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_compare.xml b/app/src/main/res/layout/activity_compare.xml index 967befc..671eaa7 100644 --- a/app/src/main/res/layout/activity_compare.xml +++ b/app/src/main/res/layout/activity_compare.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_load_file.xml b/app/src/main/res/layout/activity_load_file.xml index 001c50e..e95c962 100644 --- a/app/src/main/res/layout/activity_load_file.xml +++ b/app/src/main/res/layout/activity_load_file.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e42af5f..6ed3db9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,12 +1,12 @@ - - @@ -110,9 +110,9 @@ - + - - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index b8ba6dd..6e1c615 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,16 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.10' + ext { + agp_version = '8.1.2' + } + ext.kotlin_version = '1.7.10' repositories { jcenter() google() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath "com.android.tools.build:gradle:$agp_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6ca4f69..5d952ef 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Dec 05 14:30:29 CST 2018 +#Sat Nov 04 04:43:19 IST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip