How to find database path in Android 4.4 (API level: 19)? - android

Trying to read values from the database, that is created in webview using javascript, but unable to get the database path using
getDatabasePath()
Which seems to be deprecated is there is any solution or work around for this.
Thanks in advance

String webviewDBPath = getFilesDir().getParent() + "/"; // getFilesDir().getParent() returns base path of app private data
if (Build.VERSION.SDK_INT <= 18) { // Below kitkat
webviewDBPath += <ur old db folder>;
}
webview.getSettings().setDatabasePath(webviewDBPath);
From kitkat, we can't change webview database path. Default database path is /data/data/{package name}/app_webview/databases/. For below kitkat, we can set database path to any location.
If you want to access database created by websql, then query path of database from Databases.db.
public String getWebViewDBPath() {
if (Build.VERSION.SDK_INT > 18) {
return "app_webview/databases";
} else {
return "{any folder}";
}
}
public String getDBFileName(ZoomRxApp ctx, String dbName) {
String dbFilePath = getFilesDir().getParent() + "/" + getWebViewDBPath() + "/Databases.db";
SQLiteDatabase webSqlDb = SQLiteDatabase.openOrCreateDatabase(dbFilePath, null);
Cursor result = null;
String dbFileName = null;
try {
String query = "select * from Databases where name like '"+dbName+"'";
result = webSqlDb.rawQuery(query, null);
while (result != null && result.moveToNext()) {
String origin = result.getString(result.getColumnIndex("origin"));
if(Build.VERSION.SDK_INT <= 18) {
dbFileName = origin + "/" + result.getString(result.getColumnIndex("path"));
} else {
dbFileName = origin + "/" + result.getString(result.getColumnIndex("id"));
}
break;
}
} catch (RuntimeException e) {
throw e;
} catch (Exception ex) {
ex.printStackTrace();
}
finally {
if(result != null) {
result.close();
}
if(webSqlDb != null) {
webSqlDb.close();
}
}
}

Related

OSMdroid : How to render offline map from a local sqlite archive

