My users upload some images to the FileStorage at Backendless.
This is the upload sequence:
Backendless.Files.Android.upload(image1_scaled, Bitmap.CompressFormat.PNG,
100, "profileImage", "images", new AsyncCallback<BackendlessFile>() {
#Override
public void handleResponse(BackendlessFile response) {
fileMapping.profile_url = response.getFileURL();
Backendless.Data.of(FileMapping.class).save(fileMapping,
new AsyncCallback<FileMapping>() {
#Override
public void handleResponse(FileMapping response) {
toast_error("Image stored");
}
#Override
public void handleFault(BackendlessFault fault) {
System.out.println("ERROR" + fault.getCode());
}
});
}
#Override
public void handleFault(BackendlessFault fault) {
}
});
And that works flawlessly. Now I need to fetch back the image with the API to display it.
So I need to make a BackendlessCollection<FileMapping> userFiles = Backendless.Data.of(FileMapping.class) call to receive the URL back from that table. And then supposedly do a httpRequest with the url to get back the byte data.
What I can't work out is what sort of .find method to use? Do I .findById() ? And if so, what ID do I use? The "path", "name" ,"table" etc?
Could anyone show an example fitting my case here, with a table storing the url's and such?
Thanks.
You'd this something like this (showing sync call for simplicity, but make sure to change it to Async on Android):
BackendlessCollection<FileMapping> fileMappings;
fileMappings = Backendless.Data.of( FileMapping.class ).find();
Iterator<FileMapping> iterator = fileMappings.getCurrentPage().iterator();
while( iterator.hasNext() )
{
FileMapping fileMapping = iterator.next();
Log.i( "MyApp", "file URL is " + fileMapping.profile_url );
}
Related
I have a problem to understand a chained "RXJava-Retrofit" API call. I got inspired by this and implement this class named ObservationLoader to load the data from the API bucket per bucket. When the end of data is reached the API sends a endOfRecords=true:
public Observable<PageObject<Observation>> getAllObservationDataByRegion(long taxonKey,
String regionId) {
final PublishSubject<PageObject<Observation>> subject = PublishSubject.create();
return subject.doOnSubscribe(disposable -> {
this.getData(taxonKey, regionId, 0).subscribe(subject);
})
.doOnNext(observationPageObject -> {
if (observationPageObject.isEndOfRecords()) {
// -> list is completely loaded
subject.onComplete();
} else {
int nextOffset = observationPageObject.getOffset() + 1;
this.getData(taxonKey, regionId, null, nextOffset).subscribe(subject);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
private Observable<PageObject<Observation>> getData(long id,
String regionId,
int offset) {
// Get your API response value
return this.api.getObservations(id, regionId, ObservationLoader.PAGE_LIMIT, offset);
}
In my Android fragment HomeFragment I subscribe to the ObservationLoader:
ObservationLoader loader = new ObservationLoader(this.getApi());
Observable<PageObject<Observation>> observable = loader
.getAllObservationDataByRegion(this.getSelectedSpecies(), this.getSelectedRegion());
observable.subscribe(new Observer<PageObject<Observation>>() {
#Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "ON_SUBSCRIBE");
}
#Override
public void onNext(PageObject<Observation> observationPageObject) {
Log.i(TAG, "ON_NEXT");
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "ERROR = " + e.getMessage());
}
#Override
public void onComplete() {
Log.i(TAG, "COMPLETED");
}
});
I can see that the onSubscribe() and doOnSubscribe() are called and even the getData() is reached. I assume the API is responding correctly (a previous attempt attempt with recursion worked fine). But I never reached the doOnNext function. The observer goes straight to onComplete() and no data is received. What could be the reason?
When doOnSubscribe runs, the doesn't see any consumers yet so if getData is synchronous, there won't be any first results to trigger further results. Also if getData ends, it will complete the setup so the next getData call in doOnNext will push to an already terminated subject, ingoring all data.
You'll need a differently organized feedback loop:
// we loop back the nextOffset, in a thread-safe manner
Subject<Integer> subject = PublishSubject.<Integer>create()
.toSerialized();
// bootstrap with 0 and keep open for more offsets
subject.mergeWith(Observable.just(0))
// get the data for the current offset
.concatMap(nextOffset -> getData(taxonKey, regionId, nextOffset)
.subscribeOn(Schedulers.io())
)
// if the response is end of records, stop
.takeWhile(observationPageObject -> !observationPageObject.isEndOfRecords())
// otherwise not end of records, feedback the new offset
.doOnNext(observationPageObject ->
subject.onNext(observationPageObject.getOffset() + 1)
)
// get the data on the main thread
.observeOn(AndroidSchedulers.mainThread());
my first question is here
however since I was advised that questions should not change the original matter I created a new one.
I am saving user settings and I would like to save it in the list, I have had a look on setting by James however I found that that its not possible to save it in the list. So ia have decided to use Xamarin Essentials.
First I tried to save only a string value, which after some struggle I managed to work out and now I am trying to save an object
static void AddToList(SettingField text)
{
var savedList = new List<SettingField>(Preference.SavedList);
savedList.Add(text);
Preference.SavedList = savedList;
}
private void ExecuteMultiPageCommand(bool value)
{
var recognitionProviderSettings = new RecognitionProviderSettings
{SettingFields = new List<SettingField>()};
var set = new SettingField()
{
ProviderSettingId = "test",
Value = "test"
};
AddToList(set);
NotifyPropertyChanged("IsMultiPage");
}
and then the sterilization and des
public static class Preference
{
private static SettingField _settingField;
public static List<SettingField> SavedList
{
get
{
//var savedList = Deserialize<List<string>>(Preferences.Get(nameof(SavedList), "tesr"));
var savedList = Newtonsoft.Json.JsonConvert.DeserializeObject<SettingField>(Preferences.Get(nameof(SavedList), _settingField)) ;
SavedList.Add(savedList);
return SavedList ?? new List<SettingField>();
}
set
{
var serializedList = Serialize(value);
Preferences.Set(nameof(SavedList), serializedList);
}
}
static T Deserialize<T>(string serializedObject) => JsonConvert.DeserializeObject<T>(serializedObject);
static string Serialize<T>(T objectToSerialize) => JsonConvert.SerializeObject(objectToSerialize);
}
}
But Preferences.Get doesn't take object, is there any other way how can I save my setting to a object list? Please advise
I would recommend you to use SecureStorage. You can save your strings only into it. So the place where you have serilized your object as json. Just convert your json to string with .ToString() and save it into secure storage.
You may continue saving your serialized json object as string in Shared preferences but it is recommended to use SecureStorage Instead.
Can the links for Firebase deep links be shortened? Do they have that feature? Links generated are too long and that is not good.
UPDATE
Firebase now supports shorten dynamic links programmatically.
I've faced the same problem getting a long and not user friendly URL when creating a dynamic link programmatically.
The solution I've found is to use the Google URL Shortener API that works brilliant.
That link points to the Java library, I'm using it in Android, but you can do also a simple http request.
I'll post my Android code in case you need it:
private void createDynamicLink() {
// 1. Create the dynamic link as usual
String packageName = getApplicationContext().getPackageName();
String deepLink = "YOUR DEEPLINK";
Uri.Builder builder = new Uri.Builder()
.scheme("https")
.authority(YOUR_DL_DOMAIN)
.path("/")
.appendQueryParameter("link", deepLink)
.appendQueryParameter("apn", packageName);
final Uri uri = builder.build();
//2. Create a shorten URL from the dynamic link created.
Urlshortener.Builder builderShortener = new Urlshortener.Builder (AndroidHttp.newCompatibleTransport(), AndroidJsonFactory.getDefaultInstance(), null);
final Urlshortener urlshortener = builderShortener.build();
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
Url url = new Url();
url.setLongUrl(uri.toString());
try {
Urlshortener.Url.Insert insert=urlshortener.url().insert(url);
insert.setKey("YOUR_API_KEY");
url = insert.execute();
Log.e("url.getId()", url.getId());
return url.getId();
} catch (IOException e) {
e.printStackTrace();
return uri.toString();
}
}
#Override
protected void onPostExecute(String dynamicLink) {
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.share_subject));
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, dynamicLink);
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.share_using)));
Log.e("dynamicLink", dynamicLink);
}
}.execute(null, null, null);
}
Hope it helps!!
The links can be shortened in the Firebase console in the Dynamic Links tab. Tap on 'New Dynamic Link', which gives you an option to create a short link from an existing link.
This can be done programmatically using the Firebase Dynamic Links REST API, for example:
POST https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=api_key
Content-Type: application/json
{
"longDynamicLink": "https://abc123.app.goo.gl/?link=https://example.com/&apn=com.example.android&ibi=com.example.ios"
}
See https://firebase.google.com/docs/dynamic-links/short-links
And I just had to code it for Android - here's the code in case it helps someone:
at the top of the activity:
lateinit private var dynamicLinkApi: FbDynamicLinkApi
private var remoteCallSub: Subscription? = null // in case activity is destroyed after remote call made
in onCreate (not really but to keep it simple, actually you should inject it):
val BASE_URL = "https://firebasedynamiclinks.googleapis.com/"
val retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.build()
dynamicLinkApi = retrofit.create(FbDynamicLinkApi::class.java)
then when it's time to shorten a URL:
remoteCallSub = dynamicLinkApi.shortenUrl(getString(R.string.fbWebApiKey), UrlRo(dynamicLink))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ url -> Log.d("Shortened: $url") },
{ e -> Log.e("APP","Error with dynamic link REST api", e) })
Don't forget to unsubscribe in onDestroy:
override fun onDestroy() {
super.onDestroy()
remoteCallSub?.unsubscribe()
}
And here's the classes you need for the dynamic API:
interface FbDynamicLinkApi {
#POST("v1/shortLinks")
fun shortenUrl(#Query("key") key: String, #Body urlRo: UrlRo): Observable<ShortUrlRo>
}
data class UrlRo(val longDynamicLink: String, val suffix: SuffixRo = SuffixRo())
data class SuffixRo(val option: String = "UNGUESSABLE")
data class ShortUrlRo(val shortLink: String, val warnings: List<WarningRo>, val previewLink: String)
data class WarningRo(val warningCode: String, val warningMessage: String)
This cannot be done programmatically as of the moment.
Try This , its working fine in my case,
https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=[api-key]
{
"dynamicLinkInfo": {
"dynamicLinkDomain": "peg3z.app.goo.gl",
"link": "[Your Long Url Which you want to make short]",
"androidInfo": {
"androidPackageName": "com.xyz"//
},
"iosInfo": {
"iosBundleId": "com.dci.xyz"
}
}
}
'Content-Type: text/plain'
I want get text string shown in a textview in LinearLayout. can espresso do that? If not, is there other way to do that or can I use android api in espresso test case? I am using API 17 18 or newer, espresso 1.1(It should be the latest one.). I have no clue about this. Thanks.
The basic idea is to use a method with an internal ViewAction that retrieves the text in its perform method. Anonymous classes can only access final fields, so we cannot just let it set a local variable of getText(), but instead an array of String is used to get the string out of the ViewAction.
String getText(final Matcher<View> matcher) {
final String[] stringHolder = { null };
onView(matcher).perform(new ViewAction() {
#Override
public Matcher<View> getConstraints() {
return isAssignableFrom(TextView.class);
}
#Override
public String getDescription() {
return "getting text from a TextView";
}
#Override
public void perform(UiController uiController, View view) {
TextView tv = (TextView)view; //Save, because of check in getConstraints()
stringHolder[0] = tv.getText().toString();
}
});
return stringHolder[0];
}
Note: This kind of view data retrievers should be used with care. If you are constantly finding yourself writing this kind of methods, there is a good chance, you're doing something wrong from the get go. Also don't ever access the View outside of a ViewAssertion or ViewAction, because only there it is made sure, that interaction is safe, as it is run from UI thread, and before execution it is checked, that no other interactions meddle.
If you want to check text value with another text, you can create Matcher. You can see my code to create your own method:
public static Matcher<View> checkConversion(final float value){
return new TypeSafeMatcher<View>() {
#Override
protected boolean matchesSafely(View item) {
if(!(item instanceof TextView)) return false;
float convertedValue = Float.valueOf(((TextView) item).getText().toString());
float delta = Math.abs(convertedValue - value);
return delta < 0.005f;
}
#Override
public void describeTo(Description description) {
description.appendText("Value expected is wrong");
}
};
}
I need to execute Show created file name each time Watch files fires an event
WatchFilesActivity : NativeActivity
protected override void Execute(NativeActivityContext context)
{
var fileSystemWatcher = new FileSystemWatcher(context.GetValue(Path));
fileSystemWatcher.IncludeSubdirectories = context.GetValue(IncludeSubdirectories);
fileSystemWatcher.Filter = context.GetValue(Filter);
var bookmark = context.CreateBookmark(ResumeFileCreatedBookmark);
context.GetExtension<FileSystemWatcherExtension>().Start(fileSystemWatcher, bookmark);
}
Extension
public class FileSystemWatcherExtension : IWorkflowInstanceExtension
{
WorkflowInstanceProxy instance;
Bookmark bookmark;
public void SetInstance(WorkflowInstanceProxy instance)
{
this.instance = instance;
}
IEnumerable<object> IWorkflowInstanceExtension.GetAdditionalExtensions()
{
yield break;
}
public void Start(FileSystemWatcher fileSystemWatcher, Bookmark bookmark)
{
this.bookmark = bookmark;
fileSystemWatcher.Created += new FileSystemEventHandler(FileCreated);
fileSystemWatcher.EnableRaisingEvents = true;
}
void FileCreated(object sender, FileSystemEventArgs e)//When the file arrives
{
instance.BeginResumeBookmark(bookmark, e.FullPath, CompleteResume, null);
}
void CompleteResume(IAsyncResult ar)
{
var result = instance.EndResumeBookmark(ar);
}
}
This is working great, but only once, and after this the host closes.
I can't put a WhileActivity because I need to handle consecutive very fast file creations and the processing time of an incoming file(Show created file name, in this case) is greater than the file creation rate
Thanks!
For a starter I would make the Show created file name activity a child activity of the Watch files activity so it can control it's execution. Next I would add a bookmark resumption callback so Watch files activity can react to and schedule the Show created file name activity in the callback.
Optionally you might want to create your bookmark with BookmarkOptions.MultipleResume so you can handle as many file events as you want and only move on when you want to do so.
public class WatchFilesActivity : NativeActivity
{
public Activity Child {get; set;}
protected override void Execute(NativeActivityContext context)
{
var fileSystemWatcher = new FileSystemWatcher(context.GetValue(Path));
fileSystemWatcher.IncludeSubdirectories = context.GetValue(IncludeSubdirectories);
fileSystemWatcher.Filter = context.GetValue(Filter);
var bookmark = context.CreateBookmark(ResumeFileCreatedBookmark, OnFileCreated, BookmarkOptions.MultipleResume);
context.GetExtension<FileSystemWatcherExtension>().Start(fileSystemWatcher, bookmark);
}
protected void OnFileCreated(NativeActivityContext context, Bookmark bookmark, object value)
{
var fileName = (string)value
context.ScheduleActivity(Child);
}
}
Note: Code typed in Notepad so possible typos.
If you need to pass the file name on to the child activity you can use an ActivityAction to do just that. See here for an example using an ActivityFunc which is just like an ActivityAction except it also has a return value.