I'm trying to update my app to work with androids new scoped storage rules in Android 10 and up, but am having the hardest time with it. I know I need to rebuild my app with new versions of java, but I just want to get it to work while I study and learn enough to do so. In a nutshell, I really need help. I have read so many different ways to make scoped storage work, and everybody seems to be doing it differently.
Just for clarification, what I am trying to do with the uri is both display in an imageview, then upload to database.
This code is working to take a picture and select images and videos in android 9, but in android 10, it only works when camera component captures a picture or a video. When a user selects an image or video from file, it returns a null pointer exception. Because I am pretty sure the error is in how I am dealing with the different chooser intents, I have shown the on result code first.
I have been unable to find a clear example of how to retrieve a usable image or video uri in android 10. If anybody can help, I would really appreciate it. I know I have much to learn.
if ((new java.io.File(_filePath)).exists()){
} else {
_filePath = vidfile.getAbsolutePath();
if ((new java.io.File(_filePath)).exists()){
} else {
ArrayList<String> _filePath_1 = new ArrayList<>();
if (_data != null) {
if (_data.getClipData() != null) {
for (int _index = 0; _index < _data.getClipData().getItemCount(); _index++) {
ClipData.Item _item = _data.getClipData().getItemAt(_index);
_filePath_1.add(FileUtil.convertUriToFilePath(getApplicationContext(),
_item.getUri()));
}
}
else {
_filePath_1.add(FileUtil.convertUriToFilePath(getApplicationContext(),
_data.getData()));
}
}
_filePath = _filePath_1.get((int)0);
}
}
Just in case I am wrong, here is the code for the click event to launch the chooser...
SimpleDateFormat date1 = new SimpleDateFormat("yyyyMMdd_HHmmss");
String fileName1 = date1.format(new Date()) + ".jpg";
picfile = new
File(getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath() +
File.separator + fileName1);
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri _uri_camr1 = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
_uri_camr1 = FileProvider.getUriForFile(getApplicationContext(),
getApplicationContext().getPackageName() + ".provider", picfile);
}
else {
_uri_camr1 = Uri.fromFile(picfile);
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, _uri_camr1);
takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
SimpleDateFormat date2 = new SimpleDateFormat("yyyyMMdd_HHmmss");
String fileName2 = date2.format(new Date()) + ".mp4";
vidfile = new
File(getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath() +
File.separator + fileName2);
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
Uri _uri_camr2 = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
_uri_camr2 = FileProvider.getUriForFile(getApplicationContext(),
getApplicationContext().getPackageName() + ".provider", vidfile);
}
else {
_uri_camr2 = Uri.fromFile(vidfile);
}
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, _uri_camr2);
takeVideoIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
contentSelectionIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Intent[] intentArray = new Intent[]{ takePictureIntent, takeVideoIntent};
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Choose an action");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, REQ_CD_CAMR);
try this code. it copies the selected file to scoped storage and gives you the final path of scoped storage from where you can access it. try it out & let me know if you face any problem.
android.net.Uri sharedFileUri = android.net.Uri.fromFile(new java.io.File(_filepath));
java.io.FileInputStream input = null;
java.io.FileOutputStream output = null;
try {
String filePath = new java.io.File(getCacheDir(), "tmp").getAbsolutePath();
android.os.ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(sharedFileUri, "rw");
if (pfd != null) {
java.io.FileDescriptor fd = pfd.getFileDescriptor();
input = new java.io.FileInputStream (fd);
output = new java.io.FileOutputStream (filePath);
int read;
byte[] bytes = new byte[4096];
while ((read = input.read(bytes)) != -1) {
output.write(bytes, 0, read);
}
java.io.File sharedFile = new java.io.File(filePath);
String finalPath = sharedFile.getPath(); // this will provide you path to scoped storage. use this final path to access the selected file from scoped storage.
}
}catch(Exception ex) {
android.widget.Toast.makeText(this, ex.toString(), android.widget.Toast.LENGTH_SHORT).show();
} finally {
try {
input.close();
output.close();
} catch (Exception ignored) {
}
}
Related
I am trying to read the text from a QRcode image on my mobile app. I am using Xamarin.Forms with ZXing NuGet package.
I have been able to get the file using Xamarin.Essentials FilePicker. But I don't know how to actually read the barcode. I have looked at some stackoverflow solutions and they all seem to be Xamarin.Android based (using BinaryBitmap objects). I need a solution that can work for iOS and UWP as well. Here is what I have so far:
string file = "";
var filePickerOptions = new PickOptions
{
PickerTitle = "Select Barcode Image",
FileTypes = FilePickerFileType.Images
};
var result = await FilePicker.PickAsync(filePickerOptions);
if (result != null)
{
file = result.FullPath;
var res = Decode(file, BarcodeFormat.QR_CODE);
Console.WriteLine(res.Text);
}
public Result Decode(string file, BarcodeFormat? format = null, KeyValuePair<DecodeHintType, object>[] aditionalHints = null)
{
var r = GetReader(format, aditionalHints);
/* I need some function here that will allow me to get the BinaryBitmap from the image file path or something along those lines.*/
var image = GetBinaryBitmap(file);
var result = r.decode(image);
return result;
}
MultiFormatReader GetReader(BarcodeFormat? format, KeyValuePair<DecodeHintType, object>[] aditionalHints)
{
var reader = new MultiFormatReader();
var hints = new Dictionary<DecodeHintType, object>();
if (format.HasValue)
{
hints.Add(DecodeHintType.POSSIBLE_FORMATS, new List<BarcodeFormat>() { format.Value });
}
if (aditionalHints != null)
{
foreach (var ah in aditionalHints)
{
hints.Add(ah.Key, ah.Value);
}
}
reader.Hints = hints;
return reader;
}
https://github.com/Redth/ZXing.Net.Mobile/issues/981. This thread solved it for me. Credit to #jason for this response.
I am working on Encryption,Decryption in swift OpenSSl AES-256-CBC. I have checked with many third- party libraries or pods i.e. CryptoSwift and many others. But I am always getting HMAc is Not valid from Php back end team.
Where as in android they have done this:
Following is my android method:
public EncryptedData encrypt(Object data) throws Exception {
String text;
if (data instanceof String) {
text = String.valueOf(data);
} else {
text = (new Gson()).toJson(data);
}
if (!this.doAction) {
return new EncryptedData(text, "");
} else {
this.ivspec = new IvParameterSpec(this.getIV1().getBytes());
this.keyspec = new SecretKeySpec(this.getKey1().getBytes(), "AES");
if (text != null && text.length() != 0) {
byte[] encrypted;
try {
this.cipher.init(Cipher.ENCRYPT_MODE, this.keyspec, this.ivspec);
encrypted = this.cipher.doFinal(this.padString(text).getBytes());
} catch (Exception var5) {
throw new Exception("[encrypt] " + var5.getMessage());
}
String encryptedData = new String(Base64.encode(encrypted, Base64.DEFAULT))
.replace("\n", "");
SecretKeySpec macKey = new SecretKeySpec(getKey1().getBytes(), "HmacSHA256");
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
hmacSha256.init(macKey);
hmacSha256.update((Base64.encodeToString(getIV1().getBytes(), Base64.DEFAULT).trim() + encryptedData.trim()).getBytes());
byte[] calcMac = hmacSha256.doFinal();
return new EncryptedData(encryptedData, bytesToHex(calcMac));
} else {
throw new Exception("Empty string");
}
}
}
Any one know how this will works in iOS.
Any help will be appreciated.
Thanks
Here is a simple HMAC implement in Swift 4:
0xa6a/HMAC
No third-party library is needed. Just create a bridging header and import <CommonCrypto/CommonCrypto.h> in it.
Have a try and happy coding.
I am trying to connect an SQLite database file to a picker component (accepting strings). This should act similar to a drop-down menu. I have tried to follow previous advice and examples, but without success.
As indicated in a previous post, I have saved the database file in the source folder of the application. View of the source folder where I have saved the database file (highlighted).
The code I have used to implement my app is as follows with the below layout.
//-----------------------
database code
//-----------------------
public class MyApplication {
private Form current;
private Resources theme;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Pro only feature, uncomment if you have a pro subscription
// Log.bindCrashProtection(true);
}
private Container Home() {
Container home = new Container(new BoxLayout(BoxLayout.Y_AXIS));
return home;
}
private Container AddItem() {
Container addItem = new Container(new BoxLayout(BoxLayout.Y_AXIS));
TextArea item = new TextArea("Add Item");
addItem.addComponent(item);
Picker selectItem = new Picker();
selectItem.setType(Display.PICKER_TYPE_STRINGS);
//----------------------------------------------------------------------------------
Database db = null;
Cursor cur = null;
try {
db = Display.getInstance().openOrCreate("FoodAndBeverage.db");
if(selectItem.getText().startsWith("Still Water")) {
cur = db.executeQuery(selectItem.getText());
int columns = cur.getColumnCount();
addItem.removeAll();
if(columns > 0) {
boolean next = cur.next();
if(next) {
ArrayList<String[]> data = new ArrayList<>();
String[] columnNames = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
columnNames[iter] = cur.getColumnName(iter);
}
while(next) {
Row currentRow = cur.getRow();
String[] currentRowArray = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
currentRowArray[iter] = currentRow.getString(iter);
}
data.add(currentRowArray);
next = cur.next();
}
Object[][] arr = new Object[data.size()][];
data.toArray(arr);
addItem.add(BorderLayout.CENTER, new Table(new DefaultTableModel(columnNames, arr)));
} else {
addItem.add(BorderLayout.CENTER, "Query returned no results");
}
} else {
addItem.add(BorderLayout.CENTER, "Query returned no results");
}
} else {
db.execute(selectItem.getText());
addItem.add(BorderLayout.CENTER, "Query completed successfully");
}
addItem.revalidate();
} catch(IOException err) {
Log.e(err);
addItem.removeAll();
addItem.add(BorderLayout.CENTER, "Error: " + err);
addItem.revalidate();
} finally {
Util.cleanup(db);
Util.cleanup(cur);
}
//---------------------------------------------------------------------------------------------
addItem.addComponent(selectItem);
TextField quantity = new TextField("", "Quantity (ml or g)", 4, TextArea.NUMERIC);
addItem.addComponent(quantity);
Button add = new Button("Add");
addItem.addComponent(add);
TextArea results = new TextArea("Results");
addItem.addComponent(results);
return addItem;
}
private Container Settings() {
Container settings = new Container(new BoxLayout(BoxLayout.Y_AXIS));
TextArea nutrients = new TextArea("Target");
settings.addComponent(nutrients);
TextField volume = new TextField("", "Volume (ml)", 4, TextArea.NUMERIC);
settings.addComponent(volume);
TextArea duration = new TextArea("Hydration Duration");
settings.addComponent(duration);
settings.add("Start:");
Picker start = new Picker();
start.setType(Display.PICKER_TYPE_TIME);
settings.addComponent(start);
settings.add("End:");
Picker end = new Picker();
end.setType(Display.PICKER_TYPE_TIME);
settings.addComponent(end);
Button save = new Button("Save");
settings.addComponent(save);
return settings;
}
public void start() {
if(current != null)
{
current.show();
return;
}
Form home = new Form("Hydrate", new BorderLayout());
Tabs t = new Tabs();
t.addTab("Home", Home());
t.addTab("Intake", AddItem());
t.addTab("Settings", Settings());
home.add(BorderLayout.NORTH, t);
home.show();
}
public void stop() {
current = Display.getInstance().getCurrent();
}
public void destroy() {
}
}
I would therefore appreciate any advice and guidance on exactly where I am going wrong and how to implement the suggested changes in my code.
I'm assuming the file under src does indeed end with the extension db as the Windows hidden extensions nonsense is turned on.
This code will NOT open a db placed in src:
db = Display.getInstance().openOrCreate("FoodAndBeverage.db");
You need to do something like this to implicitly initialize the DB the first time the app is installed:
String path = Display.getInstance().getDatabasePath("FoodAndBeverage.db");
FileSystemStorage fs = FileSystemStorage.getInstance();
if(!fs.exists(path)) {
try (InputStream is = Display.getInstance().getResourceAsStream(getClass(), "/FoodAndBeverage.db");
OutputStream os = fs.openOutputStream(path)) {
Util.copy(is, os);
} catch(IOException err) {
Log.e(err);
}
}
db = Display.getInstance().openOrCreate("FoodAndBeverage.db");
Notice that the code above doesn't check for updates of the DB so assuming the DB is read only you might want to update/merge it with app updates.
The above code doesn't work on Android device, this works only on simulator. I have tested multiple times in the android device. In the real android device ,the database is not loaded at all, shows sql exception error
"No such table sql exception".
Looks like preloaded sqlite .db file is never tested on real Android device.
I'm using Google Maps v3 API to load a KML layer and want to be able to change the colour of the KML from its default blue without having to edit the KML file itself. Is this possible using JavaScript or some other means?
Unfortunately can't post a link, but it's pretty standard stuff.
var map = new google.maps.Map(document.getElementById("mymap"), { some options });
var regionLayer = new google.maps.KmlLayer("http://.../some.kml");
regionLayer.setMap(map);
From my understanding of the documentation, 'no', but it's not especially clear. I'm trying to do a similar thing (but update the colour of mouseover/mouseout).
The KML file is loaded by the Google servers, parsed and sent along to your javascript object to be applied to the map, so, by the time your javascript KMLLayer sees it, it's all sorted out.
You may be able to do something with Styles and styleUrl. This is supposed to allow you to set a number of different styles that can then be applied at runtime, however, I haven't got it working.
I have done this by creating a web service which reads in a KML file to a string, inserts a style section to the beginning of the KML string, and also a styleURL to each uniquely named placemark. It's fairly simple to amend the markup using a .net web service and write it back out to the server you have hosting the web service.
For example this uses a class which contains placemark IDs and a colour flag:
public string KMLStyler(string URL, string URLName, Data[] MyData)
{
try
{
ReadFile(URL);
string NewKML = ReadFile(URL);
string RedStyle = "<Style id=\"red\"><LineStyle><color>7F7F7F7F</color><width>2</width></LineStyle><PolyStyle><color>7F0000FF</color><fill>1</fill><outline>1</outline></PolyStyle></Style>";
string BlackStyle = "<Style id=\"black\"><LineStyle><color>7F7F7F7F</color><width>2</width></LineStyle><PolyStyle><color>7F7F7F7F</color><fill>1</fill><outline>1</outline></PolyStyle></Style>";
string GreenStyle = "<Style id=\"green\"><LineStyle><color>7F7F7F7F</color><width>2</width></LineStyle><PolyStyle><color>7F00FF00</color><fill>1</fill><outline>1</outline></PolyStyle></Style>";
string BlueStyle = "<Style id=\"blue\"><LineStyle><color>7F7F7F7F</color><width>2</width></LineStyle><PolyStyle><color>7F7F7F7F</color><fill>1</fill><outline>1</outline></PolyStyle></Style>";
//add styles to top
int EndID = 0;
EndID = NewKML.IndexOf("</name>") + 7;
NewKML = NewKML.Insert(EndID, RedStyle);
EndID = NewKML.IndexOf("</name>") + 7;
NewKML = NewKML.Insert(EndID, BlackStyle);
EndID = NewKML.IndexOf("</name>") + 7;
NewKML = NewKML.Insert(EndID, GreenStyle);
EndID = NewKML.IndexOf("</name>") + 7;
NewKML = NewKML.Insert(EndID, BlueStyle);
//add each style to each placemark
foreach (Data MyDataSingle in MyData)
{
int NamePos = NewKML.IndexOf(MyDataSingle.Name);
if (NamePos == -1) throw new Exception("Did not find '" + MyDataSingle.Name + "' within File");
NamePos += MyDataSingle.Name.Length + 7;
int MultiGeometryStartPos = NewKML.IndexOf("<MultiGeometry>", NamePos);
int MultiGeometryEndPos = NewKML.IndexOf("</MultiGeometry>", NamePos);
int PolygonStartPos = NewKML.IndexOf("<Polygon>", NamePos);
int InsertPos = 0;
if (MultiGeometryStartPos < PolygonStartPos)
{
if (MultiGeometryStartPos != -1)
{
InsertPos = MultiGeometryStartPos;
}
else
{
InsertPos = PolygonStartPos;
}
}
else
{
InsertPos = PolygonStartPos;
}
if (MyDataSingle.Red)
{
NewKML = NewKML.Insert(InsertPos, "<styleUrl>#red</styleUrl>");
}
if (MyDataSingle.Black)
{
NewKML = NewKML.Insert(InsertPos, "<styleUrl>#black</styleUrl>");
}
if (MyDataSingle.Green)
{
NewKML = NewKML.Insert(InsertPos, "<styleUrl>#green</styleUrl>");
}
if (MyDataSingle.Blue)
{
NewKML = NewKML.Insert(InsertPos, "<styleUrl>#blue</styleUrl>");
}
}
string NewFileName = WriteFile(NewKML, URLName);
return NewFileName;
}
catch (Exception ex)
{
return ex.ToString();
}
}
public string WriteFile(string KMLData, string URLName)
{
string FileName = "http:\\blah.co.uk\blah.kml";
StreamWriter writer = new StreamWriter("C:/inetpub/blah.kml");
writer.Write(KMLData);
writer.Flush();
writer.Close();
return FileName;
}
public string ReadFile(string URL)
{
string File = "";
StreamReader reader = new StreamReader(WebRequest.Create(URL).GetResponse().GetResponseStream());
string line;
while ((line = reader.ReadLine()) != null)
{
File += line;
}
return File;
}
For some reason, not all of Excel-documents can be upload from my computer. In half the cases get the error ".. no!! error:" from a block of try-catch .. What is wrong?
private function importXLS(e:MouseEvent):void {
fr = new FileReference();
var fileFilter:FileFilter = new FileFilter("Excel (.xls)", "*.xls");
fr.addEventListener(Event.SELECT,selectXLS);
fr.browse([fileFilter]);
statusLabel.text = "selecting...";
}
private function selectXLS(e:Event):void {
fr = FileReference(e.target);
fr.addEventListener(Event.COMPLETE, fileIn);
fr.load();
statusLabel.text = "loading...";
}
private function fileIn(e:Event):void {
ba = new ByteArray();
ba = fr.data;
xls = new ExcelFile();
var flag:Boolean = false;
try{
xls.loadFromByteArray(ba);
flag = true;
}catch(error:Error){
Alert.show("no!! error: " + error.getStackTrace());
}
if (flag == true) {
statusLabel.text = "XlS loaded.";
} else {
statusLabel.text = "XlS didn't load.";
}
}
You are reading the entire file into memory. If a user tries to upload too big a file, their browser will crash. Is there a reason you doing this? Are you using the bytes in the client or just passing them to the server. If you are passing them to the server, you want to just not use the fr.load method that you call in selectXLS(). Instead use fr.upload and avoid your whole problem.