My app allows user to export its data to other users or just to save as backup.
The import/export is working FINE
In order to let the user have a sample data when it first installs my app I want to package some default data. I created the sample data, tested IS WORKING FINE, then i packaged it in assets folder and load it when user runs the app for first time.
But i'm getting file not found exception
HERE GOES THE CODE:
private List<Giveaway> loadJsonData(Uri data, User user) {
List<Giveaway> result = null;
try {
InputStream is = this.getContentResolver().openInputStream(data);
Gson parser = new GsonBuilder().setDateFormat("dd/MM/yy").setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setLongSerializationPolicy(LongSerializationPolicy.DEFAULT).setLenient().excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.TRANSIENT).create();
Set<Giveaway> temp = new HashSet<Giveaway>(50);
temp.addAll((Collection<? extends Giveaway>) parser.fromJson(new InputStreamReader(is), TypeToken.getParameterized(List.class, Giveaway.class).getType()));
result = new ArrayList<Giveaway>(temp);
} catch (FileNotFoundException e) {
e.printStackTrace();
result = new ArrayList<Giveaway>(1);
}
return result;
}
and I call it using:
loadJsonData(Uri.parse("file:///android_asset/giveaway_export.json"), sampleUser);
file:///android_asset works for WebView and pretty much nothing else. Use AssetManager to work with assets — you get one of these by calling getAssets() on a Context, such as your Activity.
Use AssetManager this is an example:
AssetManager assetManager = getAssets();
InputStream is = null;
try {
is = assetManager.open("giveaway_export.json");
} catch (IOException e) {
e.printStackTrace();
}
so you have to change your method:
private List<Giveaway> loadJsonData(Uri data, User user) {
List<Giveaway> result = null;
try {
//InputStream is = this.getContentResolver().openInputStream(data);
AssetManager assetManager = getAssets();
InputStream is = null;
try {
is = assetManager.open("giveaway_export.json");
} catch (IOException e) {
e.printStackTrace();
}
Gson parser = new GsonBuilder().setDateFormat("dd/MM/yy").setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setLongSerializationPolicy(LongSerializationPolicy.DEFAULT).setLenient().excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.TRANSIENT).create();
Set<Giveaway> temp = new HashSet<Giveaway>(50);
temp.addAll((Collection<? extends Giveaway>) parser.fromJson(new InputStreamReader(is), TypeToken.getParameterized(List.class, Giveaway.class).getType()));
result = new ArrayList<Giveaway>(temp);
} catch (FileNotFoundException e) {
e.printStackTrace();
result = new ArrayList<Giveaway>(1);
}
return result;
}
Remember if you are using android 6.0+ you need to declared the permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
and require manually permissions:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
//Verify permission for Android 6.0+
checkExternalStoragePermission();
}
use this method:
private void checkExternalStoragePermission() {
int permissionCheck = ContextCompat.checkSelfPermission(
this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
Log.i("Message", "You require permissions!.");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 225);
} else {
Log.i("Message", "you have already permissions!");
}
}
Related
I'm working on an app where I'm downloading a PDF file, saving it to internal storage and then opening that file in other app using FileProvider.
Note: It may be a duplicate question, I've gone through most of the questions on StackOverflow, but still didn't find the solution.
The file is getting downloaded fine but when I'm opening it, it is empty.
The downlaoded file is 30 kb and it has 5 pages but all are empty.
Initially, I thought it is empty because the other app doesn't have permission to open the file, but I did another thing to check whether it is a permission issue. I've saved the file to external storage, still, it was empty. So, it means it is not a permission issue.
Please Note:
Along with pdf file, there is some .xls file as well and when I'm opening those in excel android app, it says cannot open the file. This indicates, that there is some issue while writing the byte stream.
Retrofit Interface.java
#GET(ApiConstants.END_POINT_DOWNLOAD_DOCUMENT)
#Streaming
Call<ResponseBody> downloadDocument(#Query("bucket") String bucket, #Query("filename") String fileName);
Code to Download the file: Here I'm checking if a file is already there, then return the file, otherwise download the file.
public LiveData<Resource<File>> openOrDownloadFile(String bucket, String fileName) {
MutableLiveData<Resource<File>> documentLiveData = new MutableLiveData<>();
documentLiveData.postValue(Resource.loading(null));
Context context = MyApp.getInstance();
final File file = new File(context.getFilesDir(), fileName);
if (file.exists()) {
documentLiveData.postValue(Resource.success(file));
} else {
Call<ResponseBody> call = apiService.downloadDocument(bucket, fileName);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
appExecutors.diskIO().execute(new Runnable() {
#Override
public void run() {
try {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
inputStream = response.body().byteStream();
outputStream = new FileOutputStream(file);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
}
documentLiveData.postValue(Resource.success(file));
outputStream.flush();
} catch (IOException e) {
documentLiveData.postValue(Resource.error("Error: Unable to save file/n"+e.getLocalizedMessage(), null));
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
Log.e(AppConstants.TAG, e.getMessage(), e);
documentLiveData.postValue(Resource.error("Error: Unable to save file/n"+e.getLocalizedMessage(), null));
}
}
});
} else {
documentLiveData.postValue(Resource.error("Unable to download file", null));
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
documentLiveData.postValue(Resource.error(t.getLocalizedMessage(), null));
}
});
}
return documentLiveData;
}
Fragment Code
private void onItemClickListener(Document document) {
mDocumentsViewModel.openORDownloadFile(document.getType(), document.getName()).observe(this, new Observer<Resource<File>>() {
#Override
public void onChanged(#Nullable Resource<File> fileResource) {
binding.setResource(fileResource);
if (fileResource.status == Status.SUCCESS) {
openFile(fileResource.data);
}
}
});
}
void openFile(File file) {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID, file);
intent.setDataAndType(uri, mDocumentsViewModel.getMimeType(file.getAbsolutePath()));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
PackageManager pm = getActivity().getPackageManager();
if (intent.resolveActivity(pm) != null) {
startActivity(intent);
} else {
Toast.makeText(getActivity(), "This file cannot be opened on this device. Please download some compatible app from play store", Toast.LENGTH_SHORT).show();
}
}
Following are the versions :
ext.retrofit_version = "2.4.0"
ext.okhttp_version = "3.8.0"
I'm struggling with this issue, it'll be a great help if you can point out the issue. Thank you.
Update: The problem was with the backend APIs. My code was correct. Once they've fixed the problem at there side, it started working at my side without any changes.
My android app on Android-M have the permission declared in Manifest.
android.permission.WRITE_EXTERNAL_STORAGE
My app starts media player which sends control to Jni layer and then to MediaPlayerService and finally to NuPlayer. I understand that my app and NuPlayer are running in two seperate process (http://rxwen.blogspot.in/2010/01/understanding-android-media-framework.html).
Now if i try to create a file in sdcard from native process, suppose from (NuPlayer.cpp) :
FILE *file = fopen("/storage/emulated/0/Test/test.txt", "w+");
file is coming as null and errno is 13 (No permission). So need to know how to give permission to native process to create file on sdcard on Android M.
Thanks in advance for the help.
In Marshmallow you need to ask for permission to user to achieve this try,
1.in your manifest add,
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
2.Create function for write into external storage.
private void createFileExternalStorage() {
MarshMallowPermission marshMallowPermission = new MarshMallowPermission(this);
if (!marshMallowPermission.checkPermissionForExternalStorage())
marshMallowPermission.requestPermissionForExternalStorage();
File backupFile;
File appFolder;
// if SDCard available
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
appFolder = new File(Environment.getExternalStorageDirectory(), getResources().getString(R.string.app_name));
if (!appFolder.exists())
appFolder.mkdir();
Log.d(TAG,"In External");
}else { // if not SDCard available
ContextWrapper cw = new ContextWrapper(this);
appFolder = cw.getDir(getResources().getString(R.string.app_name), Context.MODE_PRIVATE);
if (!appFolder.exists())
appFolder.mkdir();
Log.d(TAG,"In internal");
}
//create a new file, to save the downloaded file
backupFile = new File(appFolder, "backup.txt");
Log.d(TAG, "file #" + backupFile.getAbsolutePath());
try {
FileOutputStream f = new FileOutputStream(backupFile);
PrintWriter pw = new PrintWriter(f);
pw.println("Hi , How are you");
pw.println("Hello");
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i(TAG, "******* File not found. Did you" +
" add a WRITE_EXTERNAL_STORAGE permission to the manifest?");
} catch (IOException e) {
e.printStackTrace();
}
}
3.Create class MarshMallowPermission to get & ask for permission.
public class MarshMallowPermission {
public static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 2;
Activity activity;
public MarshMallowPermission(Activity activity) {
this.activity = activity;
}
public boolean checkPermissionForExternalStorage(){
int result = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (result == PackageManager.PERMISSION_GRANTED){
return true;
} else {
return false;
}
}
public void requestPermissionForExternalStorage(){
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)){
Toast.makeText(activity, "External Storage permission needed. Please allow in App Settings for additional functionality.", Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
}
}
}
In my app i have started download service,it is working fine in background.During download my testing team doing force stop and clear data or Uninstall.But After uninstall or clear data still my Download service is running in background.During download i have installed the same app again but it is misbehaving some thing.While uninstall or clear data or force stop i have to cancel the download How?
public class FileDownloaderService extends IntentService {
private CarcarePreferences preferences;
public FileDownloaderService() {
super("FileDownloaderService");
}
#Override
public void onCreate() {
super.onCreate();
preferences = CarcarePreferences.getCarcarePreferencesObject(getApplicationContext());
DBHelper.getInstance(getApplicationContext()).open();
downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null) {
return;
}
if (extras.containsKey("ResultReceiver")) {
resultReceiver = extras.getParcelable("ResultReceiver");
}
if (extras.containsKey("ContentToDownload")) {
contentToDownload = extras.getInt("ContentToDownload");
} else {
return;
}
if (contentToDownload != Carcare.ContentToDownload.IMAGES) {
isDefaultVehicle = extras.getBoolean("IsDefaultVehicle");
fetchVehicle();
}
switch (contentToDownload) {
case Carcare.ContentToDownload.HEADUNIT_IMAGES:
if (extras.containsKey("HeadUnits")) {
headUnits = (ArrayList<Unit>) extras.getSerializable("Units");
downloadHeadUnits();
resultReceiver.send(0, null);
}
break;
}
}
private void fetchVehicle() {
Object[] objects;
if (isDefaultVehicle) {
objects = DBAdapter.getAllVehicles(preferences.getDefaultModel(),
preferences.getDefaultYear(), isDefaultVehicle);
} else {
objects = DBAdapter.getAllVehicles(preferences.getCurrentModel(),
preferences.getCurrentYear(), isDefaultVehicle);
}
vehicle = (Vehicle) objects[0];
}
private void downloadHeadUnits() {
mHeadUnitDir = SdUtils.getDir(this);
//clearHeadUnits();
for (CUnit unit : Units) {
String fileName = mDir + "/" + unit.getGuid() + ".png";
InputStream stream = null;
final HttpGet httpRequest = new HttpGet(unit.getHuImageUrl());
httpRequest.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE);
try {
File file = new File(fileName);
if (!file.exists()) {
FileOutputStream out = new FileOutputStream(file); //openFileOutput(fileName);
stream = new DefaultHttpClient().execute(httpRequest).getEntity().getContent();
Bitmap bitmap = BitmapFactory.decodeStream(stream);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (IllegalStateException ex) {
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void download() {
cancelDownload(Carcare.FileType.QRG, vehicle.getPath());
deleteDoc(vehicle.getQRGPath());
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(vehicle.getUrl()));
request.setDestinationUri(Uri.parse(vehicle.getPath()));
request.setTitle("Unit");
request.setDescription("Quick Reference Guide");
preferences.setDownloadID(Carcare.FileType.QRG, downloadManager.enqueue(request));
}
}
You must use a Service.
In the Service's onDestroy(), you can write the code to finish the DownloadManager.
The Service will be killed before the app is about to uninstall.
This way the Download will stop.
Take a look at the remove() method of the DownloadManager.
It says:
public int remove (long... ids) Added in API level 9
Cancel downloads and remove them from the download manager. Each
download will be stopped if it was running, and it will no longer be
accessible through the download manager. If there is a downloaded
file, partial or complete, it is deleted. Parameters ids the IDs of
the downloads to remove Returns
the number of downloads actually removed
Edit
To intercept your application uninstall take a look at this answer.
The only way to get and visualize the data table of my database inside external devices is by atribution of privilege of superuser privilege in external device? Don't exist another way that allow visualize the data tables as in emulator?
I make this question because this way of superuser privilege not inspire me security.
Thanks for your attention (PS: Sorry by mistakes, but english is not my mother language :) )
You can add functionality to export the database file from the internal read-only app storage to the SD-Card by simply letting your app copy the file.
Then use whatever ways you have to get it from there. Works on any device and no root required.
private void exportDb() {
File database = getDatabasePath("myDb.db");
File sdCard = new File(Environment.getExternalStorageDirectory(), "myDb.db");
if (copy(database, sdCard)) {
Toast.makeText(this, "Get db from " + sdCard.getPath(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Copying the db failed", Toast.LENGTH_LONG).show();
}
}
private static boolean copy(File src, File target) {
// try creating necessary directories
target.mkdirs();
boolean success = false;
FileOutputStream out = null;
FileInputStream in = null;
try {
out = new FileOutputStream(target);
in = new FileInputStream(src);
byte[] buffer = new byte[8 * 1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
success = true;
} catch (FileNotFoundException e) {
// maybe log
} catch (IOException e) {
// maybe log
} finally {
close(in);
close(out);
}
if (!success) {
// try to delete failed attempts
target.delete();
}
return success;
}
private static void close(final Closeable closeMe) {
if (closeMe != null)
try {
closeMe.close();
} catch (IOException ignored) {
// ignored
}
}
I want to get the state of the system and then show a dialog (toasts at the moment so the code is short) and allow the user to either mount it rw or ro based on the current state.
I used the following code but it didn't work and I'm confused as to why it's not working.
File system = new File("/system");
if(system.canWrite()){
Toast.makeText(Utilities.this, "System is RW", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(Utilities.this, "System is RO", Toast.LENGTH_SHORT).show();
}
How can this be done?
===============================EDIT===============================
Here is the final code after parsing /proc/mounts for future searchers
private boolean readReadWriteFile() {
File mountFile = new File("/proc/mounts");
StringBuilder procData = new StringBuilder();
if(mountFile.exists()) {
try {
FileInputStream fis = new FileInputStream(mountFile.toString());
DataInputStream dis = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(dis));
String data;
while((data = br.readLine()) != null) {
procData.append(data + "\n");
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
if(procData.toString() != null) {
String[] tmp = procData.toString().split("\n");
for(int x = 0; x < tmp.length; x++) {
//Kept simple here on purpose different devices have different blocks
if(tmp[x].contains("/dev/block") && tmp[x].contains("/system")) {
if(tmp[x].contains("rw")) {
Toast.makeText(Activity.this, "System is rw", Toast.LENGTH_LONG).show();
return true;
} else if(tmp[x].contains("ro")) {
Toast.makeText(Activity.this, "System is ro", Toast.LENGTH_LONG).show();
return false;
} else {
return false;
}
}
}
}
}
return false;
}
That's because you query permissions for user. Even if /system is remounted as rw it does not mean your application will get "write" access to it.
As alternative solution, read /proc/mounts file and parse ro/rw status from there.