How do I implement a PARTIAL_WAKE_LOCK in Sketchware so that my timer sends SMS even when the screen is off - wakelock

Hello dear community I am teaching myself app programming via Sketchware.
I'm writing a GPS tracker that does the following.
The user sets a time after which the location data is sent as an SMS. Ex: send location data every 60 minutes.
The battery state of charge is constantly monitored and at 30 percent it sends an SMS to the user to draw attention to it.
My problem now is that no SMS is sent when the screen is off. However, the app does not have to be visible. I read a lot of reports about power management and try to get a PARTIAL_WAKE_LOCK all the time. No matter how I try, it always ends in an error. I would be very happy to get help with my problem.
private void initializeLogic() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DoNotSleep");
wl.acquire();
setBG = 0;
VideoView.setVisibility(View.GONE);
setTitle("GPS - Tracker BETA BUILD");
ArabWare = ArabWare.getDefault();
if (geoData.getString("setFirstRun", "").equals("")) {
_setVarDefault();
}
else {
if (geoData.getString("setFirstRun", "").equals("1")) {
_setSplash();
_getStats();
_batState();
_lfzV14();
_testInfo();
}
}
geoData.edit().putString("setFinish", "0").commit();
}
It is not possible for me to implement the PowerManager in the onCreate in Sketchware. Even if I edit the code manually, it always ends up in private void initializeLogic() I'm not getting a wake lock. This instruction PowerManager.WakeLock wl; always leads to an error.
wl cannot be resolved to a variable

Related

Google scripts moving events between calendars: Calendar.Events.move can't find event