Programming with Android Studio and the osmdroid library.
I downloaded a portion of a map using the cacheManager.downloadAreaAsync() method. This method stores the map piece in a sqlite file in the data/data/<package>/osmdroid/tiles directory, chosen by me.
Now I want to use this map to load it offline in a mobile application.
I've tried to do it through all kinds of classes (MapTileSqlCacheProvider, XYTileSource, OfflineTileProvider, ...) but I can't get the map to appear.
How should I do it?
To download a portion of the map I do this:
map.setTileSource(TileSourceFactory.OpenTopo);
outputPath = "/data/data/<package>/files" + File.separator + "osmdroid" + File.separator + "tiles" + File.separator;
outputName = outputPath + boxE6.name + ".db";
try {
writer=new SqliteArchiveTileWriter(outputName);
} catch (Exception ex) {
ex.printStackTrace();
}
CacheManager cacheManager = new CacheManager(map,writer);
cacheManager.downloadAreaAsync(this, boxE6, 7, 13, new CacheManager.CacheManagerCallback() {
#Override
public void onTaskComplete() {
Toast.makeText(ctx, "Download complete!", Toast.LENGTH_LONG).show();
if (writer!=null)
writer.onDetach();
} ...
To retrieve the stored map (in this case it is in the usa.db file) I try to do this:
map.setUseDataConnection(false);
map.setTileSource(TileSourceFactory.OpenTopo);
File cache = new File(outputName);
Configuration.getInstance().setOsmdroidTileCache(cache);
mapController.setCenter(new GeoPoint((n+s)/2,(e+w)/2));
I will show how I store and load multiple sqlite Tiles, not just one.
The above answer from José Espejo Roig worked only partly for me. It worked almost fine for caching the tiles, but not for reading them. Writing down cache files though is also not complete. I have created my own code using as example: Make a tile archive from OSMDroid Github.
So to store potentially more than 1 tiles in a specific directory I use a code like below. It creates sequentially my_mapX.sqlite, where X are just stepped consecutive integers. So I get my_map1.sqlite, my_map2.sqlite and so on.
private final String MAP_FILE_NAME = "my_map";
private final String MAP_FILE_EXTENSION = ".sqlite";
// ...
Context ctx = getActivity();
mMapView = new MapView(ctx);
((ConstraintLayout) view.findViewById(R.id.osm_fragment)).addView(mMapView);
mMapView.setTileSource(TileSourceFactory.OpenTopo);
ContextWrapper contextWrapper = new ContextWrapper(ctx);
File root_directory = contextWrapper.getDir(ctx.getFilesDir().getName(), Context.MODE_PRIVATE);
File directory_osm = new File(root_directory, "osmdroid");
directory_osm.mkdir();
File directory = new File(directory_osm, "tiles");
directory.mkdir();
File[] nrFiles = directory.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
if (name.endsWith(MAP_FILE_EXTENSION))
return true;
return false;
}
});
String osmdroidTile = directory.getAbsolutePath() + File.separator + MAP_FILE_NAME + (nrFiles.length + 1) + MAP_FILE_EXTENSION;
BoundingBox boxE6 = mMapView.getBoundingBox();
SqliteArchiveTileWriter writer = null;
try {
writer = new SqliteArchiveTileWriter(osmdroidTile);
} catch (Exception ex) {
ex.printStackTrace();
}
CacheManager cacheManager = new CacheManager(mMapView, writer);
SqliteArchiveTileWriter finalWriter = writer;
int currZoom = (int)mMapView.getZoomLevelDouble();
cacheManager.downloadAreaAsync(ctx, boxE6, currZoom, currZoom + 1, new CacheManager.CacheManagerCallback() {
#Override
public void onTaskComplete() {
Toast.makeText(ctx, "Download complete!", Toast.LENGTH_LONG).show();
if (finalWriter != null)
finalWriter.onDetach();
}
#Override
public void updateProgress(int progress, int currentZoomLevel, int zoomMin, int zoomMax) {
}
#Override
public void downloadStarted() {
}
#Override
public void setPossibleTilesInArea(int total) {
}
#Override
public void onTaskFailed(int errors) {
Toast.makeText(getActivity(), "Download complete with " + errors + " errors", Toast.LENGTH_LONG).show();
if (finalWriter != null)
finalWriter.onDetach();
}
});
}
});
This way I can create as many tile files as I want. Important is that they have ".sqlite" extension. ".db" extension didn't work for me.
Now to read these tiles I used again example from OSMDroid Github: Sample SQLITE example. In OSMDroid Github example TileSource is being determined with IArchiveFile. I skipped that, as I assume I know what TileSource I used (in my case it is OpenTopo, as you can see). Then to read multiple offline tiles from the same TileSource (basing on example from OSMDroid) my code looks like this:
//first we'll look at the default location for tiles that we support
Context ctx = getActivity();
mMapView = new MapView(ctx);
((ConstraintLayout) view.findViewById(R.id.osm_fragment)).addView(mMapView);
mMapView.setUseDataConnection(false);
ContextWrapper contextWrapper = new ContextWrapper(ctx);
File root_directory = contextWrapper.getDir(ctx.getFilesDir().getName(), Context.MODE_PRIVATE);
String osmDir = root_directory.getAbsolutePath() + File.separator + "osmdroid" + File.separator + "tiles";
File f = new File(osmDir);
if (f.exists()) {
File[] list = f.listFiles();
ArrayList<File> sqliteArray = new ArrayList<>();
if (list != null) {
for (int i = 0; i < list.length; i++) {
if (list[i].isDirectory()) {
continue;
}
String name = list[i].getName().toLowerCase();
if (!name.contains(".")) {
continue; //skip files without an extension
}
name = name.substring(name.lastIndexOf(".") + 1);
if (name.length() == 0) {
continue;
}
//narrow it down to only sqlite tiles
if (ArchiveFileFactory.isFileExtensionRegistered(name) && name.equals("sqlite")) {
sqliteArray.add(list[i]);
}
}
}
OfflineTileProvider tileProvider;
if (sqliteArray.size() > 0) {
try {
tileProvider = new OfflineTileProvider(new SimpleRegisterReceiver(getActivity()), sqliteArray.toArray(new File[0]));
mMapView.setTileProvider(tileProvider);
mMapView.setTileSource(TileSourceFactory.OpenTopo);
mMapView.invalidate();
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
Toast.makeText(getActivity(), f.getAbsolutePath() + " dir not found!", Toast.LENGTH_SHORT).show();
}

need to plot graph from internal storage file

I am storing my data which is coming from hardware device (i.e console), 1st i am creating the file in my device as follows -->>
final String folderName = LOGGING_ROOT_FOLDER;
File folder = new File(folderName);
if (!folder.isDirectory()) {
boolean ret = folder.mkdirs();
if (ret != true) {
return null; // return empty string if fail.
}
}
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.US);
final String fileName = folderName + File.separator + firstName + "-" + patientId + "-" + procedureId + "-"
+ sdf.format(new Date(System.currentTimeMillis())) + ".log";
File f = new File(fileName);
if (!f.exists()) {
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
customLogFile = new BufferedWriter(new FileWriter(f, true));
} catch (IOException e) {
e.printStackTrace();
}
After creating the file i am storing the data in my device as .log with flag (below i have given the code )-->>
if (customLogFile != null) {
StringBuilder sb = new StringBuilder(500);
final String d = mFormatter.format(new Date(System.currentTimeMillis()));
if (pdMeanValue != -1) {
sb.append(pdMeanValue);
}
sb.append("|");
if (aoMean != -1) {
sb.append(aoMean);
}
sb.append("|");
if (aoMap != -1) {
sb.append(aoMap);
}
sb.append("|");
if (ffrValue != -1) {
sb.append(ffrValue);
}
sb.append("|");
if (ffrLowestValue != -1) {
sb.append(ffrLowestValue);
}
sb.append("|");
sb.append(d);
if (mIsRecording) {
sb.append("|1"); // this is the flag
}
if (!canLogFile(sb.toString().getBytes().length)) {
return false;
}
customLogFile.write(sb.toString());
customLogFile.newLine();
So i am getting the data from console continuously and plotting the graph, but now after this i want to fetch the stored file data and wants to plot again in graph but segment wise like one interval of time to another interval (i.e like if flag is 1,then that data i want to fetch from the device and plot the graph, by taking pdMeanValue, aoMean value which will be there in side file as string format). (stored data structure is like [20|30|45|10|12|time will place here|flag])
Please help me to plot the graph by taking the value where the flag is 1.

AndroidTv, Adding channels from an XML issue(samleTvApp)

I'm having this issue with Android TV (sampleApp).
I'm inputting streaming channels from a xml file. I'm creating a temp file to be used at the start, and then I have created a button that does all the necessary functions to acquire data from the server and create the NEW xml file from that data. All of this works, but there's one issues:
After pressing the button and file is created, I try to add channels by pressing "add channels" button, but the file that is used is the temp file, not the NEW xml file. So that it uses the NEW xml file, I have to re-run the setup again and then it works flawlesly. It seems like it caches the temp file in memory or something and uses it first when adding channels, because when the app is launched there is no internal storage file (this is where i save my NEW xml file), the file is created only after the button press.
How do I make it so it uses the NEW xml file instead of the temp file(that created during app launch)?, instead of doing a re-setup
This is the method that is used. Basically, on the first launch it creates an xml with no channels or programs(the temp file) and does what it has to. Then using my other class I create a NEW xml file with all the channels and programs. That also works, the file exists and it goes to the else statement after I press the "add Channels" button. But regardless, on the first try after pressing the button it always adds the temp file, rather than the new one. The new one is only runned, if I launch the setup again.
public static XmlTvParser.TvListing getRichTvListings(Context context) {
context1 = context;
FileOutputStream fos;
try {
Boolean exists = context.getFileStreamPath(FILENAME).exists();
if (exists == false){
String string = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<!DOCTYPE tv SYSTEM \"xmltv.dtd\">\n" +
"\n" +
"<tv>\n" +
"</tv>";
Log.d(TAG,"Exists: FALSE");
fos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
read = "file:" + context.getFilesDir().toString() + "/" + FILENAME ;
Uri catalogUri =Uri.parse(read);
if (sSampleTvListing != null) {
return sSampleTvListing;
}
try (InputStream inputStream = getInputStream(context, catalogUri)) {
sSampleTvListing = XmlTvParser.parse(inputStream);
} catch (IOException e) {
Log.e(TAG, "Error in fetching " + catalogUri, e);
}
}
else{
Log.d(TAG,"Exists: TRUE");
FileInputStream fis = context.openFileInput(FILENAME2);
StringBuilder builder = new StringBuilder();
int inputChar;
while((inputChar = fis.read()) != -1) {
builder.append((char) inputChar);
}
String readFile = builder.toString();
Log.d(TAG, "FileContent: " + readFile);
read = "file:" + context.getFilesDir().toString() + "/" + FILENAME2 ;
Uri catalogUri =Uri.parse(read);
if (sSampleTvListing != null) {
return sSampleTvListing;
}
try (InputStream inputStream = getInputStream(context, catalogUri)) {
sSampleTvListing = XmlTvParser.parse(inputStream);
} catch (IOException e) {
Log.e(TAG, "Error in fetching " + catalogUri, e);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sSampleTvListing;
}
The the button functionalities are in my richSetupFragment class(I wont post all of it, but these are the part that I think are most important in this case):
#Override
protected Boolean doInBackground(Uri... params) {
mTvListing = RichFeedUtil.getRichTvListings(getActivity());
mPoster = fetchPoster();
return true;
}
#Override
public void onActionClicked(Action action) {
if (action.getId() == ACTION_ADD_CHANNELS) {
setupChannels(mInputId);
} else if (action.getId() == ACTION_CANCEL) {
getActivity().finish();
}
else if (action.getId() == RETRIEVE_DATA) {
getChannelsFromServer();
// Log.d(TAG,"List: " + list);
}
private void setupChannels(String inputId) {
inputIdLocal= inputId;
if (mTvListing == null) {
onError(R.string.feed_error_message);
return;
}
TvContractUtils.updateChannels(getActivity(), inputId, mTvListing.channels);
SyncUtils.setUpPeriodicSync(getActivity(), inputId);
SyncUtils.requestSync(inputId, true);
mSyncRequested = true;
// Watch for sync state changes
if (mSyncObserverHandle == null) {
final int mask = ContentResolver.SYNC_OBSERVER_TYPE_PENDING |
ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
mSyncObserverHandle = ContentResolver.addStatusChangeListener(mask,
mSyncStatusObserver);
}
}

How to get SD_Card path in android6.0 programmatically

I am trying to check whether device having external storage or not by using external storage path like this given below
if (new File("/ext_card/").exists()) {
specialPath = "/ext_card/";
} else if (new File("/mnt/sdcard/external_sd/").exists()) {
specialPath = "/mnt/sdcard/external_sd/";
} else if (new File("/storage/extSdCard/").exists()) {
specialPath = "/storage/extSdCard/";
} else if (new File("/mnt/extSdCard/").exists()) {
specialPath = "/mnt/extSdCard/";
} else if (new File("/mnt/sdcard/external_sd/").exists()) {
specialPath = "/mnt/sdcard/external_sd/";
} else if (new File("storage/sdcard1/").exists()) {
specialPath = "storage/sdcard1/";
}
But in marshmallow I con't find this path and while checking using ES FILEMANAGER, they give like storage/3263-3131 in Moto G 3rd generation. While check in other marshmallow devices that numbers getting differ. Please help me to check that marshmallow device have external storage or not? and if storage found means how to get the path of that external storage?
Note:- I gave permission for storage in my application and also enabled storage permission in settings for my app.
Thanks in advance and did you find any mistake in my question please crt it. thank you again.
Here's my solution, which is guaranteed to work till Android 7.0 Nougat:
/* returns external storage paths (directory of external memory card) as array of Strings */
public String[] getExternalStorageDirectories() {
List<String> results = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //Method 1 for KitKat & above
File[] externalDirs = getExternalFilesDirs(null);
String internalRoot = Environment.getExternalStorageDirectory().getAbsolutePath().toLowerCase();
for (File file : externalDirs) {
if(file==null) //solved NPE on some Lollipop devices
continue;
String path = file.getPath().split("/Android")[0];
if(path.toLowerCase().startsWith(internalRoot))
continue;
boolean addPath = false;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
addPath = Environment.isExternalStorageRemovable(file);
}
else{
addPath = Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(file));
}
if(addPath){
results.add(path);
}
}
}
if(results.isEmpty()) { //Method 2 for all versions
// better variation of: http://stackoverflow.com/a/40123073/5002496
String output = "";
try {
final Process process = new ProcessBuilder().command("mount | grep /dev/block/vold")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
output = output + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
if(!output.trim().isEmpty()) {
String devicePoints[] = output.split("\n");
for(String voldPoint: devicePoints) {
results.add(voldPoint.split(" ")[2]);
}
}
}
//Below few lines is to remove paths which may not be external memory card, like OTG (feel free to comment them out)
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
for (int i = 0; i < results.size(); i++) {
if (!results.get(i).toLowerCase().matches(".*[0-9a-f]{4}[-][0-9a-f]{4}")) {
Log.d(LOG_TAG, results.get(i) + " might not be extSDcard");
results.remove(i--);
}
}
} else {
for (int i = 0; i < results.size(); i++) {
if (!results.get(i).toLowerCase().contains("ext") && !results.get(i).toLowerCase().contains("sdcard")) {
Log.d(LOG_TAG, results.get(i)+" might not be extSDcard");
results.remove(i--);
}
}
}
String[] storageDirectories = new String[results.size()];
for(int i=0; i<results.size(); ++i) storageDirectories[i] = results.get(i);
return storageDirectories;
}
I found the solution for this over here https://stackoverflow.com/a/13648873/842607
The code is -
public static HashSet<String> getExternalMounts() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
// parse output
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase(Locale.US).contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase(Locale.US).contains("vold"))
out.add(part);
}
}
}
}
return out;
}
The other one is the hack which I found from the same page -
private static final Pattern DIR_SEPORATOR = Pattern.compile("/");
/**
* Raturns all available SD-Cards in the system (include emulated)
*
* Warning: Hack! Based on Android source code of version 4.3 (API 18)
* Because there is no standart way to get it.
* TODO: Test on future Android versions 4.4+
*
* #return paths to all available SD-Cards in the system (include emulated)
*/
public static String[] getStorageDirectories()
{
// Final set of paths
final Set<String> rv = new HashSet<String>();
// Primary physical SD-CARD (not emulated)
final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
// All Secondary SD-CARDs (all exclude primary) separated by ":"
final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
// Primary emulated SD-CARD
final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
if(TextUtils.isEmpty(rawEmulatedStorageTarget))
{
// Device has physical external storage; use plain paths.
if(TextUtils.isEmpty(rawExternalStorage))
{
// EXTERNAL_STORAGE undefined; falling back to default.
rv.add("/storage/sdcard0");
}
else
{
rv.add(rawExternalStorage);
}
}
else
{
// Device has emulated storage; external storage paths should have
// userId burned into them.
final String rawUserId;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
{
rawUserId = "";
}
else
{
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
final String[] folders = DIR_SEPORATOR.split(path);
final String lastFolder = folders[folders.length - 1];
boolean isDigit = false;
try
{
Integer.valueOf(lastFolder);
isDigit = true;
}
catch(NumberFormatException ignored)
{
}
rawUserId = isDigit ? lastFolder : "";
}
// /storage/emulated/0[1,2,...]
if(TextUtils.isEmpty(rawUserId))
{
rv.add(rawEmulatedStorageTarget);
}
else
{
rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
}
}
// Add all secondary storages
if(!TextUtils.isEmpty(rawSecondaryStoragesStr))
{
// All Secondary SD-CARDs splited into array
final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
Collections.addAll(rv, rawSecondaryStorages);
}
return rv.toArray(new String[rv.size()]);
}
This library solve my problem.
https://github.com/hendrawd/StorageUtil
What i did is:
private File directory;
String[] allPath;
allPath = StorageUtil.getStorageDirectories(this);
for (String path: allPath){
directory = new File(path);
Methods.update_Directory_Files(directory);
}
Methods.update_Directory_Files()
// Retrieving files from memory
public static void update_Directory_Files(File directory) {
//Get all file in storage
File[] fileList = directory.listFiles();
//check storage is empty or not
if(fileList != null && fileList.length > 0)
{
for (int i=0; i<fileList.length; i++)
{
boolean restricted_directory = false;
//check file is directory or other file
if(fileList[i].isDirectory())
{
for (String path : Constant.removePath){
if (path.equals(fileList[i].getPath())) {
restricted_directory = true;
break;
}
}
if (!restricted_directory)
update_Directory_Files(fileList[i]);
}
else
{
String name = fileList[i].getName().toLowerCase();
for (String ext : Constant.videoExtensions){
//Check the type of file
if(name.endsWith(ext))
{
//first getVideoDuration
String videoDuration = Methods.getVideoDuration(fileList[i]);
long playbackPosition;
long percentage = C.TIME_UNSET;
FilesInfo.fileState state;
/*First check video already played or not. If not then state is NEW
* else load playback position and calculate percentage of it and assign it*/
//check it if already exist or not if yes then start from there else start from start position
int existIndex = -1;
for (int j = 0; j < Constant.filesPlaybackHistory.size(); j++) {
String fListName = fileList[i].getName();
String fPlaybackHisName = Constant.filesPlaybackHistory.get(j).getFileName();
if (fListName.equals(fPlaybackHisName)) {
existIndex = j;
break;
}
}
try {
if (existIndex != -1) {
//if true that means file is not new
state = FilesInfo.fileState.NOT_NEW;
//set playbackPercentage not playbackPosition
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(fileList[i].getPath());
String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
retriever.release();
int duration = Integer.parseInt(time);
playbackPosition = Constant.filesPlaybackHistory.get(existIndex).getPlaybackPosition();
if (duration > 0)
percentage = 1000L * playbackPosition / duration;
else
percentage = C.TIME_UNSET;
}
else
state = FilesInfo.fileState.NEW;
//playbackPosition have value in percentage
Constant.allMemoryVideoList.add(new FilesInfo(fileList[i],
directory,videoDuration, state, percentage));
//directory portion
currentDirectory = directory.getPath();
unique_directory = true;
for(int j=0; j<directoryList.size(); j++)
{
if((directoryList.get(j).toString()).equals(currentDirectory)){
unique_directory = false;
}
}
if(unique_directory){
directoryList.add(directory);
}
//When we found extension from videoExtension array we will break it.
break;
}catch (Exception e){
e.printStackTrace();
Constant.allMemoryVideoList.add(new FilesInfo(fileList[i],
directory,videoDuration, FilesInfo.fileState.NOT_NEW, C.TIME_UNSET));
}
}
}
}
}
}
Constant.directoryList = directoryList;
}
in this i have redmi note prime 2.and i have no memory card.so when i found path and File[] externalDirs = getExternalFilesDirs(null); give null second postion value of file[].
}

