I'm trying to write a large multidimensional array into an ascii file in order to debug my program. (My phone has Android 8.0.0)
I added this line to my AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
The function is called within a subclass, so no context is available here:
public void writeAsAscii(float [][][][] labelProbArray, int Nx, int Ny, int Nz ) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
Log.i("State","External storage is writable");
} else{
Log.i("State","Error: External storage NOT WRITABLE");
}
File path = Environment.getExternalStorageDirectory();
File file = new File(path, "MyFile.txt");
try {
FileWriter writer = new FileWriter(file, true);
for (int i = 0; i < Nx; i++) {
for (int j = 0; j < Ny; j++) {
for (int k = 0; k < Nz; k++){
writer.write(String.valueOf(labelProbArray[0][i][j][k])+" ");
}
writer.write("\n"); // write new line
}
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
I'm following the advice given in related posts, but the writing fails. In debug mode I can see that path is set do /storage/emulated/0/MyFile.txt , but I cannot find the file anywhere on my phone that I use for debugging. So the file is probably never created. The try block is failing and the catch block reports:
java.io.FileNotFoundException: /storage/emulated/0/MyFile.txt (Permission denied)
I'm not sure what is going wrong. Is the file not created? Do I need to add more Permissions?
A short clear toy example how to write custom ascii files anywhere in the code would be nice, as it is crucial for debugging large arrays.
WRITE_EXTERNAL_STORAGE is a dangerous permission so you need to request this permission programmatically before trying to create a file in the file system.
Use this code to request permission from your activity onCreate():
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 12)
And after that check if it is granted or not:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
12 -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// permission was granted
} else {
// permission denied
}
return
}
// Add other 'when' lines to check for other
// permissions this app might request.
else -> {
// Ignore all other requests.
}
}
}
More about permmission: https://developer.android.com/guide/topics/permissions/overview
Related
I wrote this code to save the values of / load values to int arrayScore[10][1000] on/from a text file (ScoreFile.txt), for when the app is closed/opened.
saveData is contained in the onStop() method and loadData is contained in the onCreate() method. When I open/run/close the app, no exception is thrown. The toast says indeed "Data loaded" and "Data saved".
The values of arrayScore[][] do change during the runtime of the app, which is confirmed by these new values appearing on-screen. So during runtime everything works fine.
However, the values of the last session are not loaded when a new session is started, and after that last session ScoreFile.txt is nowhere to be found on my phone.
File scoreFile = new File("ScoreFile.txt");
public void saveData() {
try {
PrintWriter output = new PrintWriter(scoreFile);
int p, q;
for (p = 0; p <= 9; p++) {
for (q = 0; q <= 999; q++) {
output.println(arrayScore[p][q]);
}
}
} catch (Exception e){
Toast.makeText(this, "Exception savedData", Toast.LENGTH_LONG).show();
} finally {
}
Toast.makeText(this, "Data saved", Toast.LENGTH_LONG).show();
}
public void loadData() {
try {
Scanner input = new Scanner(scoreFile);
int p, q;
for (p = 0; p <= 9; p++) {
for (q = 0; q <= 999; q++) {
arrayScore[p][q] = input.nextInt();
}
}
} catch (Exception e) {
Toast.makeText(this, "Exception loadData", Toast.LENGTH_LONG).show();
} finally {
}
Toast.makeText(this, "Data loaded", Toast.LENGTH_LONG).show();
}
A few of points. First, do you have permission to write create a file at that location? Ordinarily, you'd preface the name of your file with the path to internal/external storage for your app, and check that it exists before you do anything. For example (there are many ways to write a file):
File file = new File(context.getFilesDir(), "ScoreFile.txt");
try (final FileOutputStream fos = context.openFileOutput(file, Context.MODE_PRIVATE)){
fos = openFileOutput(filename, Context.MODE_PRIVATE);
//for...
fos.write(/*...*/);
} catch (Exception e) {
e.printStackTrace();
}
Second, have you declared permission for reading and writing to the filesystem in your manifest? You may need runtime permissions for this as well on Oreo+.
Third,of course you see the "Data Saved" toast, because it runs no matter what. Replace it with throws new RuntimeException(e); in your catch and see whether your app crashes. If it does, check LogCat and add the exception to your question.
Fourth, you may need to call flush() on your PrintWriter
Please help with clearing cache in android. I am able to delete cache with below code on android version 6.0. When i am trying it in 7.1 nothing changes. what am i missing?
//method to delete cache- issue reported
public static void deleteCache(Context context) {
try {
File dir = context.getCacheDir();
deleteDir(dir);
Log.d("cache clear", "cache delete "+dir);
} catch (Exception e) { e.printStackTrace();}
}
public static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
Log.d("cache clear", "cache delete 1");
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
return dir.delete();
} else if(dir!= null && dir.isFile()) {
return dir.delete();
} else {
return false;
}
}
From Android API 19 you can do that:
((ActivityManager)context.getSystemService(ACTIVITY_SERVICE)).clearApplicationUserData();
Official docs:
clearApplicationUserData
added in API level 19
public boolean clearApplicationUserData ()
Permits an application to erase its own data from disk. This is equivalent to the user choosing to clear the app's data from within the device settings UI. It erases all dynamic data associated with the app -- its private data and data in its private area on external storage -- but does not remove the installed application itself, nor any OBB files. It also revokes all runtime permissions that the app has acquired, clears all notifications and removes all Uri grants related to this application.
Returns
boolean true if the application successfully requested that the application's data be erased; false otherwise.
EDIT
From your Activity don't call
deleteCache(this)
but call
deleteCache(getApplicationContext())
Does anyone know how the missing permissions behave and when is it shown in logcat?
I tried removing the INTERNET permission intentionally to trigger this exception, but it's not being triggered at all during httpsURLConnection.connect() below -- what happens is that it goes straight to the finally block.
Initially I thought it's because the permission was granted before and the app/test device remembers it, so I uninstalled the app then reinstalled it but the same thing happens.
Does anyone know what triggered this behavior? Thanks!
Edit: I have another app (Sunshine app from the Udacity course) where I copied this code from, and that one shows the permission error.
Excerpt from my class -- expecting a Permission denied (missing INTERNET permission?) in the httpsURLConnection.connect() line
public class MovieDBAPI extends AsyncTask<String, Object, List<Movie>> {
final String TAG = getClass().getSimpleName();
protected List<Movie> doInBackground(String... params) {
BufferedReader bufferedReader = null;
HttpsURLConnection httpsURLConnection = null;
StringBuffer stringBuffer = null;
try {
//create a URL
URL url = new URL(buildURL(params[0]));
Log.v(TAG, url.toString());
httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setRequestMethod("GET");
httpsURLConnection.connect();
//get string input
InputStream inputStream = httpsURLConnection.getInputStream();
if (inputStream == null) {
//no input stream, nothing to do
return null;
}
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
stringBuffer = new StringBuffer();
while((line = bufferedReader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
stringBuffer.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//if stringBuffer is not null, then prepare the result
if (stringBuffer != null) {
return getMovieDataFromJSON(stringBuffer.toString());
}
if (httpsURLConnection != null) {
httpsURLConnection.disconnect();
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
}
For Android M and above, check Android docs..
You don't need to permission access for normal permissions
https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
https://developer.android.com/guide/topics/security/normal-permissions.html
For Marshmallow and above, you would need to request permissions at runtime. Mentioning them in AndroidManifest.xml isn't necessary,
Please refer to the following link,
https://developer.android.com/training/permissions/requesting.html
As for marshmallow and above android has a new run time permission system. You have ask for at run time . Add the following snippet into your class which will help you to ask for run time permission .
private int INTERNET_PERMISSION_CODE = 23;
//We are calling this method to check the permission status
private boolean isInternetnAllowed() {
//Getting the permission status
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET);
//If permission is granted returning true
if (result == PackageManager.PERMISSION_GRANTED)
return true;
//If permission is not granted returning false
return false;
}
//Requesting permission
private void requestInternetPermission(){
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.INTERNET)){
//If the user has denied the permission previously your code will come to this block
//Here you can explain why you need this permission
//Explain here why you need this permission
}
//And finally ask for the permission
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.INTERNET}, INTERNET_PERMISSION_CODE);
}
//This method will be called when the user will tap on allow or deny
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
//Checking the request code of our request
if(requestCode == INTERNET_PERMISSION_CODE){
//If permission is granted
if(grantResults.length >0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
//Displaying a toast
Toast.makeText(this,"Permission granted for internet",Toast.LENGTH_LONG).show();
}else{
//Displaying another toast if permission is not granted
Toast.makeText(this,"Oops you just denied the permission",Toast.LENGTH_LONG).show();
}
}
}
Then what ever you are doing in doInBackground method
do it like this way
if(isInternetnAllowed()){
//do your doInbackground stuff
}else {
requestInternetPermission();
}
Hope it helps
My app runs on Android 5.0. I use method getExternalFilesDirs() to check if external SD card is available. If it returns more than 1 File, that means external SD card exists.
But on some devices (for example Elephone G2), method getExternalFilesDirs() returns only one directory of primary storage. I'm sure that device has external SD card (/storage/sdcard1/).
Can any one give me the answer?
For getExternalFilesDirs to return the path of the sdcard, the OEM must have set the SECONDARY_STORAGE environment variable in the device specific init.rc file as mentioned here:
https://source.android.com/devices/storage/config-example.html
Look at the source of getExternalFilesDirs here:
http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/app/ContextImpl.java#1039
The value is obtained from Environment.buildExternalStorageAppFilesDirs. Look at that source here:
http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/os/Environment.java#206
The value is dependent on mExternalDirsForApp, which in turn is populated by reading the contents of SECONDARY_STORAGE variable:
http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/os/Environment.java#136
As you can see, if the SECONDARY_STORAGE variable is not set, the sdcard path will not be returned.
You can cross-check this by going to adb shell and looking at the output of echo $SECONDARY_STORAGE
In my projects using this code & i don't have any problem.
method of getExternalFilesDirs return array with 2 length.
Dirs[0] ==> Internal Sorage
Dirs[1] ==> External Storage
File[] Dirs = ContextCompat.getExternalFilesDirs(MyApp.GetContext(), null);
this issue there is in some of Lenovo device too.
my solution is this.
String EXTERNAL_SD_PATH1;
String EXTERNAL_SD_PATH2;
public boolean hasExternalSDCard()
{
try
{
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
return true;
}
catch (Throwable e)
{}
return false;
}
#SuppressLint("SdCardPath")
protected synchronized void _prepareStorage()
{
EXTERNAL_SD_PATH1 = null;
EXTERNAL_SD_PATH2 = null;
if (hasExternalSDCard())
{
try
{
if(VERSION_SDK_INT > 18)
{
Context context = getContext();
File[] sds = getExternalFilesDirs("");
if(sds == null)
return;
if(sds.length >= 2)
{
EXTERNAL_SD_PATH1 = TextWorker.getSubStringBeforeLastMark(sds[1].getAbsolutePath(),"/Android/");
if(sds.length > 2)
EXTERNAL_SD_PATH2 = TextWorker.getSubStringBeforeLastMark(sds[2].getAbsolutePath(),"/Android/");
}
else
{
String internal = sds[0].getAbsolutePath();
internal = TextWorker.getSubStringBeforeLastMark(internal,"/Android/");
int len = internal.length();
int num = Integer.valueOf(internal.substring(len - 1));
String ex1 = internal.substring(0, len-1) + (num+1);
File sd1 = new File(ex1);
if(sd1.exists())
EXTERNAL_SD_PATH1 = sd1.getAbsolutePath();
String ex2 = internal.substring(0, len-1) + (num+2);
File sd2 = new File(ex2);
if(sd2.exists())
EXTERNAL_SD_PATH2 = sd2.getAbsolutePath();
}
}
else
{
File sd = Environment.getExternalStorageDirectory();
String path = sd.getAbsolutePath();
if (sd.exists() && (path.contains("/mnt/") || path.contains("/storage") || path.contains("/sdcard")) && (!path.contains("emulate")))
{
EXTERNAL_SD_PATH1 = path;
}
}
}
catch (Throwable e)
{}
}
}
public static String getSubStringBeforeLastMark(String str,String mark)
{
int l = str.lastIndexOf(mark);
if(l == -1 || l == 0)
return "";
return str.substring(0, l);
}
I m developing an android app in which i'm integrating facebook functionality as suggested in this blog http://www.androidhive.info/2012/03/android-facebook-connect-tutorial/
as i'm able to login first time but after logout i cannot able to login again as webcache get created in my application data....
is there any way which i can use to solve my problem ......is i have use below code as suggested here but it cannot delete delete my webcache...
static int clearCacheFolder(final File dir, final int numDays) {
int deletedFiles = 0;
if (dir != null && dir.isDirectory()) {
try {
for (File child : dir.listFiles()) {
// first delete subdirectories recursively
if (child.isDirectory()) {
deletedFiles += clearCacheFolder(child, numDays);
}
// then delete the files and subdirectories in this dir
// only empty directories can be deleted, so subdirs have
// been done first
if (child.lastModified() < new Date().getTime() - numDays
* DateUtils.DAY_IN_MILLIS) {
if (child.delete()) {
deletedFiles++;
}
}
}
} catch (Exception e) {
Log.e("error Tag",
String.format("Failed to clean the cache, error %s",
e.getMessage()));
}
}
return deletedFiles;
}
please do suggest some another way to delete my webcache without deleting my database.....