I am using a file chooser to pick a WORD file downloaded from GMail, it causes the app crashed.Here's my code segment:
== file chooser code ==
Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
intent.addCategory(Intent.CATEGORY_OPENABLE);
//sets the select file to all types of files
String [] mimeTypes = {"application/pdf", "application/msword",
"application/vnd.openxmlformats
officedocument.wordprocessingml.document"};
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
activity.startActivityForResult(Intent.createChooser(intent, "Select
File"), PICK_FILE_REQUEST);
== onActivityResult ==
Uri selectedFileUri = data.getData();
String selectedFilePath = FilePath.getPath(getActivity(),
selectedFileUri);
== FilePath.getPath() ==
...
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri =
ContentUris.withAppendedId(Uri.parse("content://downloads
/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
== getDataColumn() ==
public static String getDataColumn(Context context, Uri uri,
String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
The selectedFilePath has this value: content://com.android.providers.downloads.documents/document/164, the contentUri in FilePath.getPath() also has this value. When it goes in getDataColumn() method, the cursor is null after executing "query()".
Tried those: 1) If I put the same WORD file direct to the "Download" folder and pick it from "Downloads" link from the file chooser, I have no issue. It seems that somehow going through GMail and downloading from GMail causes problem. 2) File still downloaded from GMail and in Download folder, if I pick it through Internal storage->Download (i.e, absolute path), it works since the code goes through different flow (which is not shown above).
I am wondering where I missed in the code to handle the file downloaded from GMail?
Thanks in advance!
The Phone is Galaxy S9, android 9.
After some search and experiment, it ends up retrieving file name and content from InputStream instead of trying to get the path of the file. Here's the code segment:
1) get file details:
Uri selectedFileUri = data.getData();
FileDetail fileDetail = FilePath.getFileDetailFromUri(getActivity(), selectedFileUri);
String fileName = fileDetail.getFileName();
int fileSize = (int)fileDetail.getFileSize();
Here's the FileDetail.java:
public class FileDetail {
// fileSize.
public String fileName;
// fileSize in bytes.
public long fileSize;
public FileDetail() {
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
}
In FilePath.java:
public static FileDetail getFileDetailFromUri(Context context, Uri uri) {
FileDetail fileDetail = null;
if (uri != null) {
fileDetail = new FileDetail();
// File Scheme.
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
File file = new File(uri.getPath());
fileDetail.setFileName(file.getName());
fileDetail.setFileSize(file.length());
}
// Content Scheme.
else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
if (returnCursor != null && returnCursor.moveToFirst()) {
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
fileDetail.setFileName(returnCursor.getString(nameIndex));
fileDetail.setFileSize(returnCursor.getLong(sizeIndex));
returnCursor.close();
}
}
}
return fileDetail;
}
2) get the file content:
InputStream is = cr.openInputStream(selectedFileUri);
byte[] fileContent = new byte[fileSize];
is.read(fileContent);
I found those codes from internet and didn't keep track of where they are. With Filename & filesize in FileDetail.java and the file content in "filecontent" byte arrays, I have all information I need now.
Related
I have a big question, you can attach XML through an action, when you import the XML and also save it in the "Files" tab, the question is. if it can be attached?.
Here is an example image:
what is missing is that I automatically attach in the "Files" tab, when I put upload
Here is my code where, I attached the XML
public PXAction<ARInvoice> CargarXML;
[PXUIField(DisplayName = "Carga de código hash")]
[PXButton()]
public virtual void cargarXML()
{
var Result = NewFilePanel.AskExt(true);
if (Result == WebDialogResult.OK)
{
PX.SM.FileInfo fileInfo = PX.Common.PXContext.SessionTyped<PXSessionStatePXData>().FileInfo["CargaSessionKey"];
string result = System.Text.Encoding.UTF8.GetString(fileInfo.BinData);
ARInvoice ari = Base.Document.Current;
xtFECodHashEntry graph2 = PXGraph.CreateInstance<xtFECodHashEntry>();
var pchExt = ari.GetExtension<ARRegisterExt>();
string Valor = "";
XmlDocument xm = new XmlDocument();
xm.LoadXml(result);
XmlNodeList elemList = xm.GetElementsByTagName("ds:DigestValue");
for (int i = 0; i < elemList.Count;)
{
Valor = (elemList[i].InnerXml);
break;
}
PXLongOperation.StartOperation(Base, delegate ()
{
xtFECodHash dac = new xtFECodHash();
dac.RefNbr = ari.RefNbr;
dac.DocType = ari.DocType;
dac.Nrocomprobante = ari.InvoiceNbr;
dac.Hash = Valor;
dac.Tipo = pchExt.UsrTDocSunat;
graph2.xtFECodHashs.Insert(dac);
graph2.Actions.PressSave();
});
}
}
If you have the FileInfo object which it seems like you do I have used something like this:
protected virtual void SaveFile(FileInfo fileInfo, PXCache cache, object row)
{
if (fileInfo == null)
{
return;
}
var fileGraph = PXGraph.CreateInstance<UploadFileMaintenance>();
if (!fileGraph.SaveFile(fileInfo, FileExistsAction.CreateVersion))
{
return;
}
if (!fileInfo.UID.HasValue)
{
return;
}
PXNoteAttribute.SetFileNotes(cache, row, fileInfo.UID.Value);
}
So in your example you could call this method as shown above using the following:
SaveFile(fileInfo, Base.Document.Cache, Base.Document.Current);
I have created a custom NOTEID field in InItemLotserial Table to upload images for each serial number in a custom page. It works fine without any issue.
I have wrote a processing page to update the images for existing serial numbers.
protected void AttachImage(InfoINItemLotSerialImport row, INItemLotSerial ser, InventoryItem itm)
{
string imageurl = row.ImageSourceUrl;
string imageType = row.ImageType;
string sRetVal = string.Empty;
if (itm != null)
{
if (ser != null)
{
try
{
WebClient wc = new WebClient();
byte[] buffer = wc.DownloadData(imageurl);
//MemoryStream ms = new MemoryStream(bytes);
string fileName = Path.GetFileName(imageurl);
fileName = string.Format("Serial Number attribute ({0} {1})\\{2}", itm.InventoryID, ser.LotSerialNbr, fileName);
PX.SM.FileInfo fileinfo = null;
PX.SM.UploadFileMaintenance filegraph = PXGraph.CreateInstance<PX.SM.UploadFileMaintenance>();
fileinfo = new PX.SM.FileInfo(fileName, null, buffer);
fileinfo.IsPublic = true;
if (filegraph.SaveFile(fileinfo))
{
ItemLotSerialMnt oLotSerMnt = PXGraph.CreateInstance<ItemLotSerialMnt>();
oLotSerMnt.lotserailrecdata.Current = ser;
oLotSerMnt.lotserial.Current = ser;
PXNoteAttribute.SetFileNotes(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, fileinfo.UID.Value);
PXNoteAttribute.SetNote(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, "");
sRetVal = fileinfo.Name;
}
}
catch
{
}
}
}
}
It uploads the data into UploadFile table and entry looks fine. I can access the image using the URL, but the same is not reflecting in file section of serial page.
How acumatica links the file with the screen?
Update 1
#region UsrNoteID
public abstract class usrNoteID : PX.Data.IBqlField
{
}
protected Guid? _UsrNoteID;
[PXNote()]
public virtual Guid? UsrNoteID
{
get
{
return this._UsrNoteID;
}
set
{
this._UsrNoteID = value;
}
}
#endregion
Accumatica support helped me to fix the issue. I have missed firing persist event to save.
if (filegraph.SaveFile(fileinfo))
{
ItemLotSerialMnt oLotSerMnt = PXGraph.CreateInstance<ItemLotSerialMnt>();
oLotSerMnt.lotserailrecdata.Current = ser;
oLotSerMnt.lotserial.Current = ser;
PXNoteAttribute.SetFileNotes(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, fileinfo.UID.Value);
PXNoteAttribute.SetNote(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, "");
sRetVal = fileinfo.Name;
oLotSerMnt.Persist();
}
oLotSerMnt.Persist();
Using the Google Docs Java API with a Google Apps account, is it possible to impersonate a user and download a file?
When I run the program below, it is clearly logging on to the domain and impersonating the user because it retrieves the details of one of the files and prints out the file title. However, when it tries to download the file, a ServiceForbiddenException is thrown.
If it is not possible with the Java API, does anyone know if it is possible for my program to write an HTTP request to download the file using the Protocol API?
public class AuthExample {
private static DocsService docService = new DocsService("Auth Example");
public static void main(String[] args)
throws Exception
{
String adminUser = args[0];
String adminPassword = args[1];
String authToken = args[2];
String impersonatedUser = args[3];
loginToDomain(adminUser, adminPassword, authToken);
URL url = new URL( "https://docs.google.com/feeds/" + impersonatedUser + "/private/full" );
DocumentListFeed feed = docService.getFeed(url, DocumentListFeed.class);
DocumentListEntry entry = feed.getEntries().get(0);
String title = entry.getTitle().getPlainText();
System.out.println( title );
String type = entry.getType();
if ( type.equals("document") )
{
String encodedAdminUser = URLEncoder.encode(adminUser);
String resourceId = entry.getResourceId();
String resourceIdNoPrefix = resourceId.substring( resourceId.indexOf(':')+1 );
String downloadUrl =
"https://docs.google.com/feeds/download/documents/Export" +
"?xoauth_requestor=" + encodedAdminUser +
"&docId=" + resourceIdNoPrefix +
"&exportFormat=doc";
downloadFile( downloadUrl, title + ".doc" );
}
}
private static void loginToDomain(String adminUser, String adminPassword, String authToken)
throws OAuthException, AuthenticationException
{
String domain = adminUser.substring( adminUser.indexOf('#')+1 );
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(domain);
oauthParameters.setOAuthConsumerSecret(authToken);
oauthParameters.setOAuthType(OAuthType.TWO_LEGGED_OAUTH);
oauthParameters.setScope("https://docs.google.com/feeds/ http://spreadsheets.google.com/feeds/ http://docs.googleusercontent.com/");
docService.useSsl();
docService.setOAuthCredentials(oauthParameters, new OAuthHmacSha1Signer());
docService.setUserCredentials(adminUser, adminPassword);
}
// Method pasted directly from Google documentation
public static void downloadFile(String exportUrl, String filepath)
throws IOException, MalformedURLException, ServiceException
{
System.out.println("Exporting document from: " + exportUrl);
MediaContent mc = new MediaContent();
mc.setUri(exportUrl);
MediaSource ms = docService.getMedia(mc);
InputStream inStream = null;
FileOutputStream outStream = null;
try {
inStream = ms.getInputStream();
outStream = new FileOutputStream(filepath);
int c;
while ((c = inStream.read()) != -1) {
outStream.write(c);
}
} finally {
if (inStream != null) {
inStream.close();
}
if (outStream != null) {
outStream.flush();
outStream.close();
}
}
}
}
Impersonation will work as intended if you use Oauth2 with ServiceAccounts
I want to check the free size available on My Device's SD Card. I know FileConnection API. How can I check the free size of the SD Card ?
The idea is to open the cfcard file connection and call availableSize() on it. to get the proper memory card location read it from the System.getProperty(...). In some devices the aforesaid property may fail hence constructing new path from the memory card name system property.
NOTE: In certain devices the hostname must be 'localhost', hence change the return string in getFilehost() appropriately.
PS: Since this is a code snippet certain form / command references may throw null pointer please resolve it accordingly.
Below code with get you the available size in 'BYTES' of the memory card
String memoryCardPath = System.getProperty("fileconn.dir.memorycard");
addToLogs(memoryCardPath);
if (null == memoryCardPath) {
displayAlert(null);
}
nativeHostname(memoryCardPath);
addToLogs(nativeHostname);
String path = buildPath(memoryCardPath.substring(getFilehost().length()));
addToLogs(path);
long availableSize = getAvailableSize(path);
addToLogs(String.valueOf(availableSize)+" Bytes");
if(availableSize == 0L) {
String memoryCardName = System.getProperty("fileconn.dir.memorycard.name");
addToLogs(memoryCardName);
path = buildPath(memoryCardName + "/");
addToLogs(path);
availableSize = getAvailableSize(path);
addToLogs(String.valueOf(availableSize)+" Bytes");
if(availableSize == 0L) {
displayAlert(null);
return;
}
}
displayAlert(String.valueOf(availableSize));
Supporting methods
public String buildPath(String foldername) {
if(null == foldername) {
foldername = "";
}
addToLogs("[BP]"+getFilehost());
addToLogs("[BP]"+foldername);
return new StringBuffer().append(getFilehost()).append(foldername).toString();
}
String nativeHostname = null;
public void nativeHostname(String url) {
String host = url.substring("file://".length());
addToLogs("[NH]"+host);
int index = host.indexOf('/');
addToLogs("[NH]"+String.valueOf(index));
if(index > 0) {
nativeHostname = host.substring(0, index);
} else {
nativeHostname = "/";
}
addToLogs("[NH]"+nativeHostname);
}
public boolean tryLocalhost = false;
public String getFilehost() {
final StringBuffer buf = new StringBuffer().append("file://");
if (null != nativeHostname && nativeHostname.length() > 0) {
buf.append(nativeHostname);
}
addToLogs("[GFH] "+buf.toString());
return buf.toString();
}
public long getAvailableSize(String path) {
FileConnection fcon = null;
try {
fcon = (FileConnection) Connector.open(path, Connector.READ);
if(null != fcon && fcon.exists()) {
return fcon.availableSize();
} else {
return 0L;
}
} catch (IOException ex) {
ex.printStackTrace();
addToLogs("[GAS]"+"ERROR : "+ex.getMessage());
return 0L;
} finally {
try {
fcon.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public void displayAlert(String text) {
Alert alert = new Alert(
null == text ? "Warning" : "Info",
null == text ? "Memory card not available" : text,
null,
null == text ? AlertType.WARNING : AlertType.INFO);
alert.setTimeout(Alert.FOREVER);
Display.getDisplay(this).setCurrent(alert, Display.getDisplay(this).getCurrent());
}
public void addToLogs(String log) {
log = null == log ? "null" : log;
getFormLogs().append(new StringItem("", log+"\n"));
}
I'm struggling with this a whole day now and I can't seem to figure it out.
I have a fucntion that gives me a list of all links on a specific url. That works fine.
However I want to make this function recursive so that it searches for the links found with the first search and adds them to the list and continue so that it goes through all my pages on the website.
How can I make this recursive?
My code:
class Program
{
public static List<LinkItem> urls;
private static List<LinkItem> newUrls = new List<LinkItem>();
static void Main(string[] args)
{
WebClient w = new WebClient();
int count = 0;
urls = new List<LinkItem>();
newUrls = new List<LinkItem>();
urls.Add(new LinkItem{Href = "http://www.smartphoto.be", Text = ""});
while (urls.Count > 0)
{
foreach (var url in urls)
{
if (RemoteFileExists(url.Href))
{
string s = w.DownloadString(url.Href);
newUrls.AddRange(LinkFinder.Find(s));
}
}
urls = newUrls.Select(x => new LinkItem{Href = x.Href, Text=""}).ToList();
count += newUrls.Count;
newUrls.Clear();
ReturnLinks();
}
Console.WriteLine();
Console.Write("Found: " + count + " links.");
Console.ReadLine();
}
private static void ReturnLinks()
{
foreach (LinkItem i in urls)
{
Console.WriteLine(i.Href);
//ReturnLinks();
}
}
private static bool RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
//Getting the Web Response.
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
//Returns TURE if the Status code == 200
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}
}
The code behind LinkFinder.Find can be found here: http://www.dotnetperls.com/scraping-html
Anyone knows how I can either make that function recursive or can I make the ReturnLinks function recursive? I prefer to not touch the LinkFinder.Find method as this works perfect for one link, I just should be able to call it as many times as needed to expand my final url list.
I assume you want to load each link and find the link within, and continue until you run out of links?
Since it is likely that the recursion depth could get very large, i would avoid recursion, this should work i think.
WebClient w = new WebClient();
int count = 0;
urls = new List<string>();
newUrls = new List<LinkItem>();
urls.Add("http://www.google.be");
while (urls.Count > 0)
{
foreach(var url in urls)
{
string s = w.DownloadString(url);
newUrls.AddRange(LinkFinder.Find(s));
}
urls = newUrls.Select(x=>x.Href).ToList();
count += newUrls.Count;
newUrls.Clear();
ReturnLinks();
}
Console.WriteLine();
Console.Write("Found: " + count + " links.");
Console.ReadLine();
static void Main()
{
WebClient w = new WebClient();
List<ListItem> allUrls = FindAll(w.DownloadString("http://www.google.be"));
}
private static List<ListItem> FindAll(string address)
{
List<ListItem> list = new List<ListItem>();
foreach (url in LinkFinder.Find(address))
{
list.AddRange(FindAll(url.Address)));//or url.ToString() or what ever the string that represents the address
}
return list;
}