Stop saving photos using Android native camera

I am using native Android camera and save file to my application data folder (/mnt/sdcard/Android/data/com.company.app/files/Pictures/). At the same time anther copy of photo is saved to DCIM folder.
This is my code:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String formattedImageName = getDateString() + ".jpg";
File image_file = new File(this.getExternalFilesDir(Environment.DIRECTORY_PICTURES), formattedImageName);
Uri imageUri = Uri.fromFile(image_file);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent, REQUEST_FROM_CAMERA);
How can I prevent saving additional copy of image to DCIM folder?
Many Thanks
You can use the following :
First we get the last saved image by checking which was the last modified image. Then check if last modified time is in the last few seconds. You may also have to check the exact location of where camera stores the image.
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
File[] images = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera").listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
// OR JUST Use success = latestSavedImage.delete();
success = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera/"
+ latestSavedImage.getAbsoluteFile()).delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
Unfortunately, some smart phones save images in another folder such as DCIM/100MEDIA. So can't rely to these solution. I prefer use this way:
String[] projection = new String[] {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE};
final Cursor cursor = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,projection, null, null,
MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if(cursor != null){
cursor.moveToFirst();
// you will find the last taken picture here and can delete that
}
I tried to find out if a second copy exists and delete the copy. I used the above code to find the last taken picture.
Notice: Don't use cursor.close(); after using managedQuery, Leave the cursor for the Android system to manage and don't call that. You can see managedQuery()
Notice2: The managedQuery method is deprecated and it should be avoided, implement CursorLoaders instead.
check this code..
private void FillPhotoList() {
// initialize the list!
GalleryList.clear();
String[] projection = { MediaStore.Images.ImageColumns.DISPLAY_NAME };
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","projection "+projection[0].toString());
// intialize the Uri and the Cursor, and the current expected size.
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","FillPhoto Uri u "+u.toString());
// Query the Uri to get the data path. Only if the Uri is valid.
if (u != null)
{
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the id).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Loop each and add to the list.
GalleryList.add(c.getString(0)); // adding all the images sotred in the mobile phone(Internal and SD card)
}
while (c.moveToNext());
}
Log.i(INFOLOG,"gallery size "+ GalleryList.size());
}
and this is where the method is doing all magic
/** Method will check all the photo is the gallery and delete last captured and move it to the required folder.
*/
public void movingCapturedImageFromDCIMtoMerchandising()
{
// This is ##### ridiculous. Some versions of Android save
// to the MediaStore as well. Not sure why! We don't know what
// name Android will give either, so we get to search for this
// manually and remove it.
String[] projection = { MediaStore.Images.ImageColumns.SIZE,
MediaStore.Images.ImageColumns.DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATA,
BaseColumns._ID,};
// intialize the Uri and the Cursor, and the current expected size.
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","on activityresult projection "+projection[i]);
//+" "+projection[1]+" "+projection[2]+" "+projection[3] this will be needed if u remove the for loop
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","on activityresult Uri u "+u.toString());
if (CurrentFile != null)
{
// Query the Uri to get the data path. Only if the Uri is valid,
// and we had a valid size to be searching for.
if ((u != null) && (CurrentFile.length() > 0))
{
//****u is the place from data will come and projection is the specified data what we want
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the size).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Check each area in the gallery we built before.
boolean bFound = false;
for (String sGallery : GalleryList)
{
if (sGallery.equalsIgnoreCase(c.getString(1)))
{
bFound = true;
Log.i("InfoLog","c.getString(1) "+c.getString(1));
break;
}
}
// To here we looped the full gallery.
if (!bFound) //the file which is newly created and it has to be deleted from the gallery
{
// This is the NEW image. If the size is bigger, copy it.
// Then delete it!
File f = new File(c.getString(2));
// Ensure it's there, check size, and delete!
if ((f.exists()) && (CurrentFile.length() < c.getLong(0)) && (CurrentFile.delete()))
{
// Finally we can stop the copy.
try
{
CurrentFile.createNewFile();
FileChannel source = null;
FileChannel destination = null;
try
{
source = new FileInputStream(f).getChannel();
destination = new FileOutputStream(CurrentFile).getChannel();
destination.transferFrom(source, 0, source.size());
}
finally
{
if (source != null)
{
source.close();
}
if (destination != null)
{
destination.close();
}
}
}
catch (IOException e)
{
// Could not copy the file over.
ToastMaker.makeToast(this, "Error Occured", 0);
}
}
//****deleting the file which is in the gallery
Log.i(INFOLOG,"imagePreORNext1 "+imagePreORNext);
Handler handler = new Handler();
//handler.postDelayed(runnable,300);
Log.i(INFOLOG,"imagePreORNext2 "+imagePreORNext);
ContentResolver cr = getContentResolver();
cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=" + c.getString(3), null);
break;
}
}
while (c.moveToNext());
}
}
}
A nice solution by Parth. But it's good for Samsungs that keep images in DCIM/Camera. Some phones - Sony Ericssons, HTCs keep them in folders like DCIM/100MEDIA, DCIM/100ANDRO so I have slightly modified the code:
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
//Samsungs:
File folder = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM/Camera");
if(!folder.exists()){ //other phones:
File[] subfolders = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM").listFiles();
for(File subfolder : subfolders){
if(subfolder.getAbsolutePath().contains("100")){
folder = subfolder;
break;
}
}
if(!folder.exists())
return false;
}
File[] images = folder.listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
success = latestSavedImage.delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
I am encountering a similar problem with the Moto Z Force (7.1.1). I have the MediaStore.EXTRA_OUTPUT defined on the intent, but a duplicate file is still created in the camera directory.
I need to test on other devices, but here's an approach I took regarding this issue. Rather than trying to find the specific camera directory, I'm using the MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME location.
Here's my code snippet:
private void removeCameraDuplicate() {
String[] proj = {
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns._ID };
String selection = MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + " = ? ";
String[] selectionArgs = new String[] { "Camera" };
Cursor cursor = mActivity.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj, selection, selectionArgs, MediaStore.Images.ImageColumns.DATE_TAKEN + " desc");
if (cursor != null) {
int idxPath = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (cursor.getCount() > 0 && idxPath > -1 && cursor.moveToFirst()) {
File original = new File(mMediaPath);
File cameraDupe = new File(cursor.getString(idxPath));
if (original.exists() && cameraDupe.exists()) {
LogUtils.LOGE("***> camera", "original " + original.length());
LogUtils.LOGE("***> camera", "original " + original.lastModified());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.length());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.lastModified());
if (original.length() == cameraDupe.length() && original.lastModified() == cameraDupe.lastModified()) {
if (cameraDupe.delete()) {
LogUtils.LOGE("***> camera", "duplicate deleted");
}
}
}
}
cursor.close();
}
}

Categories

Resources