iOS - Call Conference Duration not increase using linphone 4.4.0 - swift

We used linphone for the VoIP app and faced issues in conference call duration. Duration not increased after merge call successfully.
Every time displays 00:00 but the call is generated successfully.We used below code for conference call
LinphoneAddress* linphoneAddress = linphone_core_interpret_url([LinphoneManager
getLc], [username cStringUsingEncoding:[NSString defaultCStringEncoding]]);
if (linphoneAddress == NULL) {
return;
}
linphone_core_enter_conference(LC);
LinphoneCall *call = linphone_core_invite_address(LC, linphoneAddress);
-(NSString *)getCallDuration
{
LinphoneCore *lc = [LinphoneManager getLc];
int duration =
linphone_core_get_current_call(lc) ?
linphone_call_get_duration(linphone_core_get_current_call(lc)) :
0;
NSString *str_duration = [LinphoneUtils
durationToString:duration];
return str_duration;
}
So anyone has any solution then please help us.
Thank you.

Related

FusedLocationProviderClient stop giving locationResult after several LocationRequest

Please help if anybody has idea what happened with this case.
I have BroadcastReceiver which register to ForegroundService. This BroadcastReceiver will listen to Intent.ACTION_SCREEN_ON and called startLocationUpdates() if it receive any.
fun startLocationUpdates(context: Context, entity: LockEntityDB?) {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
mSettingsClient = LocationServices.getSettingsClient(context)
mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
super.onLocationResult(locationResult)
val location = locationResult?.lastLocation
locationResult?.let {
saveLocation(context, it.lastLocation)
locationUpdatesCount(context)
}
//something will do with entityDb
}
}
mOneTimeLocationRequest = LocationRequest().apply {
interval = 5 * 1000
fastestInterval = 1000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
numUpdates = 1
}
val builder = LocationSettingsRequest.Builder()
builder.addLocationRequest(mOneTimeLocationRequest!!)
mLocationSettingsRequest = builder.build()
mSettingsClient
?.checkLocationSettings(mLocationSettingsRequest)
?.addOnSuccessListener {
mFusedLocationClient?.requestLocationUpdates(mOneTimeLocationRequest, mLocationCallback, null)
}
?.addOnFailureListener { e ->
kTimberLog(context, "loc " + e.toString())
}
}
So the idea if user turn on the phone, it will send Intent.ACTION_SCREEN_ON and FusedLocationProviderClient will update actual user location.
The problem is, when user try to turn on the phone after 5-6x FusedLocationProviderClient never give any locationResult anymore.
The more weirdest part all this locationResult for the 6th times and so on will start reappearing when user open the MainActivity.
Additional info : I m testing this code on 3 emulator : Nexus 5 API 23, PIXEL 3 API 29, Galaxy Nexus API 29. It works fine for Nexus 5 API 23.
Anybody know what is the reason of this behavior? Thank you very much
After struggling for few days, I found the solution here :
https://developer.android.com/training/location/request-updates#continue-user-initiated-action
After adding this line into my manifest, everything looks working fine
<service
android:name="MyBackgroundService"
android:foregroundServiceType="location" ... >
...
I hope can help somebody who are stuck in same problem

How to make a live counter in unity3d

