I am using the following Intent to open .xlsx file using an Intent Chooser. I have Kingsoft Offie and Polaris office at my disposal to open these files.
var calcIntent = new Intent ();
calcIntent.SetAction (Intent.ActionView);
Android.Net.Uri fileUri = Android.Net.Uri.FromFile (new File (OSUtils.GetCalcFilePath (id)));
calcIntent.SetData (fileUri);
var mimeType = OSUtils.GetMimeType (fileUri.ToString ());
calcIntent.SetType (mimeType);
try {
StartActivity (Intent.CreateChooser (calcIntent, "Open Via"));
} catch (ActivityNotFoundException) {
Toast.MakeText (this, "You do not have Kingsoft Office Installed!", ToastLength.Long).Show();
}
Where OSUtils.GetCalcFilePath is defined as
public static string GetCalcFilePath (int currentId) {
var calcDirPath = OSUtils.GetCalcDirForEstimate (currentId);
var calcSheetName = String.Format ("builder_calc_{0}.xlsx", currentId);
var calcSheet = new Java.IO.File (calcDirPath, calcSheetName);
if (!calcSheet.Exists ()) {
calcSheet.CreateNewFile ();
}
return calcSheet.Path;
}
and OSUtils.GetMimeType is defined as
public static string GetMimeType (string fileUri) {
String mimeType = null;
var extension = MimeTypeMap.GetFileExtensionFromUrl (fileUri);
if (extension != null) {
mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension (extension);
}
if (mimeType != null)
return mimeType;
else
return "*/*";
}
Now when I run it, I get a dialog(chooser) giving me two options, "Kingsoft Office" and "Polaris Office". Choosing Polaris Office gives me a toast saying "Not a supported document type" and on the other hand choosing Kingsoft Office just opens up the Kingsoft App and does nothing. The document is not opened in Kingsoft Office.
Whereas, if I go to my file manager and tap on the .xlsx file, it opens up perfectly in both the office apps. I checked my code and all the mimetypes and paths are correct and are pointing to the desired file. Any ideas?
Thanks in Advance
EDIT
It seems using SetData and SetType individually wont work here. They are mutually exclusive i.e calling one clears the other. "SetDataAndType" is the way to go here. :)
It seems using SetData and SetType individually wont work here. They are mutually exclusive i.e calling one clears the other.
"SetDataAndType" is the way to go here. :)
Related
I need to check if a document (pdf,xls,docx,ppt..etc) is viewed by user or not. In some user's devices there are no apps to open some document types. In such cases I need to know that the document is not opened/viewed by user.
This is an android app developed using xamarin forms. Used below code to open document. This is working fine if there is an app to open respective document. But, if there are no apps to open the document it shows blank activity with the title "Choose an Application:"
...other code here...
var chooserIntent = Intent.CreateChooser(intent, "Choose an Application:");
activity.StartActivityForResult(chooserIntent, 10);
You can check it by the Android file MIME type.Refer the following code
Dictionary<string, string> matchArray = new Dictionary<string, string>();
matchArray.Add(".3gp", "video/3gpp");
matchArray.Add(".apk", "application/vnd.android.package-archive");
matchArray.Add(".doc", "application/msword");
matchArray.Add(".gif", "image/gif");
matchArray.Add(".wps", "application/vnd.ms-works");
matchArray.Add(".pdf", "application/pdf");
matchArray.Add(".ppt", "application/vnd.ms-powerpoint");
matchArray.Add(".png", "image/png");
//...
public void OpenFileByPath(Context context, string path)
{
if (context == null || path == null)
return;
string type = "";
foreach (string key in matchArray.Keys)
{
if (path.Contains(key))
{
type = matchArray[key];
break;
}
}
if (type == "")
{
//there is no app can open the file ,do some you want
}
else
{
// open your file
}
}
You can add more key-values about MIME type by searching from the internet
I created an Android mobile with Xamarin Android. There is a feature inside that app that can send an image to twitter. I tested this feature after downloading the app from the play store on the Samsung S7, the alcatel Pixi4 and my tablet (Galaxy Tab 2). My code creates a bitmap file in the external cachedirectory and requests twitter to read this file in order to have it attached to a tweet.
It works fine. However, I asked other people to test it (they also downloaded my app from the play store) and they told me the file is created in the cache directory but the bitmap picture is not attached to their tweets. It is also important to know that other apps that do that (such as long tweet) work on all devices:
https://play.google.com/store/apps/details?id=de.cbruegg.longtweet
I don't understand. Twitter, works on all devices. This has been checked. Apps (such as long tweet) can send a file to twitter to tweet this. This also works on all devices. Moreover, my app perfectly writes files to the cache directory. Works on all devices. In addition, my app is able to send a bitmap picture to twitter in order to tweet the picture. This works on my S7. But not on the S7 of my tester. I don't get it. Here is my code. Someway, I am doing something wrong. By the way, generating the tweets always works. It is just the case that the picture is not attached to the tweet when testing on the device of my tester.
Here is my code. Please let me know how I should improved it.
public bool TweetImage(Bitmap imageToTweet)
{
var messageIntent = context.FindMessageIntent(this.twitterConstants.PackageName);
if (messageIntent == null)
{
return false;
}
string outputFileBMP = SaveBitmap(imageToTweet);
context.Tweet(messageIntent, outputFileBMP, this.twitterConstants.DefaultTwitterText, this.twitterConstants.ChooserMessage);
return true;
}
private string SaveBitmap(Bitmap imageToTweet)
{
string outputFileBMP = System.IO.Path.Combine(context.ExternalCacheDir.Path, System.Guid.NewGuid().ToString() + ".bmp");
using (var outputFileStream = System.IO.File.Create(outputFileBMP))
{
imageToTweet.Compress(Android.Graphics.Bitmap.CompressFormat.Png, 100, outputFileStream);
}
return outputFileBMP;
}
and
public static Intent FindMessageIntent(this ContextWrapper contextWrapper, params string[] packageNames)
{
Intent wantedIntent = new Intent();
wantedIntent.SetType("text/plain");
var resolveInfos = contextWrapper.PackageManager.QueryIntentActivities(wantedIntent, PackageInfoFlags.MatchDefaultOnly);
var result = (from r in resolveInfos
from p in packageNames
where p == r.ActivityInfo.PackageName
select p).FirstOrDefault();
if (result != null)
{
wantedIntent.SetPackage(result);
return wantedIntent;
}
return null;
}
and
public static void Tweet(this ContextWrapper contextWrapper, Intent messageIntent, string filePath = null, string message = null, string chooserMessage = null)
{
if (filePath != null)
{
using (var file = new Java.IO.File(filePath))
{
messageIntent.PutExtra(Intent.ExtraStream, Android.Net.Uri.FromFile(file));
}
}
if (message != null)
{
messageIntent.PutExtra(Intent.ExtraText, message);
}
if (chooserMessage != null)
{
using (var chooser = Intent.CreateChooser(messageIntent, chooserMessage))
{
contextWrapper.StartActivity(chooser);
}
return;
}
contextWrapper.StartActivity(messageIntent);
}
Since you're using the external cache directory, you should always make sure it exists before using it. From the Android documentation:
The external storage may be unavailable—such as when the user has mounted the storage to a PC or has removed the SD card that provides the external storage—you should always verify that the volume is available before accessing it. You can query the external storage state by calling getExternalStorageState(). If the returned state is equal to MEDIA_MOUNTED, then you can read and write your files.
S7 supports MicroSD cards so maybe this is the case with your tester?
Also, Long Tweet does the image storing and sharing slightly differently than you. Here are the differences:
They store the generated bitmap to the internal cache directory (CacheDir instead of ExternalCacheDir).
They set the read and write permissions (Readable and Executable properties of the File object) to the resulting file and directory, so that other apps can access the file when they have the full path.
For the record, here's the Java code for how they set up the intent:
public static void intentImage(Context context, File file, String intentAction)
{
file.getParentFile().getParentFile().setExecutable(true, false);
file.getParentFile().setExecutable(true, false);
file.getParentFile().getParentFile().setReadable(true, false);
file.getParentFile().setReadable(true, false);
file.setReadable(true, false);
file.setExecutable(true, false);
String fileStr = "file://" + file.toString();
Intent intent = new Intent();
intent.setAction(intentAction);
if (intentAction == "android.intent.action.SEND")
{
intent.putExtra("android.intent.extra.STREAM", Uri.parse(fileStr));
intent.setType("image/" + fileStr.substring(fileStr.lastIndexOf(".") + 1));
}
else
{
intent.setDataAndType(Uri.parse(fileStr), "image/" + fileStr.substring(fileStr.lastIndexOf(".") + 1));
}
context.startActivity(intent);
}
I would start by utilizing the internal cache directory and setting the correct permissions to the file generated (possibly also the parent folder).
Just want to confirm that is it possible that when i click on file of any extension will open up with its compatible software in android phone or display me the list of software’s present in mobile which can open the file and if it didn't found any software it will indicate user to first download the software to open that particular file (All this thing need to be done pro grammatically).
Thanks.
Any help will be appreciated.
In order to open the file you can use the following method, If there is no application that can handle given file, it simply shows a Toast saying no application found.
private void viewFile(String filePath, String title, int fileType) {
Uri uri = Uri.parse("file://" + filePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
String dataAndType = getIntentDataAndType(filePath);
intent.setDataAndType(uri, dataAndType);
intent.putExtra(Intent.EXTRA_TITLE, title);
// Verify that the intent will resolve to an activity
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(getActivity(), "No Application found", Toast.LENGTH_SHORT).show();
}
}
UPDATED :
For finding the mime type of the file.
private String getIntentDataAndType(String filePath) {
String exten = "";
int i = filePath.lastIndexOf('.');
// If the index position is greater than zero then get the substring.
if (i > 0) {
exten = filePath.substring(i + 1);
}
String mimeType = android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(exten);
mimeType = (mimeType == null) ? "*/*" : mimeType;
return mimeType;
}
I use the following snippet to open or share any file from device's storage (MyFile is my own class which extends File and should be considered as File. The flag String I'm passing is either Intent.ACTION_VIEW or Intent_ACTION_SEND):
public void openOrShare(String flag, MyFile f){
try {
MimeTypeMap mmap = MimeTypeMap.getSingleton();
String type = MimeTypeMap.getFileExtensionFromUrl(f
.getName());
String ftype = mmap.getMimeTypeFromExtension(type);
if (ftype == null)
ftype = "*/*";
Intent intent = new Intent(flag);
Uri data = Uri.fromFile(f);
intent.setDataAndType(data, ftype);
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Tools.gimmeToast(
getActivity(),
"no application found to handle this file type",
Toast.LENGTH_LONG);
} catch (Exception e) {
e.printStackTrace();
}
}
While passing Intent.ACTION_VIEW everything works fine for any (also custom) types, the system creates a chooser and lists the apps, for well-known files it launches the correct Activity to handle the file immediately .
The problem: passing Intent.ACTION_SEND seems to be working halfway - it creates the chooser as well, however most apps (Dropbox and many more that I've tested with) just crash with an NPE when confirming the action. Testing with various email clients also failed: they don't crash like most of the other apps, but create a message and put the local Uri (like //storage/.....) in the To field (gmail) OR simply create a new empty message, ignoring the Intent data (yahoo! mail), while I expect them to attach the file to a new message.
The question: what am I doing wrong to share any file type?
EDIT
I figured out that it works if using intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));, however there's an ActivityNotFoundException being caught when trying to share some specific files (like .dat). As far as I know, apps like Dropbox support adding any file types. Looking for a workaround.
If you don't know the MIME Type of the file, then do not set it.
So I would try:
Intent intent = new Intent(flag);
Uri data = Uri.fromFile(f);
String ftype = mmap.getMimeTypeFromExtension(type);
if (ftype != null) {
intent.setDataAndType(data, ftype);
} else {
intent.setData(data);
}
setDataAndType needs data and explicit mime type (maybe */* is not).
Never mind, I finally figured that out. So here's the working solution which allows to share any type of data using any app*:
*Note: the below code actually also lists apps that might be not able to handle specific file types, those apps (at least they should) notify the user that the file type is not supported if this is the case
public void doShare(MyFile f) {
try {
Intent intent = new Intent(Intent.ACTION_SEND);
MimeTypeMap mmap = MimeTypeMap.getSingleton();
String type = MimeTypeMap.getFileExtensionFromUrl(f.getName());
String ftype = mmap.getMimeTypeFromExtension(type);
if (ftype == null)
ftype = "*/*";
intent.setType(ftype);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Tools.gimmeToast(getActivity(),
"no application found to handle this file type",
Toast.LENGTH_LONG);
} catch (Exception e) {
e.printStackTrace();
}
}
I'm trying to get external applications to open different file types determined at runtime. From everything I've looked at I feel I'm doing it right but none of the programs open the files correctly. For Astro using text files it shows this error: java.lang.illegalargumentexception invalid uri used for observer. Basically it appears not to have gotten the right file location. Any help would be greatly appreciated.
Here is my code:
System.Net.WebClient webClient = new System.Net.WebClient();
String attachmentPath = appManager.getAttachmentPath(plexState.getActiveBrain().getGuid(), attachment.getId(), attachment.getType());
webClient.DownloadDataCompleted += (object sender, System.Net.DownloadDataCompletedEventArgs eventArgs) =>
{
notificationManager.hidePopup();
if ((eventArgs.Cancelled == false) && (eventArgs.Error == null))
{
byte[] fileBytes = eventArgs.Result;
Java.IO.File attachmentFile = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads), attachment.getName());
System.IO.File.WriteAllBytes(attachmentFile.AbsolutePath, fileBytes);
// Get MIME type
String extension = MimeTypeMap.GetFileExtensionFromUrl(attachmentFile.AbsolutePath);
String mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
Intent intent = new Intent(Intent.ActionView);
Logger.console("File exists: " + attachmentFile.Exists()); // Shows true.
intent.SetData(Android.Net.Uri.FromFile(attachmentFile));
intent.SetType(mimeType);
// Check if any application can open the given MIME type. Otherwise show notice to user
PackageManager packageManager = Activity.PackageManager;
IList<ResolveInfo> resolversList = packageManager.QueryIntentActivities(intent, 0);
if (resolversList.Count > 0)
{
Activity.StartActivity(intent);
}
else
{
Toast.MakeText(Activity, "Unable to find for ext...", ToastLength.Long);
}
}
else if (eventArgs.Error != null)
{
Logger.console(eventArgs.Error.Message);
}
};
webClient.DownloadDataAsync(new Uri(attachmentPath));
I just found the answer to this issue in case anyone else runs into it. I'm not sure if this is just a Monodroid bug or there is something else at play that I'm unaware of. To solve this issue take these two lines of code:
intent.SetData(Android.Net.Uri.FromFile(attachmentFile));
intent.SetType(mimeType);
and condense to this:
intent.SetDataAndType(Android.Net.Uri.FromFile(attachmentFile), mimeType);
I can't believe this worked. After spending almost two days on this I thought just to condense the code and could not at first figure out why it started working.