[NOTE: updated to better reflect understanding of the issue]
I am trying to move all events from one Google calendar to another using Google Scripts.
I have written the following code:
function move_all_events()
{
var fromYear = 2016;
var toYear=2020
var fromDate = new Date(fromYear,0,1,0,0,0); //Year, Month (where 0 = Jan, 1 = Feb...), Hour, Min, Sec, Millisecond
var toDate = new Date(toYear,1,0,0,0);
var fromCalendarName = 'Calendar1';
var toCalendarName = 'Calendar2';
// Move from start of fromYear to start of toYear (for month 0 = Jan, 1 = Feb...)
var fromCalendar = CalendarApp.getCalendarsByName(fromCalendarName)[0];
var toCalendar = CalendarApp.getCalendarsByName(toCalendarName)[0];
var fromCalendarId = fromCalendar.getId();
var toCalendarId = toCalendar.getId();
Logger.log(fromCalendarId);
Logger.log(toCalendarId);
var events = fromCalendar.getEvents(fromDate, toDate);
for(var i=0; i<events.length;i++){
var evId = events[i].getId().replace("#google.com", "");
Logger.log(evId); // show event Id in log
Calendar.Events.move(fromCalendarId, evId, toCalendarId);
}
}
The above code WORKS fine when the events to-be-moved have been created manually from within Google Calendar.
However, the code FAILS when the events to-be-moved were created externally in an ical/ics file and then IMPORTED into Google calendar.
When I run the code, I get the following error:
API call to calendar.events.move failed with error: Not Found (line 86, file "Code")
where line 86 is the line with Calendar.Events.move.
Note the log shows:
[20-04-24 01:26:15:031 EDT] myname#gmail.com
[20-04-24 01:26:15:032 EDT] asfdsfdsfdsfekwlkkllkpauc700#group.calendar.google.com
[20-04-24 01:26:15:986 EDT] myeventidprefix-a3a597f53ff059959e950fc513df33af
All seems "correct" and indeed the event Id is exactly the UID that I created externally in the ics file that I then imported.
Yet somehow, it seems that Calendar.Events.move is not able to find the event itself in the fromCalendar.
Any idea what is going wrong here?
A SECOND problem that I noted is that even when the routine works (i.e. for events created within Google Calendar), the 'notifications' are not moved. I.e., if I create an event in fromCalendar with notifications, the moved events in toCalendar have lost the notification. Note that neither calendar has Event or 'All Day' notifications turned-on generally.
Interestingly, if I move that same event back to fromCalendar, the notifications magically reappear.
So any idea why the notifications are seemingly still internally intact but are not visible along with the other event data
OK - I solved the first part.
The problem is that for imported iCal events, the iCal UID is not the same as the Google event ID. In particular, .getId() returns the iCal UID, while the function Calendar.Events.move needs the Google event ID which can be retrieved via: Calendar.Events.list(fromCalendarId).items
See: How can I find the Event Id of my Google Calendar event?
This of course is not an issue for native events since the ID's are the same which explains why my original code worked for native events.
Revised code is as follows:
for(var i=0; i<events.length; i++){
var evId = events[i].id;
Logger.log(i + ": " + evId); // show event Id in log
Calendar.Events.move(fromCalendarId, evId, toCalendarId); //Note need to turn on Calendar API under Resources
}
This however moves some but not all of the events which is weird... and I don't see any pattern... Indeed, among many similar events some are moved and others aren't.
(Also, as noted above, the notifications don't move over as mentioned before.)

Microsoft Teams incoming call event

I would like to create a app which will be informed when a user gets a call in MS Teams. I mean I want to subscribe something on event of incoming call and then do something based on information of incoming call. Is this possible? So far I do not see any events in SDK.
There seems to now be a feature that may suit this.
Call records to provide usage and diagnostic information about the calls
and online meetings that occur within your organization when using
Microsoft Teams or Skype for Business.
...
Using webhooks and the MS Graph Subscription API, you can receive a continuous feed of call records as
they are created.
I investigated this issue for three days or so. These are my findings:
The MS Graph API is too slow. See https://learn.microsoft.com/en-us/graph/webhooks#latency. callRecord Notifications have a guaranteed latency of <60 minutes. Also, callRecords are only created after the call has finished, so they're useless for incoming calls.
I didn't want to write a MS Teams bot for this. I don't want my code to sit between each and every call just to get some information. Also, I think that bots would not work for calling a user's personal number, only for service accounts (Call queues / Auto Attendants).
The MS Teams client (I only checked on Windows) does not write the phone number into any file before the call is answered. By watching storage.json, you can figure out more or less reliable whether the phone is currently ringing, but without the calling number.
The indexedDB cache files eventually contain the calling number, but only once the call is answered. Also, I didn't find a library to read IndexedDB files that are on disk.
I did not find any third party software that could do this. Some paid Team apps can do this for calls to service accounts (e.g. Landis Contact Center)
The only thing I could come up with was to read the text out of the notification window itself. After a lot of trial, error and pain, I managed to get this:
//needs a COM reference to UIAutomationClient
//using UIAutomationClient;
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
internal static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
public void GetTeamsCallNotifications() {
do {
var teamsNotificationWindowHandle = FindWindowByCaption(IntPtr.Zero, "Microsoft Teams Notification");
try {
var pUIAutomation = new CUIAutomation();
var windowElement = pUIAutomation.ElementFromHandle(teamsNotificationWindowHandle);
var trueCond = pUIAutomation.CreateTrueCondition();
while (IsWindow(teamsNotificationWindowHandle)) {
//incoming call window has: two buttons (type 50000), a ISO phone number in a group-field (50026), and no text field (50020)
if (IsWindowVisible(teamsNotificationWindowHandle)) {
var elementArray = windowElement.FindAll(TreeScope.TreeScope_Descendants, trueCond);
string number = "";
int noButtonsFound = 0;
var debugFields = new List<string>();
for (int i = 0; i < elementArray.Length; i++)
{
var element = elementArray.GetElement(i);
debugFields.Add($"{element.CurrentControlType}={element.CurrentName}");
if (element.CurrentControlType == 50000)
noButtonsFound++;
if (element.CurrentControlType == 50026 && System.Text.RegularExpressions.Regex.IsMatch(element.CurrentName, #"^\+[1-9][0-9 ]+$"))
number = element.CurrentName.Replace(" ", "");
}
Debug.WriteLine(string.Join(";", debugFields) + "\r\n");
if (noButtonsFound == 2 && !string.IsNullOrEmpty(number))
Console.WriteLine(number + " is ringing");
}
Thread.Sleep(500);
}
}
catch { }
Thread.Sleep(5000); //Teams is probably closed, need a new window handle
} while (true);
}
Some comments:
The Teams Notification Window always exists when MS Teams runs, it's just either hidden or visible
There only ever exists one Teams Notification Window, even when multiple notifications are shown. windowElement.FindAll will give you all the elements of all notifications.
The code is pretty light-weight
Limitations of this code:
Windows only, not centralized (i.e. needs to run on every client)
A change in the layout of MS Teams could break it
It's unknown whether the call was answered or not.
A second notification of any kind will break it temporarily (until the second notification disappears).
You can improve on the last limitation if you're willing to accept other limitation. For instance, you can just search all text fields for a phone number. But then the code will trigger if someone sends you a text message containing a phone number. Or you can find the call notification pretty reliably if you know the display language of the Teams client by looking at the caption of the answer / decline buttons.

Google Cast custom receiver timing out

Using the Google CAF Receiver SDK, how do we prevent the receiver from timing out and automatically killing the cast session when we're not using the receiver player?
The standard Google Cast use case is to send media from a device to the cast receiver and have the receiver render the media using a player. The CAF receiver SDK provides this functionality in a beautiful, simple way using the element cast-media-player.
But for those instances when we want to cast from a device and render content where it's not relevant to use the cast-media-player (e.g. an HTML dashboard), how do we keep the receiver alive?
The following custom receiver for example (HAML for brevity), results in the cast session automatically terminating after 5 minutes...
!!! 5
%html
%head
:css
cast-media-player {
display: none;
}
= javascript_include_tag 'https://www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js'
%body
%cast-media-player
:javascript
const context = cast.framework.CastReceiverContext.getInstance();
const player = context.getPlayerManager();
player.setMessageInterceptor(cast.framework.messages.MessageType.LOAD, loadRequestData => {
...[load custom view]...
return false;
});
context.start();
The receiver log shows the line cast.framework.common.IdleTimeoutManager] timer expired and then shuts down. Example receiver log shown here.
I've tried:
Increasing cast.framework.CastReceiverOptions#maxInactivity to a very large number
Periodically loading new data from the sender
Periodically sending custom messages from the receiver to the sender
Periodically sending custom messages from the sender to the receiver
Any help is very much appreciated!
I ran into the same problem while developing a custom receiver app that does not play media. Here is the solution I implemented:
var idleTime = 0;
const context = cast.framework.CastReceiverContext.getInstance();
const CUSTOM_CHANNEL = '[MY CHANNEL HERE]';
context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) {
var eventData = customEvent.data;
parseCommand(eventData);
idleTime = 0;
});
const options = new cast.framework.CastReceiverOptions();
options.disableIdleTimeout = true;
context.start(options);
var idleInterval = setInterval(timerIncrement, 60000); // 1 minute
function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime > 4) { // 5 minutes
context.stop();
}
}
With CastReveiverOptions I disable idle timeout, which according to the documentation: "If true, the receiver will not set an idle timeout to close receiver if there is no activity. Should only be used for non media apps."
https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#constructor_1
Since mine is a "non media app," I believe this is correct usage.
I then set my own time out based on 5 minutes of inactivity in my custom channel.
I figured out an alternative way to stop this which is more efficient than periodically sending a silent clip, but it feels dirty. Basically we have to stop Chromecast's setTimeout from firing and closing the connection due to no media. The quickest solution is to simply re-declare setTimeout as a dummy no-op function before loading the Chromecast receiver script. It does not seem to break anything Chromecast-related in this scenario because it looks like Chromecast's timeouts are all related to video which aren't relevant to this use case.
window._setTimeout = window.setTimeout;
window.setTimeout = function(a, b) {
// disable setTimeout so chromecast won't kill us after 5 minutes...
};
Then in our own app if we need to use a timeout we call _setTimeout instead.
I would be interested if anyone has discovered a better way to achieve this, aside from manually hosting cast_receiver_framework.js with the offending line commented out (which is inside the Wn(a, b) function) or sending a silent clip every few minutes. But self-hosting isn't recommended by Google.
A better solution may be to dig deep in the minified code to work out how Xn(a) is called as that disables the timeout whenever media is playing, and then find a way to call that from within the Chromecast app.
Loading a short inaudible audio clip from the sender to the receiver every 4 minutes seems to do the trick. This should not impact performance much if the file is small. Here is some android code.
MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
MediaInfo mediaInfo = new MediaInfo.Builder("https://some-inaudible-clip.mp3")
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("audio/mpeg")
.setMetadata(metadata)
.build();
RemoteMediaClient remoteMediaClient = castSession.getRemoteMediaClient();
remoteMediaClient.load(mediaInfo, true);
It is possible to send a custom namespace message from the receiver to the sender. That should keep the heartbeat live. However, your use case is not directly supported by the Cast SDK, so you would have to experiment on a solution.