I recently started using Unity3D and made a few levels. The only problem I have right now is how can I get a live counter?
So my character dies when he hits and certain object.
I want my character to get 3 lives maximum, and get -1 live when he hits that object.
And that it keeps the data when he dies so you wouldn't get lives back if you restart the app.
And after a certain amount of minutes he gets +1 live.
Thank you :)
While your game running. just create a variable counterTime to count time, whenever counterTime pass certain amount of time you want reset counterTime to 0 and increase your life.
When user quit your app, save last time to PlayerPref, eg:
PlayerPref.SaveString("LastTime", DateTime.Now);
When user comback game, just check duration between last time and now to calculate total life need added. eg:
DateTime lastTime = DateTime.Parse(PlayerPref.GetString("LastTime"));
TimeSpan timeDif= DateTime.Now - lastTime;
int duration = timeDif.TotalSeconds;
You can use PlayerPrefs.SetInt , PlayerPrefs.GetInt for storing and reading your player's hp in file storage. Read more about it here:
https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
As for Giving player +1 hp after a few minutes you can store DateTime.Now in a PlayerPrefs variable whenever you give your player some hp and use TimeSpan and TotalMinutesPassed:
TimeSpan passedTime = DateTime.Now - lastStoredDateTime;
int totalMinutesPassed = passedTime.TotalMinutes;
Should go sth like this i guess(didnt test this code just showing a general idea) :
void SetPlayerLives(int lives)
{
playerLives = lives;
PlayerPrefs.SetInt("player-lives",playerLives);
}
//TODO: also sth like => int GetPlayerLives() function
void CheckLiveRegen() //call this function whenever you want to check live regen:
{
int LIVE_REGEN_MINUTES = 5; //regen 1 live every 5 minutes
DateTime lastStoredDateTime = DateTime.Parse(PlayerPrefs.GetString("last-live-regen", DateTime.Now.ToString()));
TimeSpan passedTime = DateTime.Now - lastStoredDateTime;
double totalMinutesPassed = passedTime.TotalMinutes;
if(totalMinutesPassed >= LIVE_REGEN_MINUTES)
{
int val = (int) totalMinutesPassed / LIVE_REGEN_MINUTES;
// Add val to your player lives! + store new lives value
SetPlayerLives(playerLives+val);
//update last-live-regen value:
PlayerPrefs.SetString("last-live-regen", DateTime.Now.ToString());
}
}
Note: DateTime , TimeSpan classes have some bugs (specially in android platform) in versions older than 2017.4 (LTS) Make sure you log values and check if functions are working properly.
https://forum.unity.com/threads/android-datetime-now-is-wrong.488380/
check out the following link to understand how to create a life counter in unity
http://codesaying.com/life-counter-in-unity/
In order to calculate the time that was lapsed since you last shut down the game, you should save the last time playerprefs in the function OnApplicationPause and calcuate the timelapsed in the Awake Function.
void Awake () {
if(!PlayerPrefs.HasKey("Lives")){
PlayerPrefs.SetString("LifeUpdateTime", DateTime.Now.ToString());
}
lives = PlayerPrefs.GetInt("Lives", maxLives);
//update life counter only if lives are less than maxLives
if (lives < maxLives)
{
float timerToAdd = (float)(System.DateTime.Now - Convert.ToDateTime(PlayerPrefs.GetString("LifeUpdateTime"))).TotalSeconds;
UpdateLives(timerToAdd);
}
}
void UpdateLives(double timerToAdd ){
if (lives < maxLives)
{
int livesToAdd = Mathf.FloorToInt((float)timerToAdd / lifeReplenishTime);
timerForLife = (float)timerToAdd % lifeReplenishTime;
lives += livesToAdd;
if (lives > maxLives)
{
lives = maxLives;
timerForLife = 0;
}
PlayerPrefs.SetString("LifeUpdateTime", DateTime.Now.AddSeconds(-timerForLife).ToString());
}else{
PlayerPrefs.SetString("LifeUpdateTime", DateTime.Now.ToString());
}
}
void OnApplicationPause(bool isPause)
{
if (isPause)
{
timeOfPause = System.DateTime.Now;
}
else
{
if(timeOfPause == default(DateTime)){
timeOfPause = System.DateTime.Now;
}
float timerToAdd = (float)(System.DateTime.Now - timeOfPause).TotalSeconds;
timerForLife += timerToAdd;
UpdateLives(timerForLife);
}
}
}

Blackberry 10: Unable to record voice

I want to record call voice during the call time on BlackBerry 10 OS. For this I used Telephony Phone and Call State Listener. I start as found Call State "Connected" and stop when call "Disconnected".
But at start time recorder records only 160Byte of data each time.
and at stop time recorder shows "Already unprepared".
For this I use recorder.prepare() but it still didn't work for me. Please suggest and help me. Here is the code
void ApplicationHeadless::onCallUpdated(const Call &call)
{
QMetaObject MetaCallTypeObject = CallType::staticMetaObject;
QMetaEnum CallTypeEnum = MetaCallTypeObject.enumerator(
MetaCallTypeObject.indexOfEnumerator("Type"));
QMetaObject MetaCallStateObject = CallState::staticMetaObject;
QMetaEnum CallStateEnum = MetaCallStateObject.enumerator(
MetaCallStateObject.indexOfEnumerator("Type"));
CallType::Type CurrentCallType = call.callType();
CallState::Type CurrentCallState = call.callState();
QString conn = "Connected";
QString dcon = "Disconnected";
QDateTime now = QDateTime::currentDateTime();
bb::multimedia::AudioRecorder recorder;
if (conn.compare(CallStateEnum.valueToKey(CurrentCallState)) == 0) {
recorder.setOutputUrl(QUrl("/tmp/" + now.toString() + ".m4a"));
recorder.setOutputUrl(QUrl("file://" + QDir::currentPath() + "/data/recording121.m4a"));
recorder.record();
}
if (dcon.compare(CallStateEnum.valueToKey(CurrentCallState)) == 0) {
recorder.reset();
}
}
Thanks in advance..

