I wrote my own camera app. This app writes exif information in the jpg files. It works well but I have some problems with the exifInferface class, e.g., I get the following errors when re-read the JPG file:
Warning Invalid EXIF text encoding
Warning Invalid size (8589934590) for IFD0 tag 0x8827
Warning Bad IFD1 directory
I know that my IFD0 pointer at the exif-Information is broken. It may be that while writing the exif information the pointer is broken?
However, I've Googled and found nothing
I use this class to read an write the exif-informations:
public class ExifHelper {
private String aperature = null;
private String exposureTime = null;
private String flash = null;
private String focalLength = null;
private String gpsAltitude = null;
private String gpsAltitudeRef = null;
private String gpsDateStamp = null;
private String gpsLatitude = null;
private String gpsLatitudeRef = null;
private String gpsLongitude = null;
private String gpsLongitudeRef = null;
private String gpsProcessingMethod = null;
private String gpsTimestamp = null;
private String iso = null;
private String make = null;
private String model = null;
private String imageLength = null;
private String imageWidth = null;
private String orientation = null;
private String whiteBalance = null;
private String exifVersion = null;
private String time = null;
private ExifInterface inFile = null;
private ExifInterface outFile = null;
final static String TAG = "ExifHelper";
/**
* The file before it is compressed
*
* #param filePath
* #throws IOException
*/
public void createInFile(String filePath) throws IOException {
this.inFile = new ExifInterface(filePath);
}
/**
* The file after it has been compressed
*
* #param filePath
* #throws IOException
*/
public void createOutFile(String filePath) throws IOException {
this.outFile = new ExifInterface(filePath);
}
/**
* Reads all the EXIF data from the input file.
*/
public void readExifData() {
this.aperature = inFile.getAttribute(ExifInterface.TAG_APERTURE);
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
this.focalLength = inFile.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
this.gpsAltitude = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE);
this.gpsAltitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF);
this.gpsDateStamp = inFile.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
this.gpsLatitude = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
this.gpsLatitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
this.gpsLongitude = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
this.gpsLongitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
this.gpsProcessingMethod = inFile.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);
this.gpsTimestamp = inFile.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
this.imageLength = inFile.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
this.imageWidth = inFile.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
this.iso = inFile.getAttribute(ExifInterface.TAG_ISO);
this.make = inFile.getAttribute(ExifInterface.TAG_MAKE);
this.model = inFile.getAttribute(ExifInterface.TAG_MODEL);
this.orientation = inFile.getAttribute(ExifInterface.TAG_ORIENTATION);
this.whiteBalance = inFile.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
this.exifVersion = inFile.getAttribute("ExifVersion");
}
/**
* Writes the previously stored EXIF data to the output file.
* #param pictureDate
* #param orientationValues
* #param accelValues
*
* #throws IOException
*/
public void writeExifData(String pictureDate) throws IOException {
// Don't try to write to a null file
if (this.outFile == null) {
return;
}
if (this.aperature != null) {
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperature);
}
if (this.exposureTime != null) {
this.outFile.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, this.exposureTime);
}
if (this.flash != null) {
this.outFile.setAttribute(ExifInterface.TAG_FLASH, this.flash);
}
if (this.focalLength != null) {
this.outFile.setAttribute(ExifInterface.TAG_FOCAL_LENGTH, this.focalLength);
}
if (this.gpsAltitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, this.gpsAltitude);
}
if (this.gpsAltitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, this.gpsAltitudeRef);
}
if (this.gpsDateStamp != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, this.gpsDateStamp);
}
if (this.gpsLatitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE, this.gpsLatitude);
}
if (this.gpsLatitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, this.gpsLatitudeRef);
}
if (this.gpsLongitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, this.gpsLongitude);
}
if (this.gpsLongitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, this.gpsLongitudeRef);
}
if (this.gpsProcessingMethod != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, this.gpsProcessingMethod);
}
if (this.gpsTimestamp != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, this.gpsTimestamp);
}
if (this.iso != null) {
this.outFile.setAttribute(ExifInterface.TAG_ISO, this.iso);
}
if (this.make != null) {
this.outFile.setAttribute(ExifInterface.TAG_MAKE, this.make);
}
if (this.model != null) {
this.outFile.setAttribute(ExifInterface.TAG_MODEL, this.model);
}
if (this.orientation != null) {
this.outFile.setAttribute(ExifInterface.TAG_ORIENTATION, this.orientation);
}
if (this.whiteBalance != null) {
this.outFile.setAttribute(ExifInterface.TAG_WHITE_BALANCE, this.whiteBalance);
}
if (this.exifVersion != null) {
this.outFile.setAttribute("ExifVersion", this.exifVersion);
}
this.outFile.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, this.imageLength);
this.outFile.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, this.imageWidth);
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, pictureDate);
String mString = exifRandomZahlen();
this.outFile.setAttribute("UserComment", mString);
this.outFile.saveAttributes();
}
I also write 2 images form the takepicture-method. One the original there is no problem and the second with modify exif informations. There is the problem with the exifinterface I think by writing it back to the JPG.
I used the tool DumpImage to view the exif informations. This tool is from the metaworking group (www.metadataworkinggroup.org)
So I have a big question, How I can fix this broken exif data? For example the IDF0 pointer
somebody know or have the same problem?
I get, for example, the Tag TAG_DATETIME two times in my exif information
this is the class for saveing the photo:
public class Photo extends Activity implements PictureCallback {
public interface OnPictureTakenListener {
void pictureTaken(File pictureFile, File pictureFilePatched, String exifDateString);
}
private final Context context;
private OnPictureTakenListener listener;
public Photo(Context ourContext, OnPictureTakenListener theListener) {
this.context = ourContext;
this.listener = theListener;
}
#SuppressLint("SimpleDateFormat")
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Date date = new Date();
File pictureFileDir = getDir();
if (!pictureFileDir.exists() && !pictureFileDir.mkdirs())
{
Log.d(AndroidCamera.DEBUG_TAG,
"Can't create directory to save image.");
Toast.makeText(context, "Can't create directory to save image.",
Toast.LENGTH_LONG).show();
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
SimpleDateFormat dateConverter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String exifDateString = dateFormat.format(date);
String datePicture = dateConverter.format(date);
String photoFile = "Picture_" + datePicture + ".jpg";
String photoFilePatched = "Picture_" + datePicture + "_patched.jpg";
String filename = pictureFileDir.getPath() + File.separator + photoFile;
String filenamePatched = pictureFileDir.getPath() + File.separator + photoFilePatched;
File pictureFile = new File(filename);
File pictureFilePatched = new File (filenamePatched);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
FileOutputStream fosPatched = new FileOutputStream(pictureFilePatched);
fos.write(data);
fosPatched.write(data);
fos.close();
fosPatched.close();
Toast.makeText(context, "New Image saved:" + photoFile,
Toast.LENGTH_LONG).show();
} catch (Exception error) {
Log.d(AndroidCamera.DEBUG_TAG, "File" + filename + " not saved: "
+ error.getMessage());
Toast.makeText(context, "Image could not be saved.",
Toast.LENGTH_LONG).show();
}
listener.pictureTaken(pictureFile,pictureFilePatched,exifDateString);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
}
private File getDir() {
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
return new File(sdDir, "Camera");
}
static final int REQUEST_IMAGE_CAPTURE = 1;
}
here in main activity i call on one from the two pictures to write the exif infortmations:
camera.takePicture(null, null, new Photo(this,
new Photo.OnPictureTakenListener() {
public void pictureTaken(final File pictureFile,final File pictureFilePatched , final String date) {
final String fileName = pictureFile.getPath();
final String fileNamePatched = pictureFilePatched.getPath();
final String dateTime = date;
// don't start picture preview immediately, but a little
// delayed...
continueWithPreview.postDelayed(new Runnable() {
#Override
public void run() {
try {
// EXIF Matadata change
ExifHelper exifHelper = new ExifHelper();
exifHelper.createInFile(fileName);
//EXIF Metadata read
exifHelper.readExifData();
exifHelper.createOutFile(fileNamePatched);
//Exif Metadata write
exifHelper.writeExifData(dateTime);
} catch (IOException e) {
e.printStackTrace();
Log.e("PictureActivity", e.getLocalizedMessage());
}
if (null != camera)
{
camera.startPreview();
Toast.makeText(AndroidCamera.this, "started!",
Toast.LENGTH_SHORT).show();
}
}
}, 2500);
Related
I am creating an application where i do some real-time image analysis and store them into a csv file. The csv has 2 columns time and y-value of each frame.
I want to read this file and store the values from 2 columns into to double array. I want this because i want to perform an fast Fourier transformation on the data.
public class MainActivity extends AppCompatActivity implements CameraView.PreviewReadyCallback {
private static Camera camera = null;
private CameraView image = null;
private LineChart bp_graph;
private int img_Y_Avg, img_U_Avg, img_V_Avg;
private long end = 0, begin = 0;
double valueY, valueU, valueV;
Handler handler;
private int readingRemaining = 1200;
private static long time1, time2, timeDifference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
bp_graph = (LineChart)findViewById(R.id.graph);
graph_features();
//open camera
try {
camera = Camera.open();
handler = new Handler();
final Runnable runnable = new Runnable() {
#Override
public void run() {
camera.stopPreview();
camera.release();
}
};
handler.postDelayed(runnable, 30000);
} catch (Exception e) {
Log.d("ERROR", "Failed to get camera: " + e.getMessage());
}
if (camera != null) {
image = new CameraView(this, camera);
FrameLayout camera_view = (FrameLayout) findViewById(R.id.camera_view);
camera_view.addView(image);
image.setOnPreviewReady(this);
}
}
#Override
protected void onResume(){
super.onResume();
}
#Override
protected void onPause() {
super.onPause();
}
#Override
public void onPreviewFrame(long startTime, int ySum, int uSum, int vSum, long endTime) {
begin = startTime;
img_Y_Avg = ySum;
img_U_Avg = uSum;
img_V_Avg = vSum;
end = endTime;
showResults(begin, img_Y_Avg, img_U_Avg, img_V_Avg, end);
}
private void showResults(long startTime, int ySum, int uSum, int vSum, long endTime){
//set value of Y on the text view
TextView valueOfY = (TextView)findViewById(R.id.valueY);
//valueY = img_Y_Avg;
valueOfY.setText(String.valueOf(img_Y_Avg));
//start time in milliseconds
long StartDurationInMs = TimeUnit.MILLISECONDS.convert(begin, TimeUnit.MILLISECONDS);
ArrayList<Long> startOfTime = new ArrayList<>();
startOfTime.add(StartDurationInMs);
//store value to array list
ArrayList<Integer> yAverage = new ArrayList<>();
yAverage.add(img_Y_Avg);
//convert to readable format
String readableDate = new SimpleDateFormat("MMM dd,yyyy, HH:mm:ss.SSS").format(EndDurationInMs);
Log.d("Date ", readableDate);
Log.d("time ", String.valueOf(String.valueOf(yAverage.size())));
//store when all array are generated
Log.d("time ", String.valueOf(StartDurationInMs));
ArrayList<Long> getValues = new ArrayList<>();
for(int i = 0; i < yAverage.size(); i++) {
getValues.add(startOfTime.get(i));
getValues.add((long)(yAverage.get(i)));
}
//store the yAverage and start time to csv file
storeCsv(yAverage, getValues);
Log.d("MyEntryData", String.valueOf(getValues));
}
public void storeCsv(ArrayList<Integer>yAverage, ArrayList<Long>getValues){
String filename = "temporary.csv";
//File directoryDownload = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bpReader";
//File logDir = new File (directoryDownload, "bpReader"); //Creates a new folder in DOWNLOAD directory
File logDir = new File(path);
logDir.mkdirs();
File file = new File(logDir, filename);
FileOutputStream outputStream = null;
try {
file.createNewFile();
outputStream = new FileOutputStream(file, true);
//outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
for (int i = 0; i < yAverage.size(); i += 2) {
outputStream.write((getValues.get(i) + ",").getBytes());
outputStream.write((getValues.get(i + 1) + "\n").getBytes());
//outputStream.write((getValues.get(i + 2) + ",").getBytes());
//outputStream.write((getValues.get(i + 3) + "\n").getBytes());
}
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void readCsv(){
}
}
This is my MainActivity. What I am doing here is getting the data from CameraView class for each frame with the help of an interface that I created. After that im storing the values into a CSV file called temporary.csv.
Issues
I want to read this csv and store the first column(the time) into one double array and the second column(yAverage) into another double array.
I also want to delete the file once i have all the data stored into the into the double array.
How can I do that?
I would suggest youto use an open source library like OpenCSV to get the datafrom the CSV file. When you have the library implemented it's only a matter of iterating through the x and y columns and assign them to an array. With OpenCSV it would look like that. But i would also suggest you an more object orientec approach if the x and y with the same index coords are related to each other.
String csvFile = "/Users/mkyong/csv/country3.csv";
int length = 100; //If you dont know how many entries the csv file has i would suggest to use ArrayList
double[] xCoords = new double[length];
double[] yCoords = new double[length];
CSVReader reader = null;
try {
reader = new CSVReader(new FileReader(csvFile));
String[] line;
int i = 0;
while ((line = reader.readNext()) != null) {
xCoords[i] = Double.parseDouble(line[0]);
yCoords[i] = Double.parseDouble(line[1]);
}
} catch (IOException e) {
e.printStackTrace();
}
From the answer given by Lucas, I got the direction to my solution
public void readCsv(){
//set the path to the file
String getPath = Environment.getExternalStorageDirectory() + "/bpReader";
String csvFile = "temporary.csv";
String path = getPath+ "/" + csvFile;
//File file = new File(path, csvFile);
int length = 500;
double[] xCoords = new double[length];
double[] yCoords = new double[length];
CSVReader reader = null;
try {
File myFile = new File (path);
reader = new CSVReader(new FileReader(myFile));
String[] line;
int i = 0;
while ((line = reader.readNext()) != null) {
xCoords[i] = Double.parseDouble(line[0]) ;
yCoords[i] = Double.parseDouble(line[1]);
Log.d("read:: ", "Time: "+String.valueOf(xCoords[i])+" Y: "+String.valueOf(yCoords[i]));
}
myFile.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
And then i had to add
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.6'
to my gradle,, which can be found at MVN repository
I have tried almost all solutions to get list of storage paths including How can I get external SD card path for Android 4.0+?. But nothing returns the removable sd card path.
And finally I tried with "/mnt/". This returns all storage paths including some other paths also. But I want only storage paths. How could I achieve this?. Is there any way to detect whether the given path is storage path, or is there any better way to get all storage paths in Android.
You can use this class for getting all available storage on Android Phone.
This code is taken from stackoverflow.com but contains some error. I remove the error and this code tested in Android 6 and Android 7.
public class StorageUtils {
private static final String TAG = "StorageUtils";
public static class StorageInfo {
public final String path;
public final boolean readonly;
public final boolean removable;
public final int number;
StorageInfo(String path, boolean readonly, boolean removable, int number) {
this.path = path;
this.readonly = readonly;
this.removable = removable;
this.number = number;
}
public String getDisplayName() {
StringBuilder res = new StringBuilder();
if (!removable) {
res.append("Internal Storage");
} else if (number > 1) {
res.append("SD card " + number);
} else {
res.append("SD card");
}
if (readonly) {
res.append(" (Read only)");
}
return res.toString();
}
}
public static List<StorageInfo> getStorageList() {
List<StorageInfo> list = new ArrayList<StorageInfo>();
String def_path = Environment.getExternalStorageDirectory().getPath();
boolean def_path_removable = Environment.isExternalStorageRemovable();
String def_path_state = Environment.getExternalStorageState();
boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
HashSet<String> paths = new HashSet<String>();
int cur_removable_number = 1;
if (def_path_available) {
paths.add(def_path);
list.add(0, new StorageInfo(def_path, def_path_readonly, def_path_removable, def_path_removable ? cur_removable_number++ : -1));
}
BufferedReader buf_reader = null;
try {
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
String line;
while ((line = buf_reader.readLine()) != null) {
if (line.contains("vfat") || line.contains("/mnt") || line.contains("sdcardfs")) {
Log.d(TAG, line);
StringTokenizer tokens = new StringTokenizer(line, " ");
String mPath = tokens.nextToken(); //device
String mount_point = tokens.nextToken(); //mount point
if (paths.contains(mount_point)) {
continue;
}
String unused = tokens.nextToken(); //file system
List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
boolean readonly = flags.contains("ro");
if (!line.contains("/mnt/secure")
&& !line.contains("/mnt/asec")
&& !line.contains("/mnt/obb")
&& !line.contains("/dev/mapper")
&& !line.contains("tmpfs")) {
File testFile = new File(mount_point);
if(testFile.isDirectory() && testFile.listFiles() != null) {
paths.add(mount_point);
list.add(new StorageInfo(mount_point, readonly, true, cur_removable_number++));
}
}
}
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (buf_reader != null) {
try {
buf_reader.close();
} catch (IOException ex) {}
}
}
return list;
}
}
Hope this help.
I want to get External SdCard path on devices if it available. by using Environment.getExternalStorageDirectory().getAbsolutePath() I can get the path to the Internal Storage. So I used below class for detecting External storage.
public class ExternalStorage {
public static final String SD_CARD = "sdCard";
public static final String EXTERNAL_SD_CARD = "externalSdCard";
/**
* #return True if the external storage is available. False otherwise.
*/
public static boolean isAvailable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
public static String getSdCardPath() {
return Environment.getExternalStorageDirectory().getPath() + "/";
}
/**
* #return True if the external storage is writable. False otherwise.
*/
public static boolean isWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/**
* #return A map of all storage locations available
*/
public static Map<String, File> getAllStorageLocations() {
Map<String, File> map = new HashMap<String, File>(10);
List<String> mMounts = new ArrayList<String>(10);
List<String> mVold = new ArrayList<String>(10);
mMounts.add("/mnt/sdcard");
mVold.add("/mnt/sdcard");
try {
File mountFile = new File("/proc/mounts");
if(mountFile.exists()){
Scanner scanner = new Scanner(mountFile);
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (line.startsWith("/dev/block/vold/")) {
String[] lineElements = line.split(" ");
String element = lineElements[1];
// don't add the default mount path
// it's already in the list.
if (!element.equals("/mnt/sdcard"))
mMounts.add(element);
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
try {
File voldFile = new File("/system/etc/vold.fstab");
if(voldFile.exists()){
Scanner scanner = new Scanner(voldFile);
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (line.startsWith("dev_mount")) {
String[] lineElements = line.split(" ");
String element = lineElements[2];
if (element.contains(":"))
element = element.substring(0, element.indexOf(":"));
if (!element.equals("/mnt/sdcard"))
mVold.add(element);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < mMounts.size(); i++) {
String mount = mMounts.get(i);
if (!mVold.contains(mount))
mMounts.remove(i--);
}
mVold.clear();
List<String> mountHash = new ArrayList<String>(10);
for(String mount : mMounts){
File root = new File(mount);
if (root.exists() && root.isDirectory() && root.canWrite()) {
File[] list = root.listFiles();
String hash = "[";
if(list!=null){
for(File f : list){
hash += f.getName().hashCode()+":"+f.length()+", ";
}
}
hash += "]";
if(!mountHash.contains(hash)){
String key = SD_CARD + "_" + map.size();
if (map.size() == 0) {
key = SD_CARD;
} else if (map.size() == 1) {
key = EXTERNAL_SD_CARD;
}
mountHash.add(hash);
map.put(key, root);
}
}
}
mMounts.clear();
if(map.isEmpty()){
map.put(SD_CARD, Environment.getExternalStorageDirectory());
}
return map;
}
Example usage
Map <String, File> externalLocations = ExternalStorage.getAllStorageLocations();
File sdCard = externalLocations.get(ExternalStorage.SD_CARD);
File externalSdCard=externalLocations.get(ExternalStorage.EXTERNAL_SD_CARD);
on some device like Samsung Galaxy S3 it detects External SdCard correctly and return /storage/extSdCard for path to the External SdCard but on other devices like Sony Experia Z1 and Z2 it can't detect External Sdcard and give me the path to the Internal Storage. How can I solve this problem?
Use below code in order to get sd card path
public class DiskHelper
{
public static final int MODE_INTERNAL = 0;
public static final int MODE_EXTERNAL = 1;
public static final int MODE_EXTERNAL_SD = 2;
private StatFs statFs;
protected String path;
public DiskHelper(int mode)
{
try
{
if(mode == 0)
{
path = Environment.getRootDirectory().getAbsolutePath();
statFs = new StatFs(path);
statFs.restat(path);
}
else if(mode == 1)
{
path = Environment.getExternalStorageDirectory().getAbsolutePath();
statFs = new StatFs(path);
statFs.restat(path);
}
else
{
for(String str : getExternalMounts())
{
path = str;
statFs = new StatFs(str);
statFs.restat(str);
break;
}
}
}
catch(Exception e)
{
KLog.error(e);
}
}
public String getPath()
{
return path;
}
public long getTotalMemory()
{
if(statFs == null)
{
return 0;
}
long total = ((long)statFs.getBlockCount() * (long)statFs.getBlockSize());
return total;
}
public long getFreeMemory()
{
if(statFs == null)
{
return 0;
}
long free = ((long)statFs.getAvailableBlocks() * (long)statFs.getBlockSize());
return free;
}
public long getBusyMemory()
{
if(statFs == null)
{
return 0;
}
long total = getTotalMemory();
long free = getFreeMemory();
long busy = total - free;
return busy;
}
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(Exception e)
{
KLog.error(e);
}
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;
}
private static final long MEGABYTE = 1024L * 1024L;
public static String humanReadableByteCount(long bytes, boolean si)
{
if(true)
{
long ret = bytes / MEGABYTE;
return ret + " MB";
}
int unit = si ? 1000 : 1024;
if (bytes < unit) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
}
Then
final DiskHelper sdDiskHelper = new DiskHelper(DiskHelper.MODE_EXTERNAL_SD);
path = sdDiskHelper.getPath();
You can customize this class.
String secStore = System.getenv("SECONDARY_STORAGE");
File externalsdpath = new File(secStore);
This will get the path of external sd secondary storage.
I am using JakeWhartons DiskLruCache and some code from a post in SO. I am able to add the bitmaps with the keys but unable to retrieve it in getBitmap(), instead it returns null. Am I creating a new cache every time? Not sure on this. Here is my code:
AsyncTask:
public BitmapWorkerAsyncTask(ImageView imageView,Context context1) {
imageViewReference = new WeakReference<ImageView>(imageView);
diskLruCache = new DiskLruImageCache(context1, "thumbnails",
(int) (20 * Math.pow(2, 20)), CompressFormat.JPEG, 70);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Object... params) {
// Get the passed arguments here
view = (View) params[0];
songId = (Long) params[1];
albumId = (Long) params[2];
context = (Context) params[3];
data = (Long) params[2];
// Check disk cache
Bitmap bitmap = diskLruCache.getBitmap(String.valueOf(albumId)); // returns null
}
DiskLruImageCache:
public DiskLruImageCache(Context context, String uniqueName,
int diskCacheSize, CompressFormat compressFormat, int quality) {
try {
final File diskCacheDir = getDiskCacheDir(context, uniqueName);
// Log.d(TAG, "file cache dir="+diskCacheDir.getAbsolutePath());
mDiskCache = DiskLruCache.open(diskCacheDir, APP_VERSION,
VALUE_COUNT, diskCacheSize);
mCompressFormat = compressFormat;
mCompressQuality = quality;
} catch (IOException e) {
e.printStackTrace();
}
}
public void put(String key, Bitmap data) {
DiskLruCache.Editor editor = null;
try {
editor = mDiskCache.edit(key);
if (editor == null) {
return;
}
if (writeBitmapToFile(data, editor)) {
mDiskCache.flush();
editor.commit();
if (BuildConfig.DEBUG) {
Log.d("cache_test_DISK_", "image put on disk cache " + key
+ "size aft adding=" + mDiskCache.get(key));
}
}
}
public Bitmap getBitmap(String key) {
Bitmap bitmap = null;
DiskLruCache.Snapshot snapshot = null;
try {
Log.d("cache_test_DISK_", "bitmap got=" + mDiskCache.get(key)
+ " key=" + key);
snapshot = mDiskCache.get(key); // null here
if (snapshot == null) {
return null;
}
}
I was wondering whether it was possible to do such a thing. I know that one would need to modify some of the existing code to pull this off but I was wondering if anyone had any direction on where to look and how to do this.
I am placing a few custom tiles on a specific area on the map as a replacement for OSM tiles providers but need them to be stored in the /assets/ folder. Any ideas?
I use the nexts classes to do that.
import java.io.InputStream;
import org.osmdroid.ResourceProxy.string;
import org.osmdroid.tileprovider.util.StreamUtils;
import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;
public class AssetsTileSource extends CustomBitmapTileSourceBase {
private final AssetManager mAssetManager;
public AssetsTileSource(final AssetManager assetManager, final String aName, final string aResourceId,
final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels,
final String aImageFilenameEnding) {
super(aName, aResourceId, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels, aImageFilenameEnding);
mAssetManager = assetManager;
}
#Override
public Drawable getDrawable(final String aFilePath) {
InputStream inputStream = null;
try {
inputStream = mAssetManager.open(aFilePath);
if (inputStream != null) {
final Drawable drawable = getDrawable(inputStream);
return drawable;
}
} catch (final Throwable e) {
// Tile does not exist in assets folder.
// Ignore silently
} finally {
if (inputStream != null) {
StreamUtils.closeStream(inputStream);
}
}
return null;
}
}
MapTileFileAssetsProvider.java
public class MapTileFileAssetsProvider extends MapTileModuleProviderBase {
protected ITileSource mTileSource;
public MapTileFileAssetsProvider(final ITileSource pTileSource) {
super(OpenStreetMapTileProviderConstants.NUMBER_OF_TILE_FILESYSTEM_THREADS, OpenStreetMapTileProviderConstants.TILE_FILESYSTEM_MAXIMUM_QUEUE_SIZE);
mTileSource = pTileSource;
}
#Override
public boolean getUsesDataConnection() {
return false;
}
#Override
protected String getName() {
return "Assets Folder Provider";
}
#Override
protected String getThreadGroupName() {
return "assetsfolder";
}
#Override
protected Runnable getTileLoader() {
return new TileLoader();
}
#Override
public int getMinimumZoomLevel() {
return mTileSource != null ? mTileSource.getMinimumZoomLevel() : MAXIMUM_ZOOMLEVEL;
}
#Override
public int getMaximumZoomLevel() {
return mTileSource != null ? mTileSource.getMaximumZoomLevel() : MINIMUM_ZOOMLEVEL;
}
#Override
public void setTileSource(final ITileSource pTileSource) {
mTileSource = pTileSource;
}
private class TileLoader extends MapTileModuleProviderBase.TileLoader {
#Override
public Drawable loadTile(final MapTileRequestState pState) throws CantContinueException {
if (mTileSource == null) {
return null;
}
final MapTile pTile = pState.getMapTile();
String path = mTileSource.getTileRelativeFilenameString(pTile);
Drawable drawable;
try {
drawable = mTileSource.getDrawable(path);
} catch (final LowMemoryException e) {
// low memory so empty the queue
throw new CantContinueException(e);
}
return drawable;
}
}
}
And
import java.io.File;
import java.io.InputStream;
import java.util.Random;
import org.osmdroid.ResourceProxy;
import org.osmdroid.ResourceProxy.string;
import org.osmdroid.tileprovider.ExpirableBitmapDrawable;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
public abstract class CustomBitmapTileSourceBase implements ITileSource,
OpenStreetMapTileProviderConstants {
private static final Logger logger = LoggerFactory.getLogger(CustomBitmapTileSourceBase.class);
private static int globalOrdinal = 0;
private final int mMinimumZoomLevel;
private final int mMaximumZoomLevel;
private final int mOrdinal;
protected final String mName;
protected final String mImageFilenameEnding;
protected final Random random = new Random();
private final int mTileSizePixels;
private final string mResourceId;
public CustomBitmapTileSourceBase(final String aName, final string aResourceId,
final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels,
final String aImageFilenameEnding) {
mResourceId = aResourceId;
mOrdinal = globalOrdinal++;
mName = aName;
mMinimumZoomLevel = aZoomMinLevel;
mMaximumZoomLevel = aZoomMaxLevel;
mTileSizePixels = aTileSizePixels;
mImageFilenameEnding = aImageFilenameEnding;
}
#Override
public int ordinal() {
return mOrdinal;
}
#Override
public String name() {
return mName;
}
public String pathBase() {
return mName;
}
public String imageFilenameEnding() {
return mImageFilenameEnding;
}
#Override
public int getMinimumZoomLevel() {
return mMinimumZoomLevel;
}
#Override
public int getMaximumZoomLevel() {
return mMaximumZoomLevel;
}
#Override
public int getTileSizePixels() {
return mTileSizePixels;
}
#Override
public String localizedName(final ResourceProxy proxy) {
return proxy.getString(mResourceId);
}
#Override
public Drawable getDrawable(final String aFilePath) {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
final Bitmap bitmap = BitmapFactory.decodeFile(aFilePath);
if (bitmap != null) {
return new ExpirableBitmapDrawable(bitmap);
} else {
// if we couldn't load it then it's invalid - delete it
try {
new File(aFilePath).delete();
} catch (final Throwable e) {
logger.error("Error deleting invalid file: " + aFilePath, e);
}
}
} catch (final OutOfMemoryError e) {
logger.error("OutOfMemoryError loading bitmap: " + aFilePath);
System.gc();
}
return null;
}
#Override
public String getTileRelativeFilenameString(final MapTile tile) {
final StringBuilder sb = new StringBuilder();
sb.append(pathBase());
sb.append('/');
sb.append(tile.getX());
sb.append('_');
sb.append(tile.getY());
sb.append('_');
sb.append(tile.getZoomLevel());
sb.append(imageFilenameEnding());
return sb.toString();
}
#Override
public Drawable getDrawable(final InputStream aFileInputStream) {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
final Bitmap bitmap = BitmapFactory.decodeStream(aFileInputStream);
if (bitmap != null) {
return new ExpirableBitmapDrawable(bitmap);
}
System.gc();
} catch (final OutOfMemoryError e) {
logger.error("OutOfMemoryError loading bitmap");
System.gc();
//throw new LowMemoryException(e);
}
return null;
}
public final class LowMemoryException extends Exception {
private static final long serialVersionUID = 146526524087765134L;
public LowMemoryException(final String pDetailMessage) {
super(pDetailMessage);
}
public LowMemoryException(final Throwable pThrowable) {
super(pThrowable);
}
}
}
Modify method getTileRelativeFilenameString() to get yout tiles (i use the next format: x_y_zoom.png)
Example:
mapView = new MapView(getApplicationContext(), 256);
mapView.setClickable(true);
mapView.setTag("Mapa");
mapView.setTileSource(TileSourceFactory.MAPNIK);
mapView.setMultiTouchControls(true);
mapView.setUseDataConnection(true);
MapTileModuleProviderBase moduleProvider =
new MapTileFileAssetsProvider(ASSETS_TILE_SOURCE);
SimpleRegisterReceiver simpleReceiver =
new SimpleRegisterReceiver(getApplicationContext());
MapTileProviderArray tileProviderArray =
new MapTileProviderArray(ASSETS_TILE_SOURCE, simpleReceiver,
new MapTileModuleProviderBase[] { moduleProvider });
TilesOverlay tilesOverlay =
new TilesOverlay(tileProviderArray, getApplicationContext());
mapView.getOverlays().add(tilesOverlay);
Instead to read directly from assets I copy/deploy the maptiles zipped (following osmdroid map tiles directory structure format) into osmdroid maptiles directory and then declare 3 tile providers, archive, cache and online provider.
public class MapTileProviderAssets extends MapTileProviderArray
implements IMapTileProviderCallback {
private static final String LOG_TAG = "MapTileProviderAssets";
private static final String ASSETS_MAP_DIRECTORY = "map";
private static final String SDCARD_PATH = Environment.getExternalStorageDirectory().getPath();
private static final String OSMDROID_MAP_FILE_SOURCE_DIRECTORY = "osmdroid";
private static final String OSMDROID_MAP_FILE_SOURCE_DIRECTORY_PATH =
SDCARD_PATH + "/" + OSMDROID_MAP_FILE_SOURCE_DIRECTORY;
public MapTileProviderAssets(final Context pContext) {
this(pContext, TileSourceFactory.DEFAULT_TILE_SOURCE);
}
public MapTileProviderAssets(final Context pContext, final ITileSource pTileSource) {
this(pContext, new SimpleRegisterReceiver(pContext),
new NetworkAvailabliltyCheck(pContext), pTileSource);
}
public MapTileProviderAssets(final Context pContext, final IRegisterReceiver pRegisterReceiver,
final INetworkAvailablityCheck aNetworkAvailablityCheck,
final ITileSource pTileSource) {
super(pTileSource, pRegisterReceiver);
final TileWriter tileWriter = new TileWriter();
// copy assets delivered in apk into osmdroid map source dir
// load zip archive first, then cache, then online
final List<String> zipArchivesRelativePathInAssets =
listArchives(pContext.getAssets(), ASSETS_MAP_DIRECTORY);
for (final String zipFileRelativePathInAssets : zipArchivesRelativePathInAssets) {
final String copiedFilePath = copyAssetFile(
pContext.getAssets(), zipFileRelativePathInAssets,
OSMDROID_MAP_FILE_SOURCE_DIRECTORY);
Log.d(LOG_TAG, String.format(
"Archive zip file copied into map source directory %s", copiedFilePath));
}
// list zip files in map archive directory
final Set<String> setZipFileArchivesPath = new HashSet<String>();
FileTools.listFiles(setZipFileArchivesPath, new File(
OSMDROID_MAP_FILE_SOURCE_DIRECTORY_PATH), ".zip", true);
final Set<IArchiveFile> setZipFileArchives = new HashSet<IArchiveFile>();
for (final String zipFileArchivesPath : setZipFileArchivesPath) {
final File zipfile = new File(zipFileArchivesPath);
final IArchiveFile archiveFile = ArchiveFileFactory.getArchiveFile(zipfile);
if (archiveFile != null) {
setZipFileArchives.add(archiveFile);
}
setZipFileArchives.add(archiveFile);
Log.d(LOG_TAG, String.format(
"Archive zip file %s added to map source ", zipFileArchivesPath));
}
final MapTileFileArchiveProvider archiveProvider;
Log.d(LOG_TAG, String.format(
"%s archive zip files will be used as source", setZipFileArchives.size()));
if (setZipFileArchives.size() > 0) {
final IArchiveFile[] pArchives =
setZipFileArchives.toArray(new IArchiveFile[setZipFileArchives.size()]);
archiveProvider = new MapTileFileArchiveProvider(
pRegisterReceiver, pTileSource, pArchives);
} else {
archiveProvider = new MapTileFileArchiveProvider(
pRegisterReceiver, pTileSource);
}
mTileProviderList.add(archiveProvider);
// cache
final MapTileFilesystemProvider fileSystemProvider =
new MapTileFilesystemProvider(pRegisterReceiver, pTileSource);
mTileProviderList.add(fileSystemProvider);
// online tiles
final MapTileDownloader downloaderProvider =
new MapTileDownloader(pTileSource, tileWriter, aNetworkAvailablityCheck);
mTileProviderList.add(downloaderProvider);
}
public static List<String> listArchives(final AssetManager assetManager,
final String subDirectory) {
final List<String> listArchives = new ArrayList<String>();
try {
final String[] lstFiles = assetManager.list(subDirectory);
if (lstFiles != null && lstFiles.length > 0) {
for (final String file : lstFiles) {
if (isZip(file)) {
listArchives.add(subDirectory + "/" + file);
}
// filter files (xxxxx.xxx format) and parse only directories,
// with out this all files are parsed and
// the process is VERY slow
// WARNNING: we could have directories with dot for versioning
else if (isDirectory(file)) {// (file.lastIndexOf(".") != (file.length() - 4)) {
listArchives(assetManager, subDirectory + "/" + file);
}
}
}
} catch (final IOException e) {
Log.w(LOG_TAG, String.format("List error: can't list %s, exception %s",
subDirectory, Log.getStackTraceString(e)));
} catch (final Exception e) {
Log.w(LOG_TAG, String.format("List error: can't list %s, exception %s",
subDirectory, Log.getStackTraceString(e)));
}
return listArchives;
}
private static boolean isZip(final String file) {
return file.endsWith(".zip");
}
private static boolean isDirectory(final String file) {
return file.lastIndexOf(".") != (file.length() - 4);
}
private static String copyAssetFile(final AssetManager assetManager,
final String assetRelativePath,
final String destinationDirectoryOnSdcard) {
InputStream in = null;
OutputStream out = null;
final String newfilePath = SDCARD_PATH + "/" +
destinationDirectoryOnSdcard + "/" + assetRelativePath;
final File newFile = new File(newfilePath);
// copy file only if it doesn't exist yet
if (!newFile.exists()) {
Log.d(LOG_TAG, String.format(
"Copy %s map archive in assets into %s", assetRelativePath, newfilePath));
try {
final File directory = newFile.getParentFile();
if (!directory.exists()) {
if (directory.mkdirs()) {
// Log.d(LOG_TAG, "Directory created: " + directory.getAbsolutePath());
}
}
in = assetManager.open(assetRelativePath);
out = new FileOutputStream(newfilePath);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (final Exception e) {
Log.e(LOG_TAG, "Exception during copyAssetFile: " + Log.getStackTraceString(e));
}
}
return newfilePath;
}
private static void copyFile(final InputStream in, final OutputStream out) throws IOException {
final byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
}