I'm developing a Xamarin Android app and I need the ability to be able to work with Passes (PassKit passes for example (JSON)). I need to be able to list all the passes in a ListVew and be able to open and display the pass. Also be able to save them to a wallet such as PassWallet or Pass2u. I don't need the ability to create them, just view them, and save them to a wallet or discard them.
There seems to be an example Xamarin iOS app which does exactly what i need here but of course I need to be able to do this in Xamarin Android.
I've been researching this for hours but don't know how to achieve what i need. JSON.net seems the way to go to read the passes, but that's as far as I've managed to get. Some examples would be great. Can anybody help?
To add the pass into PassWallet you can use the following:
private static boolean launchPassWallet(Context applicationContext, Uri uri, boolean launchGooglePlay) {
if (null != applicationContext) {
PackageManager packageManager = applicationContext.getPackageManager();
if (null != packageManager) {
final String strPackageName = "com.attidomobile.passwallet";
Intent startIntent = new Intent();
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startIntent.setAction(Intent.ACTION_VIEW);
Intent passWalletLaunchIntent = packageManager
.getLaunchIntentForPackage(strPackageName);
if (null == passWalletLaunchIntent) {
// PassWallet isn't installed, open Google Play:
if (launchGooglePlay) {
String strReferrer = "";
try {
strReferrer = "&referrer=" + URLEncoder.encode(uri.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
strReferrer = "";
}
try {
startIntent.setData(Uri.parse("market://details?id=" + strPackageName + strReferrer));
applicationContext.startActivity(startIntent);
} catch (android.content.ActivityNotFoundException anfe) {
// Google Play not installed, open via website
startIntent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + strPackageName + strReferrer));
applicationContext.startActivity(startIntent);
}
}
} else {
final String strClassName = "com.attidomobile.passwallet.activity.TicketDetailActivity";
startIntent.setClassName(strPackageName, strClassName);
startIntent.addCategory(Intent.CATEGORY_BROWSABLE);
startIntent.setDataAndType(uri, "application/vnd.apple.pkpass");
applicationContext.startActivity(startIntent);
return true;
}
}
}
return false;
}
And an example call is:
launchPassWallet(getApplicationContext(),Uri.parse("http://test.attidomobile.com/PassWallet/Passes/AttidoMobile.pkpass"), true);
You can also use a file:// URL if you have the file locally.
To display them in the list, you'd need to unzip the .pkpass file and then parse the JSON for the relevant fields.
Related
One user reported that my app fails to request directory access when selecting a folder via the ACTION_OPEN_DOCUMENT_TREE intent.
For some reason it does not show my application, instead "Anonymous":
Translated: "Allow Anonymous to access files in Camera. This will let Anonymous access current and future content stored in Camera".
The user has a MIUI 12 with Android 11 on a Mi Note 10 lite.
I have the same just with a Mi Note 10, no issues ofc.
Checked the Android source code:
https://android.googlesource.com/platform/packages/apps/DocumentsUI/+/refs/heads/master/src/com/android/documentsui/picker/ConfirmFragment.java#82
case TYPE_OEPN_TREE:
final Uri treeUri = mTarget.getTreeDocumentUri();
final BaseActivity activity = (BaseActivity) getActivity();
final String target = activity.getCurrentTitle();
final String text = getString(R.string.open_tree_dialog_title,
**getCallingAppName**(getActivity()), target);
message = getString(R.string.open_tree_dialog_message,
**getCallingAppName**(getActivity()), target);
builder.setTitle(text);
builder.setMessage(message);
builder.setPositiveButton(
R.string.allow,
(DialogInterface dialog, int id) -> {
pickResult.increaseActionCount();
mActions.finishPicking(treeUri);
});
break;
#NonNull
public static String getCallingAppName(Activity activity) {
final String anonymous = activity.getString(R.string.anonymous_application);
final String packageName = getCallingPackageName(activity);
if (TextUtils.isEmpty(packageName)) {
return anonymous;
}
final PackageManager pm = activity.getPackageManager();
ApplicationInfo ai;
try {
ai = pm.getApplicationInfo(packageName, 0);
} catch (final PackageManager.NameNotFoundException e) {
return anonymous;
}
CharSequence result = pm.getApplicationLabel(ai);
return TextUtils.isEmpty(result) ? anonymous : result.toString();
}
public static String getCallingPackageName(Activity activity) {
String callingPackage = activity.getCallingPackage();
// System apps can set the calling package name using an extra.
try {
ApplicationInfo info =
activity.getPackageManager().getApplicationInfo(callingPackage, 0);
if (isSystemApp(info) || isUpdatedSystemApp(info)) {
final String extra = activity.getIntent().getStringExtra(
Intent.EXTRA_PACKAGE_NAME);
if (extra != null && !TextUtils.isEmpty(extra)) {
callingPackage = extra;
}
}
} catch (NameNotFoundException e) {
// Couldn't lookup calling package info. This isn't really
// gonna happen, given that we're getting the name of the
// calling package from trusty old Activity.getCallingPackage.
// For that reason, we ignore this exception.
}
return callingPackage;
}
...and it seems that for whatever reason my packagename isn't found. How can can happen?
Asked him to install one of my other apps, and it happens there as well.
Asked him then to install another app from the playstore (FX File Explorer) and there it does not happen.
So it is specific to his device and my app.
So it turned out that this user having that issue turned off the MIUI Optimizations in the developer settings.
Bug report: συσκευη, εκδοση miui, Play store install (alpha 1021). It was impossible to specify a b i o s file or specify a game image directory in when MIUI optimizations are off. Turning them back on fixed the issue and directories are scanned normally. Also on the popup to allow folder access the app displays as "Anonymous" instead of AetherSX2 on my system. Some developer was talking about having the same issue here.
Has anyone one worked on attaching GIFs in apps? Something similar to WhatsApp or Skype.
When I try to get the content uri from the below code -
final InputConnectionCompat.OnCommitContentListener callback = new InputConnectionCompat.OnCommitContentListener() {
#Override
public boolean onCommitContent(InputContentInfoCompat info, int flags, Bundle opts) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && (flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
try {
info.requestPermission();
} catch (Exception e) {
return false; // return false if failed
}
}
Log.d("TAG", "Content URI " + info.getContentUri());
return true; // return true if succeeded
}
};
return InputConnectionCompat.createWrapper(ic, info, callback);
}
This is what the content URI looks like, which works fine when passed to Glide.
Content URI (Swift keyboard) -> content://com.touchtype.swiftkey.fileprovider/share_images/Bi7V7tixYcLzbNgEJhE_NaXQ5eE.gif
Content URI(Gboard) -> content://com.google.android.inputmethod.latin.inputcontent/inputContent?fileName=%2Fdata%2Fuser_de%2F0%2Fcom.google.android.inputmethod.latin%2Ffiles%2Fgif69727604103223767740&packageName=com.testsdk&mimeType=image%2Fgif
But when same is passed to iOS app, then it will not work.
Can you please guide me in correct direction?
What needs to be sent to iOS app, so that same selected GIF can be shown in iOS app as well?
EDIT - I think I should use getLinkUri() instead of getContentUri().
Is it correct approach?
URI https://tse2.mm.bing.net/th?id=OGC.5e1a1b1d71e12b32cc7bfac93fbb7d1f&pid=Api&rurl=https%3a%2f%2fmedia.giphy.com%2fmedia%2fVe20ojrMWiTo4%2f200.gif&ehk=iHJHy%2fFSR7s2nIoEXxTIrUAWWuGBnz%2fecKwkM8Hm2ac%3d
In my app there is a WebView to which I load a website from server.
There are two cases:
On the loaded website there is a button which should lead to an another app. I know it can be handled like this:
Click me
but this doesn't work in the WebView (only in a standalone browser)! I tried to handle it in shouldOverrideUrlLoading and redirect to an external browser with Intent, but the URI with "intent://" URL is not recognised and cannot be opened.
The link I get from server is the 'intent' link.
The behaviour in both cases should be the same: if app is installed open the app, if not open Google Play do download the app.
Is there any way to do this?
I'm not sure if this is the best option, but I handeled it similarily to what #vineetv suggested. This method is called inside shouldOverrideUrlLoading():
private void handleNewUrl(String url) {
Uri uri = Uri.parse(url);
if (uri.getScheme().equals("http") || uri.getScheme().equals("https"))
openExternalWebsite(url);
else if (uri.getScheme().equals("intent")) {
String appPackage = getAppPackageFromUri(uri);
if (appPackage != null) {
PackageManager manager = getContext().getPackageManager();
Intent appIntent = manager.getLaunchIntentForPackage(appPackage);
if (appIntent != null) {
getActivity().startActivity(appIntent);
} else {
openExternalWebsite("https://play.google.com/store/apps/details?id=" + appPackage);
}
}
}
}
private String getAppPackageFromUri(Uri intentUri) {
Pattern pattern = Pattern.compile("package=(.*?);");
Matcher matcher = pattern.matcher(intentUri.getFragment());
if (matcher.find())
return matcher.group(1);
return null;
}
private void openExternalWebsite(String url) {
Intent webeIntent = new Intent(Intent.ACTION_VIEW);
webeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
webeIntent.setData(Uri.parse(url));
getActivity().startActivity(webeIntent);
}
It seems, to work. But if you have a better solution, let me know!
I programmed an app that can send a message to twitter with an image attached. It works! I tested it on several devices and asked other people to do the same. It even works for a Direct Message when a twitter friend is selected. However, it does not work when "Direct Message" is selected. This forces the user to select a friend directly instead of selecting him via "Direct Message" (which is really strange) otherwise the picture is not attached. Just have a look at the screenshot:
Here is my Xamarin Android programming code. Let me know how to fix it. Currently, all options work, even selecting my friend but not "Direct Message". I also need to tell that I do not have any issue with the twitter text I expect to see in the tweet.
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;
}
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);
}
Please note that I am using Android and need a solution based on Android (intent based).
Sadly, Twitter don't provide API access for uploading images via DM.
If you are able to use Twitter's private API, you should be able to attach a media_id to your DM. But other than that, you're out of luck.
Sorry.
Using Android 6.0.1, API 23,
I successfully implemented reading a file from a storage-location that the user picks in the document-picker of SAF (Storage Access Framework).
Now, I would like to use the Persist permissions that SAF allows in order to allow my App to always pick the same file (but without any document-picker window [and sub-windows] popping in all the time).
I somehow succeeded in implementing the Persist-permissions (as shown in the SAF-doc) (...for that, see my code example below...) - But questions came up:
In spite of persistence-permission working - why does the document-picker window keep popping up? Once the file is picked by the user - from this moment on - I would like to simply get the file content without any picker window coming to foreground. Is this possible? And if yes, how?
(I can tell that persistence permission is somehow working since the file gets read immediately without the user having to pick again. But still this nasty picker window should not have to come up - or does it?)
Is it possible to keep SAF persistence-permission even if my App gets closed or stopped? If yes, how?
Here is the code sample that does get the file content, including my trial with the persistence permission:
Inside a StorageClientFragment Class:
public String locationFileContent;
public void performFileSearch() {
Intent openDocumentIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
openDocumentIntent.addCategory(Intent.CATEGORY_OPENABLE);
openDocumentIntent.setType("text/plain");
openDocumentIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(openDocumentIntent, READ_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
// Here is my Persist-permission trial !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
getActivity().getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
this.locationFileContent = readTextFromUri(uri);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private String readTextFromUri(Uri uri) throws IOException {
InputStream inputStream = getActivity().getContentResolver().openInputStream(uri);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
inputStream.close();
return stringBuilder.toString();
}
Creating the Fragment gets done in MainActivity:
StorageClientFragment storageClientFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
storageClientFragment = new StorageClientFragment();
transaction.add(storageClientFragment, FRAGTAG);
transaction.commit();
}
}
Calling the FileSearch is done when a Test-Button is clicked:
public void TestingButtonClicked(View view) {
this.storageClientFragment.performFileSearch();
String stringFileContent = this.storageClientFragment.locationFileContent;
Toast.makeText(this, "Back in Main, Content = " + stringFileContent, Toast.LENGTH_SHORT).show();
}
The above code works nicely. But again, how can I stop the SAF document-picker window from showing up all the time (even though persist permission is set)?
why does the document-picker window keep croping up ?
Presumably because you keep starting an ACTION_OPEN_DOCUMENT activity. If you do not want to ask the user to pick a document, do not start this activity.
I would like to simply get the file content without any picker-window coming to foreground. Is this possible ? And if yes, how ??
The same way that you got the content originally: pass the Uri to openInputStream() on a ContentResolver.
If you are using takePersistableUriPermissions() to aim for long-lived access to the content, it is still your job to hold onto the Uri. This is not significantly different than how this sort of thing works in other environments (e.g., remembering the user's history of local file paths for a desktop OS program).
Since a Uri can be trivially converted to and from a String, saving the Uri somewhere (database, SharedPreferences, or other file) is not especially difficult.
Is it possible to keep SAF persistance-permission even if my App gets closed or stopped ? If yes, how ??
takePersistableUriPermission(), as you have already implemented.