I have successfully configured iOS app to handle audio document types (not deeplinks!) so I can use share dialog to open audio files in my app, this works fine.
I have successfully configured Android intent-filter in the AndroidManifest.xml to hande audiofiles same way, this is also recognized fine by Android system and I see my app when I use share menu on audiofiles. However, Android implementation of Linking component seems to ignore SEND intent actions and only care about View intent actions as I saw on IntentModule.java:55:
if (Intent.ACTION_VIEW.equals(action) && uri != null) {
initialURL = uri.toString();
}
I tried to patch intent in my MainActivity.java to make it return same uri with VIEW action, but this always produces an error in runtime.
#Override
public Intent getIntent() {
Intent origIntent = super.getIntent();
if (origIntent != null && Intent.ACTION_SEND.equals(origIntent.getAction())) {
return new Intent(Intent.ACTION_VIEW, origIntent.getData());
}
return origIntent;
}
However I always get an error indicating that getData is null.
I saw this answer, but having a share extension is an overkill for me.
I discovered that Intent might contain additional information called ClipData, which can contain Uri as well. My luck was that I had exactly one Uri (I guess it can contain multiple Uri objects if I share multiple audiofiles at once. So this code worked, I can now share files to react native app
#Override
public Intent getIntent() {
Intent origIntent = super.getIntent();
if (origIntent != null && Intent.ACTION_SEND.equals(origIntent.getAction())) {
return new Intent(Intent.ACTION_VIEW, this.uriFromClipData(origIntent.getClipData()));
}
return origIntent;
}
private Uri uriFromClipData(ClipData clip) {
if (clip != null && clip.getItemCount() > 0) {
return clip.getItemAt(0).getUri();
}
return null;
}
Related
I have recently started working on an android project that uses Microsft authentication and graph API. By following this:
https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-android
I am able to authenticate and get some data from graph API. Now I want to load the profile photo of Microsoft account in the app. For this purpose, I used ProfilePhoto object with a call back as follows:
graphClient
.me()
.photo()
.buildRequest()
.get(new ICallback<ProfilePhoto>() {
#Override
public void success(ProfilePhoto profilePhoto) {
Log.d(TAG, "Found " + profilePhoto.getRawObject().toString());
}
#Override
public void failure(ClientException ex) {
displayError(ex);
}
});
Here profilePhoto.getRawObject() returns a json file like:
{"#odata.context":"https://graph.microsoft.com/v1.0/$metadata#users('hanzla_hawk%40outlook.com')/photo/$entity","#odata.mediaContentType":"image/png","#odata.mediaEtag":"W/\"8050a078da935403cf67163f23f1baace5c7abf3ff784452cb08c38660308a83\"","id":"default","height":256,"width":256}
With this Json, how can I load the image into an image view? I have previous experience with Picasso and other fake apis. But right now I just dont know what should I pass in the Picasso to load image from this json.
I have just make a call to get a profile photo to show on an Android app using Jetpack Compose views in Kotlin. To achieve it I have followed your question and this tutorial:
https://learn.microsoft.com/en-us/graph/tutorials/android
You almost got it. Just add .content() call between .me().photo() and .buildRequest().
This is my code on my project to get the photo content:
// GET /me/photo/$value (logged in user)
fun getUserPhoto(
onGotPhoto: (ImageBitmap) -> Unit,
onGotError: (Exception) -> Unit
) {
mClient!!.me().photo().content().buildRequest()
.async
.thenAccept { inputStream ->
val bitmap = BitmapFactory.decodeStream(inputStream).asImageBitmap()
onGotPhoto.invoke(bitmap)
}
.exceptionally { processError(it, onGotError) }
}
I am building a Kotlin app and am using FirebaseAuth for login. I activated Facebook login via Firebase and it works fine. My problem is that the created Firebase user's photoUrl: (https://graph.facebook.com/<UserId>/picture) is pointing to a tiny version of it's Facebook profile picture, instead of a normal sized one.
I found this solution: https://stackoverflow.com/a/52099896/13674106. The Google part of this answer worked perfectly for me, but in the Facebook case I am now getting a default avatar image (in the right resolution). My code looks like this:
fun handleSignInSucceeded(dataIntent: Intent?) {
val response = IdpResponse.fromResultIntent(dataIntent)
// For new created users, check if a photo is available from the auth provider
if (response?.isNewUser == true) {
// All users have a default FirebaseAuth provider data - we want to check which is the
// other one
currentUser.value?.providerData
?.find { it.providerId != FirebaseAuthProvider.PROVIDER_ID }?.apply {
val photoUrl = when (providerId) {
GoogleAuthProvider.PROVIDER_ID ->
photoUrl.toString().replace("s96-c", "s400-c").toUri()
FacebookAuthProvider.PROVIDER_ID ->
photoUrl.toString().plus("?type=large").toUri()
else -> null
}
photoUrl?.let { updatePhotoUri(it, false) }
}
}
}
fun updatePhotoUri(photoUrl: Uri, uploadToStorage: Boolean = true): Task<Void>? {
val profileUpdates = UserProfileChangeRequest.Builder()
return if (uploadToStorage) ...
else updateProfile(profileUpdates.setPhotoUri(photoUrl).build())
}
private fun updateProfile(profileUpdates: UserProfileChangeRequest): Task<Void>? {
return currentUser.value?.updateProfile(profileUpdates)
?.addOnCompleteListener {
if (it.isSuccessful) {
Log.d(TAG, "User profile updated.")
_currentUser.value = firebaseAuth.currentUser
}
}
}
Where _currentUser is a private MutableLiveData<FirebaseUser?> shadowed by a public LiveData<FirebaseUser?> called user which is binded to my ImageView on the fragment:
<ImageView
android:id="#+id/image_profile_picture"
...
app:userPhotoSrc="#{profileViewModel.user}" />
Where userPhotoSrc is implemented by the following BindingAdapter using Glide:
#BindingAdapter("userPhotoSrc")
fun ImageView.setUserPhotoSrc(user: FirebaseUser?) {
Glide.with(context)
.load(user?.photoUrl)
.circleCrop()
.fallback(R.drawable.ic_profile_black_24)
.into(this)
}
I checked in debug and the value of user?.photoUrl at that point is https://graph.facebook.com/<UserId>/picture?type=large as expected.
I found this thread: Retrieving Default Image All Url Profile Picture from Facebook Graph API, Which shows a problem similar to mine, but according to the answers there, my code should work by now. I also tried to use the height parameter instead of type, like stated in this answer: https://stackoverflow.com/a/50710161/13674106, But I got the same result.
Please help me solve it,
Omer
Im currently having an issue where after I fetch data from my api, the view still seems to think the data is null. Please see below code:
View:
#if (goals == null)
{
<Loader />
}
else if (goals.Count == 0)
{
<div class="center-text">
<h2>It doesn't look like you have any goals create yet!</h2>
</div>
}
else
{
<h1>found stuff!</h1>
}
#functions {
List<GoalViewModel> goals;
protected override async Task OnInitAsync()
{
var token = await LocalStorage.GetItem<string>("AuthToken");
var httpClient = HttpClientExtensions.GetAuthHttpClient(token);
goals = await httpClient.GetJsonAsync<List<GoalViewModel>>(ClientSettings.GetHostRoot() + "/api/Goals/");
}
}
the controller action being hit looks like the following (I've verified that it is being called correctly and is returning data via the chrome network tab and fiddler):
[HttpGet]
public IActionResult Get()
{
try
{
var goalIndexDtos = _goalManager.GetAll(_claimResolver.User.FindFirstValue(ClaimTypes.NameIdentifier));
var goalViewModels = Mapper.Map<List<GoalViewModel>>(goalIndexDtos);
return Ok(goalViewModels);
}
catch (Exception ex)
{
log.Error(FormatError(nameof(GoalsController), nameof(Get), ex));
return BadRequest(ex);
}
}
My issue is that the <Loader /> always displays whereas I should be seeing a <h1>found stuff!</h1> since my controller is returning data appropriately and the blazor view code is successfully deserializing the data.
What am I missing here? I'm doing very similar things in other places in my application and they seem to work fine. I've compared this use case to the others over and over and don't see anything different.
I've even stuck a Console.WriteLine(goals.Count); line after the call to retrieve goals from the server and it correctly prints 1 to the console.
UPDATE: after playing around with everything I could think of, I deleted all data from the db table that goals is being retrieved from. this returns an empty list from my controller. In this case <h2>It doesn't look like you have any goals create yet!</h2> does print as expected! HOWEVER, the second I add a row and a list of count 1 is returned, all I get is a loader
SOLVED: After cleaning, building, rebuilding and closing/opening visual studio multiple times, my issue remained. It was only after restarting my machine that this began to work as expected.
So I'm using the Parse component from the Xamarin store in my MonoDroid app. So I was able to use the following code to store an object
ParseClient.Initialize ("appid", "windowskey");
var obj = new ParseObject("Note");
obj ["text"] = "Hello, world! This is a Xamarin app using Parse!";
obj ["tags"] = new List<string> {"welcome", "xamarin", "parse"};
obj.SaveAsync ();
My real goal is to be able to do push notifications. Even though the above object stored, Parse did not register the device in the installations to be able to send push notifications. What else am I missing. Note: I'm doing this in the emulator but if i'm not mistaken it still should work.
#basit-zia, yes I did! I had to create a binding for the push library from the Java Parse SDK. I believe I was able to strip away all the libraries except for the necessary elements. I can't remember exactly what I did though.
Then in the Main Activity class, I put the following into the OnStart() method:
// check for a notification
if (Intent != null)
try {
string jsonString = Intent.Extras.GetString("com.parse.Data");
PushObject jsonObj = JsonConvert.DeserializeObject<PushObject>(jsonString);
if (jsonObj.alert != null) {
Toast.MakeText (BaseContext, jsonObj.alert, ToastLength.Long).Show ();
}
} catch (Exception e) {
Console.WriteLine ("JSONException: " + e.Message);
}
And put the following into the OnCreate() method:
Com.Parse.Parse.Initialize(this, "app id here"}, "client key here");
PushService.SetDefaultPushCallback (this, this.Class);
PushService.StartServiceIfRequired (this);
ParseInstallation.CurrentInstallation.SaveInBackground ();
I'm simply trying to attach a file named Document.pdf in the DocumentsLibrary to an email using the Share Charm. My code below works perfectly on the Local Machine:
private async void OnDataRequestedFiles(DataTransferManager sender, DataRequestedEventArgs e)
{
List<IStorageItem> shares = new List<IStorageItem>();
StorageFile filetoShare = await Windows.Storage.KnownFolders.DocumentsLibrary.GetFileAsync("Document.pdf");
if (filetoShare != null)
{
shares.Add(filetoShare);
filetoShare = null;
}
if (shares != null)
{
DataPackage requestData = e.Request.Data;
requestData.Properties.Title = "Title";
requestData.Properties.Description = "Description"; // The description is optional.
requestData.SetStorageItems(shares);
shares = null;
}
else
{
e.Request.FailWithDisplayText("File not Found.");
}
}
But when I run the exact same code on a Windows Surface Tablet, I get the dreaded "There's nothing to share right now." on the right in the Charms flyout area.
Here's a little more background to help:
I'm not looking to use a File Picker...I know the exact file I'm looking for
I've enabled the Documents Library Capability in the manifest
I've added a File Type Association for pdf in the manifest
and yes, the file does exist and is in the Documents Library
an email account is properly setup in the Mail App on the surface
I can successfully send text emails from the Tablet...just not emails with attachments
Like I said, this works on my Win 8 Development Machine as expected...just not on the Surface. I'm wondering if the Surface has different file or folder permissions?
Thanks for the help...this is driving me CRAZY
I finally figured it out - the problem was that my Event Handler was async (so that I could use await to set the StorageFile variable).
I solved it by setting the StorageFile variable earlier in my code so that it was already available when the Event Handler was called.
I still have no idea why it worked on my development machine, but no on the WinRT surface...
The handler can be an async method. In this case, it is critical to use DataTransferManager. Please refer to the MSDN page specifically for this scenario. For your convenience, the code from the page is copied to here:
private void RegisterForShare()
{
DataTransferManager dataTransferManager = DataTransferManager.GetForCurrentView();
dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager,
DataRequestedEventArgs>(this.ShareStorageItemsHandler);
}
private async void ShareStorageItemsHandler(DataTransferManager sender,
DataRequestedEventArgs e)
{
DataRequest request = e.Request;
request.Data.Properties.Title = "Share StorageItems Example";
request.Data.Properties.Description = "Demonstrates how to share files.";
// Because we are making async calls in the DataRequested event handler,
// we need to get the deferral first.
DataRequestDeferral deferral = request.GetDeferral();
// Make sure we always call Complete on the deferral.
try
{
StorageFile logoFile =
await Package.Current.InstalledLocation.GetFileAsync("Assets\\Logo.png");
List<IStorageItem> storageItems = new List<IStorageItem>();
storageItems.Add(logoFile);
request.Data.SetStorageItems(storageItems);
}
finally
{
deferral.Complete();
}
}
It is critical to place the following statement before any async method is called:
DataTransferManager dataTransferManager = DataTransferManager.GetForCurrentView();
You only have half a second to get the whole job done (getting the file, attaching...etc.). If the half-second deadline occurs you'll get this "driving crazy" message. Consider implementing some resumable logic and replace the message with "the attachment is being prepared please try again in a few seconds" (or else).
Your WinRT device might be just slower than your development machine. The latter just does the job before the deadline...