KMarb
Members-
Content Count
77 -
Joined
-
Last visited
-
Days Won
1
Everything posted by KMarb
-
Thank you for the link. Re: when permissions are requested, it's not that I think I need to request outside the main thread, but the logic makes more sense to me to request the permission within the task that downloads the file. IE, within the task, request permission to save to external storage, then if not granted, save the downloaded file to private storage rather than: request permission to save to external storage start task to download a file. Do you think it's a problem to request permissions outside the main ui?
-
One other question... If I have started a process using TTask.Run, and then within that thread I RequestPermission, then the permission handler is running in the context of the TTask thread, not the main thread, correct?
-
awesome! Thank you. Follow up question: I'm testing on an android 11 device, a uleFone Armor X9. The permission handler shows that both read and write external storage have been granted. I'm getting a permission error when I try to download a file to the shared downloads folder: if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // both are granted... UpdateFileName := extractFNFromURL (UpdateURL); UpdateFullFilename := TPath.Combine(TPath.GetSharedDownloadsPath, UpdateFileName); // I get a permission denied on the following line, not sure if the error is triggered by the fileExists function or request to DeleteFile. if fileExists (UpdateFullFilename) then TFile.Deletefile (UpdateFullFilename); Is this possibly related to android 11 changes with shared storage?
-
I have a TEdit for user to enter a job number. Job numbers vary in format, but one format is yy-xxxx, where yy is the year, so 22-1437 is a valid job number. The popup keyboard on android sometimes does not list a minus sign (dash) when entering numbers. In the past I programmed the edit control to take any of these characters (comma, *, #, space) and to convert them to a dash on the fly. Ideally I could do this in KeyUp or KeyDown or KeyPress (if it existed in FMX but it does not), and just swap characters. I haven't been able to make that work for some reason, so I'm resorting to the OnChangeTracking event. My code is below. IT is way longer than I would need if I could intercept and change character in KeyUp or KeyDown. The problem I have is, say user types 22#. # is replaced with -, so TEdit now holds 22- and the caret is flashing after the -. When user types the next number, it appears at the START of the edit field, not after the - as expected. On screen the caret is where it should be right up until I type the number after the -. This is one of those that I've spent a couple hours on and it should be trivial. Maybe it is. Please advise. procedure TfrmMain.edtJobChangeTracking(Sender: TObject); var s : string; i1, i2, i3, i4, imax : integer; iTotal : integer; savePos : integer; saveEvent : TNotifyEvent; begin saveEvent := TEdit (sender).OnChangeTracking; TEdit (sender).OnChangeTracking := nil; try savePos := TEdit (sender).CaretPosition; s := TEdit (sender).Text; i1 := s.IndexOf (' '); i2 := s.IndexOf ('#'); i3 := s.IndexOf ('*'); i4 := s.IndexOf (','); iTotal := i1 + i2 + i3 + i4 + 4; // -1 from indexOf means not found if i1 >= 0 then s := s.Replace (' ', '-', [rfReplaceAll]); if i2 >= 0 then s := s.Replace ('#', '-', [rfReplaceAll]); if i3 >= 0 then s := s.Replace ('*', '-', [rfReplaceAll]); if i4 >= 0 then s := s.Replace (',', '-', [rfReplaceAll]); if iTotal > 0 then TEdit (sender).Text := s; RunRefSearch; // this is the query in database to see if we found a match finally if iTotal > 0 then begin // 9/17/22 - cannot get this to work right... // user types 22#... display shows 22- which is what I want // but next character appears at START of edit field, not after the - if (savePos < 0) or (savePos = s.Length) then TEdit (sender).GoToTextEnd else TEdit (sender).CaretPosition := savePos; TEdit (sender).SelStart := TEdit (sender).CaretPosition; TEdit (sender).SelLength := 0; end; TEdit (sender).OnChangeTracking := saveEvent; end; end;
-
I've solved my problem by adding a timer that I call in the finally block that does the things listed in the finally. It runs with a 50ms so I don't notice any delays with the screen. This required me to save the current editor and caret position in form variables, and the code is much uglier than I'd like, but I will take working over pretty any time. first two lines of ChangeTracking event: HackEdit := TEdit (sender); // HackEdit is a form var of TEdit HackEditSaveCaretPos := HackEdit.CaretPosition; In the finally I have this now: TEdit (sender).OnChangeTracking := saveEvent; if iTotal > 0 then tmrHackEdit.Enabled := true; and the timer does this: tmrHackEdit.Enabled := false; if assigned (HackEdit) then begin if (HackEditSaveCaretPos >= 0) then HackEdit.CaretPosition := HackEditSaveCaretPos else HackEdit.GoToTextEnd; HackEdit.SelStart := HackEdit.CaretPosition; HackEdit.SelLength := 0; end; All works as it should now.
-
Delphi 11.1, creating android apps In researching other topics I found some discussion that android is a different beast in terms of memory management, and maybe we should no longer call free or freeAndNil? I haven't had an issue until now... The following code blows up: freeAndNil (SyncCDS); freeAndNil (SyncQuery); When earlier in the execution, I called this code: SyncQuery := TFDQuery.Create (nil); SyncQuery.Connection := DMConn.dbcMain; SyncCDS := TClientDataSet.Create (nil); Not sure it matters but this code is running in a TTask.Run procedure. Can someone please explain the (new) mindset around allocating and freeing objects in android please.
-
form1.Invalidate does not update the label while the loop is active. On android, even application.processmessages does not update the UI. Conclusion: If you need to do a second or three of processing on android, and you want to do this in the main form, you cannot update the screen in any way while you do that short processing. Please correct me if that is wrong. I don't need someone to explain that I should do the 1-3 seconds of processing in a thread, I understand that, but sometimes the nature of the processing might make threading quite a bit more work than what would seem necessary.
-
Tried label1.ApplyStyleLookup in the loop, but it does not update the label while loop is running, only when done it changes to "Step 10". This is somewhat academic at this point, for me, because I'm moving my long running code to threads, but so far there doesn't appear to be away to force a label to update within the context of constant main thread processing. for i := 1 to 10 do begin label1.Text := 'Step ' + intToStr (i); label1.ApplyStyleLookup; sleep (200); end;
-
Your answer I think applies to a multi-threaded app, but not one where there is only one main thread. My need is to update the display in the context of a long-running process in the main thread, say 2-3 seconds of processing that must happen in the main thread. I know that is not proper design, and that anything that makes the UI unresponsive should be run outside the main thread, but my question is not about proper design, it is about updating the screen while the main thread is doing some processing. Thanks for your reply, though, always good to see how people solve problems.
-
Not really sure how a timer helps. When the timer fires I would still need a way to say: label1.RepaintYourselfNowAlready; Can you elaborate and explain how a timer fits in with this loop, to make the label update every .2 seconds? for i := 1 to 10 do begin label1.Text := 'Step ' + intToStr (i); // what to do here to make label1 show new value on the screen? sleep (200); end;
-
I have not tried that, but the form does not have a repaint or refresh method. How do you tell the form or any control to repaint now? I tried form6.InvalidateRect(label1.ClipRect); and label1.InvalidateRect(label1.UpdateRect); Neither worked
-
Repaint does not work - I should have said I tested it, sorry. So far, I cannot find a way to update the label in this simple loop. Any other ideas? I am learning TTask.Run and making progress, and it does seem like TThread.Synchronize updates the UI well enough, but I have left an option in the app to not use TTask.Run (especially when I first have users run the new version), so it can run in the same mode (non-threaded) as I originally wrote it 7 years ago. Hence the need to do force a control to update/repaint during the course of other processing. Does anyone know how to force label1 to update in the loop below? for i := 1 to 10 do begin label1.Text := 'Step ' + intToStr (i); // what to do here to make label1 show new value on the screen? sleep (200); end;
-
Tried begin/end update and that did not work: for i := 1 to 10 do begin label1.BeginUpdate; label1.Text := 'Step ' + intToStr (i); label1.EndUpdate; sleep (200); end; Also, .refresh is not valid in fmx.
-
In researching how to get my android app updated from Berlin to Alexandria, I have come across some posts of people saying that using FMX.xxx units is a problem, android apps will not work well or will crash. see this post for example: log.d not working in Android services (Delphi Berlin) - Stack Overflow Can someone comment on that? Was that a problem at one time? Or is it a problem still? If I don't use FMX.xxx units, what should I use instead?
-
I have some database operations that take 10-15 seconds sometimes, and so I'm researching how to create a local service to run those ops in a separate thread. The download service demo seems like a good place to learn, but it will not run on any of my devices. I will keep researching this and add notes here if I find the problem, but can someone enlighten me as to why this demo does not work?
-
Related question: TTask.Run seems like a very simple thing to implement, simpler than local service. And it looks like tasks created with TTask.Run run in a separate thread, so the UI does not get blocked. In my case, where I have some time-consuming things that need to get done (sending and receiving data from back end database), does TTask.Run make more sense than a local service? The big thing I'm unsure of: if my app moves to background, will the task continue to run and complete a 10-15 second transfer?
-
Is it true that using FMX units will cause android apps to fail?
KMarb replied to KMarb's topic in FMX
Thank you. Is your answer that it is impossible (or very difficult) to create a stable android app using Delphi? I have three Berlin-era apps that work well and have for about 7 years (only used within my company), but it is harder and harder to find devices running versions of android that allow the apps to run. So it is time for something different, and the most obvious path forward is to rebuild it in Alexandria. But another option is to start over completely with a different environment (Android Studio maybe) and build from scratch. The second option would be too much for me to do, so I would need to hire someone to do that, and then I would not have the same control over features and updates that I have now. Do you have a recommendation for me for how to move forward? Maybe an android developer who can build fairly simple apps for reasonable cost. Apps have local database with reference tables (lists of vendors, inventory, jobs, etc) that are updated from tables that live on an SQL server, retrieved using a datasnap server, and transaction are submitted back to the datasnap server (work activity, photos for various steps of projects, material pulls from inventory). Developing android apps in Delphi is horrible compared to VCL, but is it doable? -
In prior apps built with Berlin I have gone through some efforts to make sure edit controls were visible to user when the on-screen keyboard pops up. If I recall correctly, I use a scrollbox, sized as if it was align to alClient, but align set to alNone. That allows me to slide the entire scrollbox up if needed when the keyboard appears. I'm just wondering what the current approach is for managing edit fields when they are low enough on form to be covered by the keyboard. Are there any articles I can read for best practices with keyboard but also for delphi android form design in general? Thanks for any pointers you have.
-
Prevent TEdit FMX being overwritten by the virtual keyboard with the floating mode?
KMarb replied to Fabian1648's topic in FMX
Did you ever make progress with this question? I'm wondering what is the best way to ensure the active edit control or memo, whatever is visible to the user when keyboard pops up. I wasn't aware you could make the keyboard float, so making it always float sounds like a possible solution. I am only concerned with android at this point. -
In 2015 or so I used Berlin to create some android apps. I'm primarily a Windows developer, so that was a painful process. I need to update those apps so they run on new android versions, but I'm running into several issues. I am happy I found this group, I have found a lot of good advice, but I have a feeling like answers change every couple of years, and so far I have not figured out a few things. I know very little about android development, which is why I struggle, so any direction where I can learn more is much appreciated. First thing is, I want my app to self-update by downloading an apk and running so user can install if wanted. I have async download working, so that is good (threads are also new to me). My code that runs the update is below. I get the following error: Project JCMTime.apk raised exception class EJNIException with message 'android.os.FileUriExposedException: file:///data/user/0/com.hello.world/files/BigTime64-4.1.apk exposed beyond app through Intent.getData()'. Here is one version of the code I've tried: procedure TDMCommon.initiateUpdate; var F: Jfile; Intent: JIntent; begin F := TJfile.JavaClass.init(stringtojstring(UpdateFolder),stringtojstring(UpdateFN)); Intent := TJIntent.Create(); Intent.setAction(TJIntent.JavaClass.ACTION_VIEW); Intent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK); Intent.setDataAndType(TJnet_Uri.JavaClass.fromFile(F), StringToJString('application/vnd.android.package-archive')); TAndroidHelper.Context.startActivity(Intent); end; I've tried adding the below to manifest file: <provider android:name="androidx.core.content.FileProvider" android:authorities="com.hello.world.fileprovider" android:exported="false" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider> with following filepaths.xml file deploying to res\xml\: <paths xmlns:android="http://schemas.android.com/apk/res/android"> <root-path name="root" path="." /> </paths> but I'm not even sure I need those things. Any feedback is appreciated... I think the biggest problems I have are 1) lack of active delphi discussion groups, 2) programming that worked 2-3 years ago no longer works, so even when I find a relevant article/post it doesn't mean hooray, and 3) I am really uneducated about android development. Thanks for any feedback.
-
"Request install packages" was the final piece! saving the apk to app-specific folder works but so does downloading to shared documents folder. I prefer the shared doc folder because user can find those downloads if something goes wrong. With that setting, when the app calls the intent to install update, I get a window that says the app does not have that permission, but a click takes me to the settings where I can turn on the permission, then go back to app and click install. This is a major win for my app! Thank you so much. I will definitely check out Device Lens. Thanks again, happy weekend!
-
Question: I know in recent versions of android, to install an app manually that does not come from play store, you have to enable permissions on the device for the app in question (like Files app, or Opera or whatever) so that you can even install the app. How do I know if my app has permission to install an apk? Does it matter if the apk is in shared downloads or if it is in my sandboxed documents? Also, can my app install any apk, or can I only initiate the install for apk for same app as what I'm running? If this is all explained in android docs please let me know a good starting point. Thank you again.
-
Thank you very much. Progress, but still problems. On my Samsung Galaxy S6 running 64-bit Android 7 - IT WORKS! The "Do you want to install an update..." window pops up like it should. Oddly it is full screen, not just a portion of window like when run manually. On two other 64-bit android 11 devices - same as before. the update prompt window never shows. If I go to shared downloads folder and run the just-downloaded apk manually, it works as expected, so the apk file is downloading correctly. Lastly, on a 32-bit android 9 phone - same as the android 11's - no window pops up. I had only been testing on android 11, but I wanted to test on more devices before reporting back, so it might have worked on the S6 (android 7) with prior versions of code. I'm wondering if since my original work on this was in Berlin if there are app or main form settings that are preventing the intent from running properly on newer versions of android. Can you think of any other things to look at? And a broader question, I see people who write log messages somehow/somewhere which I would think would be a great help sometimes to figure what's wrong. Is there an article that talks about setting that up?
-
Ok. I did that and saw how the new provider_paths.xml was placed in the Android64\Debug folder, and also added to deployment page. Now when I run the app, the call to TAndroidHelper.Context.startActivity(Intent); seems to go to la-la land... No window pops up asking if I want to update, like it would if I ran the apk from the downloads folder manually. I do see my app state go through WillBecomeInactive, then BecameActive, but no prompt to install/update and no error message. Here is an important detail maybe... the apk I'm launching is the same version I'm running... it is not actually an update. Could that be why it doesn't show the install window? Or could it be a permission problem still with launching an apk from my app? Your help is really appreciated.
-
Thanks! I have removed the provider section from manifest. I do have secure file sharing checked in entitlements list. With the change you suggested my error has changed: Project JCMTime.apk raised exception class EJNIException with message 'java.lang.IllegalArgumentException: Couldn't find meta-data for provider with authority com.zunna.JCMTime.fileprovider'. Unless you have a quicker solution I will try the steps laid out here: android - Couldn't find meta-data for provider with authority - Stack Overflow Although... that seems almost like the same xml I had before?