WPF writing/confirming write to serial port

I am writing a program which writes to a wireless device on the serial port. The firmware has a feature which will confirm the last message sent to the device was ok, so I'm trying to make use of that and update the settings readout on the GUI rather than ask the device for all of its settings every time I make a change. I think my code will explain it a little better:
// global variable
bool queryStatusOK;
//event handler response from serial port
this.QueryStatusReponseEvent += QueryStatusResponse;
private void QueryStatusResponse(byte[] packet)
{
if (packet[3] == 0) queryStatusOK = true;
else queryStatusOK = false;
}
public void setParameter(string device)
{
//send command to serial port to change a single device parameter
Thread.Sleep(100); //sleep thread so 2 commands are not sent at once
//send command to confirm previous command was received
Thread.Sleep(100); //sleep thread to give time for confirmation to receive
if (queryStatusOK)
{
//update GUI at this point
}
}
The program is not consistent. It works sometimes, but not always. Even if I extend the thread sleep to a full second to give the boolean time to update, it still sometimes will not hit it. Can anyone suggest a better way to do this?
Thanks!
Mike

Android Development: Writing a for loop inside onClick with Intent

I am trying to get an application to run 5 times after the user presses the designated button, but it only runs once and prints out my debugging statement (Log.v) five times.
What is the correct format to do this?
This is what I tried:
Button btnStart = (Button) findViewById(R.id.StartService);
btnStart.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
for (int i = 0; i < 5; i++)
{
Intent intent = new Intent(currentClass.this, different.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(intent);
finish();
Log.v(TAG, "testing");
}
}
});
EDIT:
I tried to make the service do my task five times, but after the first time, I get a java.io.IOException: invalid preview surface. when mMediaRecorder.prepare() is called, and startRecording() is called again.
Your service has not yet had the chance to finish when your for() loop runs five times. You need to implement communication between your UI and the service - let the service send you a message when it's done so that you can call it again (read on service-activity communication here).
Alternatively, modify the service to do whatever it does five times. If your data is dynamic each time you want to run the service, you may have to go with the first approach.

Resources