Send multiple messages in Photoshop SDK

I am using the Photoshop Connection SDK to get my iPad app connected to Photoshop.
(The SDK with all sample iOS projects can be downloaded here if you want to look closer: http://www.adobe.com/devnet/photoshop/sdk.html)
While I have it working, its hard for me to solve my issue as I don't entirely understand the networking between the iPad and Photoshop. So here is my issue:
NSString *s1 = [NSString stringWithUTF8String:"app.activeDocument.layers[0].name;"];
NSData *dataToSend = [s1 dataUsingEncoding:NSUTF8StringEncoding];
[self sendJavaScriptMessage:dataToSend];
artLayerName_transaction = transaction_id -1;
There is a little snippet of code, to send a message asking for the name of layer at index 0. That works great. However, let's say I try and send the same message straight after that but for index 1 as well.
Both messages are sent, but only one returns its string. The string is returned here:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent;
If you see the example projects its just the same, each message with its own transaction id as well. The method goes through a bunch of decrypting and things to receive that string and then reaches this:
NSString *string = [[NSString alloc] initWithBytes:received_data length:received_length encoding:NSUTF8StringEncoding];
if (content != 1)
{
if (transaction == artLayerName_transaction)
{
[self processLayerName:string];
needOutput = NO;
}
}
I've out the full method at the bottom for full analysing.
It checks that its receiving the specific message so I can then take the result (string, my layer name) and do what I like with it. However when I try and send more than one message with the same transaction id I only get one of the two results. In the code above, string gives me both my layer names, but the if statement is only called once.
Is there a known way around sending several messages at once? I have tried getting an array back rather than several strings without luck as well.
Apparently I need to modify the code to accept more then message. However I don't really understand the code well enough, so please also explain any of the principals behind it.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent
{
NSInputStream * istream;
switch(streamEvent)
{
case NSStreamEventHasBytesAvailable:;
UInt8 buffer[1024];
unsigned int actuallyRead = 0;
istream = (NSInputStream *)aStream;
if (!dataBuffer)
{
dataBuffer = [[NSMutableData alloc] initWithCapacity:2048];
}
actuallyRead = [istream read:buffer maxLength:1024];
[dataBuffer appendBytes:buffer length:actuallyRead];
// see if we have enough to process, loop over messages in buffer
while( YES )
{
// Did we read the header yet?
if ( packetBodySize == -1 )
{
// Do we have enough bytes in the buffer to read the header?
if ( [dataBuffer length] >= sizeof(int) ) {
// extract length
memcpy(&packetBodySize, [dataBuffer bytes], sizeof(int));
packetBodySize = ntohl( packetBodySize ); // size is in network byte order
// remove that chunk from buffer
NSRange rangeToDelete = {0, sizeof(int)};
[dataBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
}
else {
// We don't have enough yet. Will wait for more data.
break;
}
}
// We should now have the header. Time to extract the body.
if ( [dataBuffer length] >= ((NSUInteger) packetBodySize) )
{
// We now have enough data to extract a meaningful packet.
const int kPrologLength = 16;
char *buffer = (char *)[dataBuffer bytes];
// if incoming message is color change, then don't display message
BOOL needOutput = YES;
// fetch the communication status
unsigned long com_status = *((unsigned long *)(buffer + 0));
com_status = ntohl( com_status );
// decrypt the message
size_t decryptedLength = (size_t) packetBodySize - 4; // don't include com status
int skip_message = 0;
if (com_status == 0 && sCryptorRef)
{
PSCryptorStatus decryptResult = EncryptDecrypt (sCryptorRef, false, buffer+4, decryptedLength, buffer+4, decryptedLength, &decryptedLength);
if (kCryptorSuccess != decryptResult)
{
// failed to decrypt. Ingore messageg and disconnect
skip_message = 1;
[self logMessage:#"ERROR: Decryption failed. Wrong password.\n" clearLine:NO];
}
}
else
{
if (com_status != 0)
[self logMessage:#"ERROR: Problem with communication, possible wrong password.\n" clearLine:NO];
if (!sCryptorRef)
[self logMessage:#"ERROR: Cryptor Ref is NULL, possible reason being that password was not supplied or password binding function failed.\n" clearLine:NO];
}
// Interpret encrypted section
if (!skip_message)
{
// version, 32 bit unsigned int, network byte order
unsigned long protocol_version = *((unsigned long *)(buffer + 4));
protocol_version = ntohl( protocol_version );
if (protocol_version != 1)
{
// either the message is corrupted or the protocol is newer.
[self logMessage:#"Incoming protocol version is different the expected. (or the message is corrupted.) Not processing.\n" clearLine:NO];
skip_message = 1;
}
if (!skip_message)
{
// transaction, 32 bit unsigned int, network byte order
unsigned long transaction = *((unsigned long *)(buffer + 8));
transaction = ntohl( transaction );
// content type, 32 bit unsigned int, network byte order
unsigned long content = *((unsigned long *)(buffer + 12));
content = ntohl( content );
unsigned char *received_data = (unsigned char *)(buffer+kPrologLength);
int received_length = (decryptedLength-(kPrologLength-4));
if (content == 3) // image data
{
// process image data
unsigned char image_type = *((unsigned char *)received_data);
[self logMessage:#"Incoming data is IMAGE. Skipping\n" clearLine:NO];
if (image_type == 1) // JPEG
{
[self logMessage:#"By the way, incoming image is JPEG\n" clearLine:NO];
}
else if (image_type == 2) // Pixmap
{
[self logMessage:#"By the way, incoming image is Pixmap\n" clearLine:NO];
}
else
{
[self logMessage:#"Unknown image type\n" clearLine:NO];
}
}
else
{
// Set the response string
NSString *string = [[NSString alloc] initWithBytes:received_data length:received_length encoding:NSUTF8StringEncoding];
//NSLog(#"string: %#\n id:%li", string, transaction);
// see if this is a response we're looking for
if (content != 1)
{
if (transaction == foregroundColor_subscription || transaction == foregroundColor_transaction)
{
[self processForegroundChange:string];
needOutput = NO;
}
if (transaction == backgroundColor_subscription || transaction == backgroundColor_transaction)
{
[self processBackgroundChange:string];
needOutput = NO;
}
if (transaction == tool_transaction)
{
[self processToolChange:string];
needOutput = NO;
}
if (transaction == artLayerName_transaction)
{
[self processLayerName:string];
needOutput = NO;
}
}
//Tells me about every event thats happened (spammy tech nonsence, no good for user log)
//if (needOutput) [self logMessage:string clearLine:NO];
}
}
}
// Remove that chunk from buffer
NSRange rangeToDelete = {0, packetBodySize};
[dataBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
// We have processed the packet. Resetting the state.
packetBodySize = -1;
}
else
{
// Not enough data yet. Will wait.
break;
}
}
break;
case NSStreamEventEndEncountered:;
[self closeStreams];
[self logMessage:[NSString stringWithFormat: #"%# End encountered, closing stream.\n", outputMessage.text] clearLine:NO];
break;
case NSStreamEventHasSpaceAvailable:
case NSStreamEventErrorOccurred:
case NSStreamEventOpenCompleted:
case NSStreamEventNone:
default:
break;
}
}
EDIT:
While this has worked, it has bumped me into another issue. I looked at the psconnection sample and saw what you were talking about, however I haven't been using the full framework but have it connect like in the other sample projects. I very simply increase my transaction id by 1 every time it comes to process a layer name such as in the code above.
Like this:
if (transaction == docName_transaction)
{
docName_transaction++;
[self processDocName:string];
}
This works for the most part, however if I do this to another transaction id at the same time I get overlap. Meaning I end up processing the result for one id at the wrong time. Say I am getting both the total number of docs and the name of each one.
So I have two if statements like above, but I end up processing the total docs in both if statements and I can't see how to atop this overlap. Its fairly important to be able to receive multiple messages at once.
I just downloaded samples, and as far as i can see, you should use unique transaction IDs for transactions that are waiting for response.
I mean if you send layer_name request with transactionID 1, you cannot use 1, until it's response is received.
So better case will be, store your transactionID's in a Dictionary (transactionID as key), and message type as value.
just like:
transactions[transaction_id] = #"layername";
and when you receive response:
use:
transactions[transaction_id] to get the message type (ex: layername) behave according to this.
You can also put some other details to transactions dictionary (you can put a dictionary containing all information, which command, on which object, etc)

Where can I find updated, live exchange rates?

How do I link live currency exchange rates to my iPhone app? First, anyone know any sites where I can get the exchange rates? And second, how do I link that to my app? I want to do what this app does. http://the-dream.co.uk/currencee/
Here is a blog post about this, however to recap, if you use TBXML you can do it with the methods below.
They do the following:
Assume you have made a mutable dictionary object as a class property called exchangeRates
Set's EUR as the base rate (value of 1.0)
Call the European Central Bank's exchange rate XML feed and parses it.
After you've called the loadExchangeRates() method you can obtain a specific exchange rate by doing:
NSDecimalNumber *rate = [NSDecimalNumber decimalNumberWithString:[self.exchangeRates objectForKey:#"USD"]];
Here are the methods:
- (void)loadExchangeRates {
// initialize rate array
exchangeRates = [[NSMutableDictionary alloc] init];
// Load and parse the rates.xml file
TBXML * tbxml = [[TBXML tbxmlWithURL:[NSURL URLWithString:#"http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"]] retain];
// If TBXML found a root node, process element and iterate all children
if (tbxml.rootXMLElement)
[self traverseElement:tbxml.rootXMLElement];
// add EUR to rate table
[exchangeRates setObject:#"1.0" forKey:#"EUR"];
// release resources
[tbxml release]; }
- (void) traverseElement:(TBXMLElement *)element {
do {
// Display the name of the element
//NSLog(#"%#",[TBXML elementName:element]);
// Obtain first attribute from element
TBXMLAttribute * attribute = element->firstAttribute;
// if attribute is valid
NSString *currencyName;
while (attribute) {
/* Display name and value of attribute to the log window
NSLog(#"%#->%# = %#",
[TBXML elementName:element],
[TBXML attributeName:attribute],
[TBXML attributeValue:attribute]);
*/
// store currency
if ([[TBXML attributeName:attribute] isEqualToString: #"currency"]) {
currencyName = [TBXML attributeValue:attribute];
}else if ([[TBXML attributeName:attribute] isEqualToString: #"rate"]) {
// store currency and rate in dictionary
[exchangeRates setObject:[TBXML attributeValue:attribute] forKey:currencyName];
}
// Obtain the next attribute
attribute = attribute->next;
}
// if the element has child elements, process them
if (element->firstChild)
[self traverseElement:element->firstChild];
// Obtain next sibling element
} while ((element = element->nextSibling));
}
I realise this question has been answered already, but for anyone else looking for a solution to this same issue, there's also a great JSON solution available at openexchangerates.org as well.
My first port of call would be to find a webservice that provides currency rates with a public API.
Then you'd need to integrate some functionality into your app that communicates with the API in order to get the information you need.
There might be some services that offer the exchange rates in an RSS feed or similar feed. You could then parse the XML downloaded from that feed into some objects that you can use in your app.
Average monthly exchange rates of GBP to other currencies you can find as xml in link -
string url = "http://www.hmrc.gov.uk/softwaredevelopers/rates/exrates-monthly-" +
month2SymbolsYear2SymbolsString + ".xml";
then you can load this xml into lists and use in your code -
string xmlStr;
using (var wc = new WebClient())
{
xmlStr = wc.DownloadString(url);
}
var doc = XDocument.Parse(xmlStr);
var currenciesCodes = doc.Root.Elements().Select(x => x.Element("currencyCode"));
var rates = doc.Root.Elements().Select(x => x.Element("rateNew"));
List<string> currenciesCodesList = new List<string>();
foreach (var code in currenciesCodes)
{
currenciesCodesList.Add(code.Value);
}
List<double> currenciesRatesToGBPList = new List<double>();
foreach (var rate in rates)
{
double rateDouble;
if (!Double.TryParse(rate.Value, out rateDouble))
{
errorMessage = "During monthly average exchanges rates loading from page" + "\r\n" +
url + "\r\n" +
"program found text value - " + rate.Value + "\r\n" +
"which can't be converted to double value" + "\r\n" +
"Program can't correctly form reports and will end now.";
return errorMessage;
}
currenciesRatesToGBPList.Add(rateDouble);
}