tag:blogger.com,1999:blog-50747763034455998622024-02-19T00:46:27.139-08:00ThE uSeFuLUsefulness is comparative...ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.comBlogger107125tag:blogger.com,1999:blog-5074776303445599862.post-67263286812566064062022-04-11T12:01:00.007-07:002022-09-22T00:01:40.048-07:00Using WebRTC in your iOS app (what the official documentation will not tell you)<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZGb-fxwHCl6OGz1WrPOkmxhU7Q6nUL7ll2NC2g95CVppgXL8ynvEdLU6wYaS4lf_cONGpez-wJHwX7ZcVjPhNOLJ99WUVqpKaPu1bVOkGOvHiLjOeqsG03ZrPCOhsOrJPqafyPSyKmEoavIOEU1C6fVcOf588uWo4vnTjNhmldwhnoqjSAZ629W6m/s305/webrtc-logo-vert-retro-255x305-1.webp" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" height="320" data-original-height="305" data-original-width="255" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZGb-fxwHCl6OGz1WrPOkmxhU7Q6nUL7ll2NC2g95CVppgXL8ynvEdLU6wYaS4lf_cONGpez-wJHwX7ZcVjPhNOLJ99WUVqpKaPu1bVOkGOvHiLjOeqsG03ZrPCOhsOrJPqafyPSyKmEoavIOEU1C6fVcOf588uWo4vnTjNhmldwhnoqjSAZ629W6m/s320/webrtc-logo-vert-retro-255x305-1.webp"/></a></div>
<p>The <a href="https://webrtc.github.io/webrtc-org/native-code/ios/" target="_blank">documentation</a> is prety extensive and when followed you can successfully get it running in both your simulator and device. However, you are in for two surprises!!!</p>
<a name='more'></a>
<p><strong>1st Surprise</strong></p>
<p>
Documentation doesn't tell you about their branching strategy. If you are like me and would always keep your 'master' branch stable and in a releasable state, you are in for surprise with WebRTC repository. I will start off saying, "please don't assume the 'master' is stable". Therefore it is not wise to checkout their latest master and build the framework and use it in your app. This is what you would be doing if you just follow the instructions in the <a href="https://webrtc.github.io/webrtc-org/native-code/ios/" target="_blank">documentation</a>. Instead what you must do is look for the 'stable' tag in their commit history. They have three main tags.
</p>
<ul>
<li>stable</li>
<li>beta</li>
<li>dev</li>
</ul>
<p>Commit history can be found here <a href="https://chromiumdash.appspot.com/commits?repo=webrtc&platform=iOS" target="_blank">https://chromiumdash.appspot.com/commits?repo=webrtc&platform=iOS</a></p>
<p>Release history can be found here <a href="https://chromiumdash.appspot.com/releases?platform=iOS" target="_blank">https://chromiumdash.appspot.com/releases?platform=iOS</a></p>
<p><strong>2nd Surprise</strong></p>
<p>Apple has been rejecting WebRTC frameworks since April 2020. WebRTC is allegedly using private APIs. If you submit an app with a bitcode enabled WebRTC framework created following the official <a href="https://webrtc.github.io/webrtc-org/native-code/ios/" target="_blank">documentation</a> you will get a rejection mail from Apple like the one below.</p>
<blockquote>ITMS-90338: Non-public API usage - The app references non-public selectors in "My App Name": determineAppInstallationAttributionWithCompletionHandler:, didTap, initWithURLStrings:, lookupAdConversionDetails:, sdp. If method names in your source code match the private Apple APIs listed above, altering your method names will help prevent this app from being flagged in future submissions. In addition, note that one or more of the above APIs may be located in a static library that was included with your app. If so, they must be removed. For further information, visit the Technical Support Information at http://developer.apple.com/support/technical/</blockquote>
<p>The issue was first reported here <a href="https://bugs.chromium.org/p/webrtc/issues/detail?id=11492&q=references%20non-public%20selectors&can=2" target="_blank">https://bugs.chromium.org/p/webrtc/issues/detail?id=11492&q=references%20non-public%20selectors&can=2</a></p>
<p>Brought in to attention for the second time again here <a href="https://bugs.chromium.org/p/webrtc/issues/detail?id=13925&q=references%20non-public%20selectors&can=2" target="_blank">https://bugs.chromium.org/p/webrtc/issues/detail?id=13925&q=references%20non-public%20selectors&can=2</a></p>
<p>Developers at Google are still not convinced that this is an issue worth their valuable time. Luckily some nice developers decided to share their fix with the rest of us. The fix is not official but I have personally tested the framework both in simulator and device. WebRTC framework is used for live audio streaming in the app I maintain. We selected WebRTC because we wanted something that works on all three platforms iOS, Android and Web. I can also confirm that it passes Apple appstore review.</p>
<p>Below is the link to the WebRTC repo that works in all architectures and also accepted by Apple.</p>
<p><a href="https://github.com/jitsi/webrtc" target="_blank">https://github.com/jitsi/webrtc</a></p>
<p>Hope Google will prompt you this blog post when your WebRTC app gets rejected by Apple.</p>
ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-17516750762005712202019-01-20T18:19:00.002-08:002022-09-22T00:02:01.320-07:00Create peer-to-peer connections over Wi-Fi (Android)<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX2s-NVsYhztq2AvxN4sUvrHIyh8kYd4ffqwtCNbro0QQJLWIqzr56CjBg90p1Vchcz5QmzX3cc4QxqOx-Aih_OiHR-uFOpDcn3j7zb9LyxxlmkAZVqEP2wkjOXuXTsHmAdJv_nfrQuV4/s1600/wifi-direct.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="184" data-original-width="411" height="143" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX2s-NVsYhztq2AvxN4sUvrHIyh8kYd4ffqwtCNbro0QQJLWIqzr56CjBg90p1Vchcz5QmzX3cc4QxqOx-Aih_OiHR-uFOpDcn3j7zb9LyxxlmkAZVqEP2wkjOXuXTsHmAdJv_nfrQuV4/s320/wifi-direct.png" width="320" /></a></div><br />
This post is an extension of the conceptual explanation found on the same at <a href="https://cypressf.com/scope-website/wifi-direct.html">P2Feed</a>. They also felt that documentation provided by <a href="https://developer.android.com/training/connect-devices-wirelessly/wifi-direct">Google</a> was not comprehensive enough. There are number of libraries/starter projects developed by other developers for the same but if you are interested in whats happening under the hood and want to have full control of your peer-to-peer connection with no cost, please do keep on reading. This article assumes that you have already read the <a href="https://developer.android.com/training/connect-devices-wirelessly/wifi-direct">Android Developer Documentation on Wifi direct</a> and is here for more clarity.<a name='more'></a><br />
<br />
Wi-Fi Direct allows devices to connect directly to each other using methods similar to traditional Wi-Fi, except without a pre-established access point. Instead, they negotiate to establish one peer device to act as a software access point for all the other devices in the group. Devices can connect simultaneously to a Wi-Fi Direct group and a traditional access point. There are two ways to make a peer-to-peer connection,<br />
<br />
1. Service discovery<br />
2. Peer discovery<br />
<br />
Peer discovery finds all nearby peers with WiFi Direct. Service discovery finds all nearby peers with WiFi Direct that are running the same service. i.e. With service discovery you can also know whether the peer has a certain application installed and is ready to be paired. One other advantage is one device can have more than one such connection. In this article we will only discuss how you could use service discovery to connect to peers.<br />
<br />
<br />
<b>1. Setup P2P Manager</b><br />
<br />
private void setupP2PManager() {<br />
mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);<br />
mChannel = mManager.initialize(this, getMainLooper(), new WifiP2pManager.ChannelListener() {<br />
@Override<br />
public void onChannelDisconnected() {<br />
Log.i(TAG, "Channel disconnected");<br />
}<br />
});<br />
}<br />
<br />
<b>2. Start local service</b><br />
<br />
private void startLocalService() {<br />
<br />
/*<br />
* We are generating the buddy name by appending a random number instead of the device name to keep the buddy name unique at most.<br />
* Because buddy name represents a Wifi direct service to which one could connect to. One device can have more than one such service.<br />
* */<br />
String buddyName = "APP_NAME" + String.valueOf((int)(Math.random()*1000));<br />
<br />
// You may display the buddy name in your app so you will know what service to select from the second device<br />
<br />
// Create a string map containing information about your service.<br />
Map record = new HashMap();<br />
record.put(KEY_BUDDY_NAME, buddyName);<br />
<br />
// Service information. Pass it an instance name, service type<br />
// _protocol._transportlayer , and the map containing<br />
// information other devices will want once they connect to this one.<br />
WifiP2pDnsSdServiceInfo serviceInfo =<br />
WifiP2pDnsSdServiceInfo.newInstance("_appname", "_presence._tcp", record);<br />
<br />
mManager.addLocalService(mChannel, serviceInfo, new WifiP2pManager.ActionListener() {<br />
@Override<br />
public void onSuccess() {<br />
Log.i(TAG, "Local service added");<br />
}<br />
<br />
@Override<br />
public void onFailure(int arg0) {<br />
Log.i(TAG, "Could not add local service");<br />
}<br />
});<br />
}<br />
<br />
<b>3. Setup a broadcast receiver</b><br />
<br />
private void setupBroadcastReceiver() {<br />
// Indicates a change in the Wi-Fi P2P status.<br />
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);<br />
<br />
// Indicates a change in the list of available peers.<br />
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);<br />
<br />
// Indicates the state of Wi-Fi P2P connectivity has changed.<br />
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);<br />
<br />
// Indicates this device's details have changed.<br />
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);<br />
}<br />
<br />
private void registerWDBroadcastReceiver() {<br />
receiver = new WDBroadcastReceiver();<br />
receiver.activity = this;<br />
receiver.mChannel = mChannel;<br />
receiver.mManager = mManager;<br />
registerReceiver(receiver, intentFilter);<br />
<br />
discoverService();<br />
}<br />
<br />
public class WDBroadcastReceiver extends BroadcastReceiver {<br />
<br />
private static final String TAG = "WDBroadcastReceiver";<br />
<br />
public WDMainActivity activity;<br />
public WifiP2pManager.Channel mChannel;<br />
public WifiP2pManager mManager;<br />
<br />
@Override<br />
public void onReceive(Context context, Intent intent) {<br />
String action = intent.getAction();<br />
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {<br />
// Determine if Wifi P2P mode is enabled or not, alert<br />
// the Activity.<br />
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);<br />
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {<br />
activity.setIsWifiP2pEnabled(true);<br />
} else {<br />
activity.setIsWifiP2pEnabled(false);<br />
}<br />
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {<br />
<br />
// Request available peers from the wifi p2p manager. This is an<br />
// asynchronous call and the calling activity is notified with a<br />
// callback on PeerListListener.onPeersAvailable()<br />
<br />
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {<br />
<br />
// Connection state changed! We should probably do something about<br />
// that.<br />
<br />
if (mManager == null) {<br />
return;<br />
}<br />
<br />
NetworkInfo networkInfo = (NetworkInfo) intent<br />
.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);<br />
<br />
if (networkInfo.isConnected()) {<br />
<br />
// We are connected with the other device, request connection<br />
// info to find group owner IP<br />
<br />
mManager.requestConnectionInfo(mChannel, activity);<br />
}<br />
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {<br />
}<br />
}<br />
}<br />
<br />
<b>4. Start discovering services</b><br />
<br />
private void discoverService() {<br />
WifiP2pManager.DnsSdTxtRecordListener txtListener = new WifiP2pManager.DnsSdTxtRecordListener() {<br />
@Override<br />
/* Callback includes:<br />
* fullDomain: full domain name: e.g "printer._ipp._tcp.local."<br />
* record: TXT record dta as a map of key/value pairs.<br />
* device: The device running the advertised service.<br />
*/<br />
<br />
public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {<br />
Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());<br />
if (record.containsKey(KEY_BUDDY_NAME)) {<br />
buddies.put(device.deviceAddress, record);<br />
}<br />
}<br />
};<br />
<br />
WifiP2pManager.DnsSdServiceResponseListener servListener = new WifiP2pManager.DnsSdServiceResponseListener() {<br />
@Override<br />
public void onDnsSdServiceAvailable(String instanceName, String registrationType,<br />
WifiP2pDevice resourceType) {<br />
if (buddies<br />
.containsKey(resourceType.deviceAddress) && !peers.contains(resourceType)) {<br />
<br />
Map record = buddies.get(resourceType.deviceAddress);<br />
resourceType.deviceName = record.get(KEY_BUDDY_NAME).toString();<br />
peers.add(resourceType);<br />
// You may show the peers in your UI so you could select to pair <br />
<br />
Log.d(TAG, "onBonjourServiceAvailable " + instanceName);<br />
}<br />
}<br />
};<br />
<br />
mManager.setDnsSdResponseListeners(mChannel, servListener, txtListener);<br />
<br />
WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();<br />
mManager.addServiceRequest(mChannel,<br />
serviceRequest,<br />
new WifiP2pManager.ActionListener() {<br />
@Override<br />
public void onSuccess() {<br />
Log.i(TAG, "Service request added successfully");<br />
}<br />
<br />
@Override<br />
public void onFailure(int code) {<br />
Log.i(TAG, "Failed to add service request");<br />
}<br />
});<br />
<br />
mManager.discoverServices(mChannel, new WifiP2pManager.ActionListener() {<br />
<br />
@Override<br />
public void onSuccess() {<br />
Log.i(TAG, "Started discovering services");<br />
}<br />
<br />
@Override<br />
public void onFailure(int code) {<br />
Log.i(TAG, "Failed to start discovering services");<br />
handleActionListenerFailure(code);<br />
}<br />
});<br />
}<br />
<br />
private void handleActionListenerFailure(int code) {<br />
if (code == WifiP2pManager.P2P_UNSUPPORTED) {<br />
Log.d(TAG, "P2P isn't supported on this device.");<br />
} else if (code == WifiP2pManager.BUSY) {<br />
Log.d(TAG, "The system is to busy to process the request.");<br />
} else if (code == WifiP2pManager.ERROR) {<br />
Log.d(TAG, "The system is to busy to process the request.");<br />
}<br />
}<br />
<br />
<b>5. Connect to peer</b><br />
<br />
private void connect(WifiP2pDevice device) {<br />
WifiP2pConfig config = new WifiP2pConfig();<br />
config.deviceAddress = device.deviceAddress;<br />
config.wps.setup = WpsInfo.PBC;<br />
<br />
mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {<br />
@Override<br />
public void onSuccess() {<br />
Toast.makeText(WDMainActivity.this, "Peer connection initiated. Please wait...", Toast.LENGTH_SHORT).show();<br />
}<br />
<br />
@Override<br />
public void onFailure(int arg0) {<br />
Log.i(TAG, "Failed to initiate peer connection");<br />
handleActionListenerFailure(arg0);<br />
}<br />
});<br />
}<br />
<br />
<b>6. Detect changes in connection and exchange IP addresses</b><br />
<br />
For this you need to implement WifiP2pManager.ConnectionInfoListener<br />
<br />
@Override<br />
public void onConnectionInfoAvailable(WifiP2pInfo info) {<br />
Log.i(TAG, "onConnectionInfoAvailable");<br />
<br />
if (!info.groupFormed) {<br />
return;<br />
}<br />
<br />
Toast.makeText(this, "Group formed", Toast.LENGTH_SHORT).show();<br />
<br />
if (info.isGroupOwner) {<br />
shouldStartServer();<br />
} else {<br />
shouldConnectToServer(info.groupOwnerAddress.getHostAddress());<br />
}<br />
}<br />
<br />
private void shouldStartServer() {<br />
Log.i(TAG, "shouldStartServer");<br />
<br />
// You are the group owner. You should start a TCP server so your peers could connect and exchange IP addresses<br />
}<br />
<br />
private void shouldConnectToServer(String ip) {<br />
Log.i(TAG, "shouldConnectToServer: " + ip);<br />
<br />
// You are not the group owner. You should connect to the TCP server running in group owner to exchange the IP addresses<br />
}<br />
<br />
Every phone has a MAC address and an IP address. By the nature of the WiFi Direct connection, the group owner is aware of all the clients MAC addresses, but the clients are not aware of each other. In order to get clients to talk to each other, they must be aware of each other. To solve this problem, we needed to make the group owner distribute a list of the clients MAC and IP addresses to all clients. Unfortunately, the Wi-Fi Direct API does not give the group owner direct access to the IP addresses of the clients. The group owner only has access to its clients’ MAC addresses. To get access the clients’ IP addresses, initiate a socket connection from the client to the group owner.<br />
<br />
<br />
Additional setup<br />
<br />
1. Unregistering the broadcast receiver onPause()<br />
<br />
@Override<br />
public void onResume() {<br />
super.onResume();<br />
Log.d(TAG, "onResume: ");<br />
registerWDBroadcastReceiver();<br />
}<br />
<br />
@Override<br />
public void onPause() {<br />
super.onPause();<br />
<br />
unregisterReceiver(receiver);<br />
}<br />
<br />
2. Cleaning up onDestroy()<br />
<br />
private void cleanup() {<br />
mManager.clearLocalServices(mChannel, new WifiP2pManager.ActionListener() {<br />
@Override<br />
public void onSuccess() {<br />
Log.i(TAG, "Local services cleared");<br />
}<br />
<br />
@Override<br />
public void onFailure(int arg0) {<br />
Log.i(TAG, "Could not clear local services");<br />
}<br />
});<br />
<br />
mManager.clearServiceRequests(mChannel, new WifiP2pManager.ActionListener() {<br />
@Override<br />
public void onSuccess() {<br />
Log.i(TAG, "Service requestes cleared");<br />
}<br />
<br />
@Override<br />
public void onFailure(int arg0) {<br />
Log.i(TAG, "Could not clear service requests");<br />
}<br />
});<br />
}<br />
<br />
3. If you would like to disconnect from existing groups you may do this<br />
<br />
mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {<br />
@Override<br />
public void onSuccess() {<br />
Log.i(TAG, "Successfully removed existing group");<br />
initConnection();<br />
}<br />
<br />
@Override<br />
public void onFailure(int i) {<br />
Log.i(TAG, "Failed to remove group. It is possible that none exists");<br />
initConnection();<br />
}<br />
});<br />
<br />
Do not forget to read <a href="https://cypressf.com/scope-website/wifi-direct.html">P2Feed's notes</a> as they have shared some pain points in the peer-to-peer connection.<br />
<br />
Complete code<br />
<br />
<script src="https://gist.github.com/Tulakshana/8b6b0b7d5d8d6658b444ba26a25600f6.js"></script><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-82828726858033566832018-01-07T03:25:00.000-08:002022-09-22T00:02:20.064-07:00Setting an attributed string to an UITextViewSetting an attributed string to an UITextView can be pretty straight forward. See code sample below,<br />
<br />
yourTextView.attributedText = <i>Your attributed string</i><br />
<br />
However, I noticed that the scroll position of the UITextView changes upon executing the above line of code. <a name='more'></a>The solution is to disable scrolling before setting the text. The code below worked for me,<br />
<br />
yourTextView.isScrollEnabled = false<br />
yourTextView.attributedText = <i>Your attributed string</i><br />
yourTextView.isScrollEnabled = trueThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-5264555617192345122017-12-11T20:12:00.000-08:002022-09-22T00:02:34.263-07:00Selecting text in mobile Safari using voice overFollow the 7 easy steps below to select text in Safari using voice over.<br />
<br />
1. Listen to voice over reading the text you want to select (navigate the website the usual way). Voice over might read more than what you actually want. You'll be ok as long as the text you want to select is included.<br />
2. Use the rotor (Do a gesture like as if you are using a drawing compass to draw a circle. Use two fingers.) to switch to words<br />
3. Do the opposite of pinching<a name='more'></a><br />
4. Voice over will read out till what word (single letter words like 'I' and spaces included) the initial selection was made<br />
5. Repeat step 4 to extend the selection<br />
6. If you happen to select more than what you intend to, do a pinch gesture and voice over will unselect one or more words<br />
7. Repeat step 6 until voice over selects what you intend<br />
<br />
Just so you know the selection actually worked, try any of the edit options available.<br />
<br />
8. Use the rotor to switch to edit mode<br />
9. Swipe up to hear available edit options<br />
<br />
<br />
If you know of any alternatives, do not forget to share them in a comment below.ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-12661169814482284092017-06-08T05:20:00.000-07:002017-06-08T05:20:35.967-07:00View console on PostmanLately, I was trying out pre-request scripts on <a href="https://www.getpostman.com/" target="_blank">Postman</a>. We all know as the script gets larger and larger it would be handy to have place to print out the values as you debug. This is how you could achieve that on postman.<br />
<br />
1. Open Postman and go to View -> Show postman console.<br />
2. That's it!!!<br />
ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-86734431402391017742017-04-11T20:43:00.000-07:002017-04-11T20:43:16.779-07:00Huawei GR 5 night shots (default settings)<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCWzkpsehCiOGLmMmIN3jHNP_uxgA8JqEQIhA2PPuSIC5482u1IAo1_cUFn6ikXdTp6H50CvI2qbKd096NhjUfz5OXq8RK-nkYWbIosWRStR_ltt7U7KwgKnfYkZD3_-ou0ige4CQpDTo/s1600/huawei_gr5_night_shot_candles.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><br />
<img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCWzkpsehCiOGLmMmIN3jHNP_uxgA8JqEQIhA2PPuSIC5482u1IAo1_cUFn6ikXdTp6H50CvI2qbKd096NhjUfz5OXq8RK-nkYWbIosWRStR_ltt7U7KwgKnfYkZD3_-ou0ige4CQpDTo/s320/huawei_gr5_night_shot_candles.jpg" width="320" /><br />
</a>Candles</div><a name='more'></a><br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtSYE7Ln_sJtG9q6UpBOsZ_pOwDYV3_K4sSGhQm4ioHiVKz6-GND-xco4D2hIBQUGBUTm2g7D_pcfbnKMzze99hNARgrwyzu4lyRPDvvsAqUKOxzWejEPCC2YfwYjrdIPx2mhJ3iOmk-4/s1600/huawei_gr5_night_shot_full_moon.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><br />
<img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtSYE7Ln_sJtG9q6UpBOsZ_pOwDYV3_K4sSGhQm4ioHiVKz6-GND-xco4D2hIBQUGBUTm2g7D_pcfbnKMzze99hNARgrwyzu4lyRPDvvsAqUKOxzWejEPCC2YfwYjrdIPx2mhJ3iOmk-4/s320/huawei_gr5_night_shot_full_moon.jpg" width="240" /><br />
</a>Full moon</div><br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI6X0SJEuhM17fSCMkdIsVFouK10AO5CR0kBKar2JXCj9xfJtkMiV_anyvRrFHm8zW87p2sT4AIc6Y8gEsw957h8Z9fO17N1f6imTdHyxxQrEH8QeKhmXpP154HSjK6aCHiVpb-ALplqo/s1600/huawei_gr5_night_shot_lights.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><br />
<img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI6X0SJEuhM17fSCMkdIsVFouK10AO5CR0kBKar2JXCj9xfJtkMiV_anyvRrFHm8zW87p2sT4AIc6Y8gEsw957h8Z9fO17N1f6imTdHyxxQrEH8QeKhmXpP154HSjK6aCHiVpb-ALplqo/s320/huawei_gr5_night_shot_lights.jpg" width="320" /><br />
</a>Lights</div>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-33893419674644986702017-04-11T20:23:00.000-07:002017-04-11T20:45:54.620-07:00Grand Peak Ella, Sri Lanka reviewedLocation: It is close to the Ella railway station and three attractions in the area (if you are up for hiking). They are the Nine Arch Bridge, Mini Adams Peak, and Ella Rock.<a name='more'></a><br />
<br />
Food: We only asked for breakfast. We were there for two days. Both days we got bread. Earliest you can get breakfast is 8.00 a.m. However, we managed to get them to pack us some sandwiches earlier because we wanted to start our day early. Not much variety, but the food they served tasted good. If you are a coffee fan you are in luck. Their tea was not good. They will not provide food for the driver. However, you could order separately at a discounted rate. For all your food/beer/cocktail needs I suggest you head on to "<a href="https://www.tripadvisor.in/Restaurant_Review-g616035-d11910702-Reviews-360_Ella-Ella_Uva_Province.html" target="_blank">360 Ella</a>". It is located at the middle of the Ella town opposite Bank of Ceylon.<br />
<br />
Staff: The Manager speaks English and is friendly. There are about four people to look after the place. One made my wife very uncomfortable with his continuous staring.<br />
<br />
Cleanliness: We were welcomed by a cockroach and a lizard as we entered the room. We killed the roach and chased the lizard only to find black ants! Towels were stained and we found sand on the bed. There was an unpleasant odour off and on, which we suspected to be from the toilet.<br />
<br />
Security: Ella sleeps late compared to what it was a few years ago. However, having a window with a broken lock still bothered me. Rumour has it that there were few HIV cases reported in the area. Anyway no harm in playing it safe. :) Our room was protected with wire meshes on the grills of the windows, however, the mosquito net over the bed was torn.<br />
<br />
Lighting: Sufficient :)<br />
<br />
Facilities: Free Wi-Fi available, no TV, no A/C (this isn't a major issue), no parking within the hotel premises (available outside).<br />
<br />
Website:<a _blank="" href="http://www.ellagrandpeak.com/" target="_blank"> http://www.ellagrandpeak.com/</a>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-18758672443895523032017-04-01T20:03:00.001-07:002017-04-02T20:51:55.618-07:00Making your mobile app respond to voice commands (iOS vs Android)Making you app to understand voice command was not an easy task before the introduction of Siri (iOS) and Google assistant. Both are now opened for developers. However, as usual Apple's Siri is not fully open to the developers when compared to the capabilities of "Google Assistant".<a name='more'></a><br />
<br />
Siri is said to be limited to the following domains.<br />
<br />
- VoIP calling <br />
- Messaging <br />
- Payments <br />
- Photos/Videos <br />
- Workouts <br />
- Ride booking <br />
- Car commands <br />
- CarPlay (automotive vendors only) <br />
- Restaurant reservations (requires additional support from Apple)<br />
- IOT (using HomeKit) <br />
<br />
This means that Siri will only understand certain phrases. Therefore certain phrases will sound in-appropriate for other domains. For example "Look for beach photos taken last summer in your-app-name." will be an appropriate search only if your app contains images. I'm afraid at the moment Siri cannot be taught to respond to "Look for notes taken last summer in your-app-name.". To be fair they are at least supporting all the apps that are used on the move. Apple will also not want their stock apps to be less popular either :)<br />
<br />
Both Siri and Google assistant could be programmed to use your app to respond to voice commands even when the app is not running in the background. As anticipated Google is a step ahead by not limiting support for a selected set of domains. Google does this by allowing the user to switch to a private channel with an app of their choice. While the user is in this space Google assistant will be deaf to the rest of the commands it can support and will only respond to the commands defined by the app. The Conversation API defines a request and response format that you must adhere to when communicating with the Google Assistant. <br />
<br />
Conversation Actions help you fulfill user requests by letting you have a two-way dialog with users. When users request an action, the Google Assistant processes this request, determines the best action to invoke, and invokes your Conversation Action if relevant. From there, your action manages the rest, including how users are greeted, how to fulfill the user's request, and how the conversation ends. Even though you control the user experience when your action is invoked, the Google Assistant still brokers and validates the communication between your action and the user. The Actions SDK gives you all the tools you need to build Conversation Actions, but you might want to choose to build with one of our supported tools, such as API.AI. These tools generally provide a better developer experience by making it easier to build and deploy actions within a single interface and also provide additional features to make building actions easier. By using API.AI to build actions, you gain a lot of conveniences, such as:<br />
<br />
• API.AI NLU - API.AI's Natural Language Understanding is integrated into API.AI and does in-dialog processing of input. This offers you with conveniences such as natural expansion of phrases and built-in features to easily distinguish and parse arguments from user input.<br />
• A GUI interface - You can define and configure action components, such as dialogs and invocation, in an easy-to-use UI.<br />
• Conversation building features - API.AI has advanced features such as contexts, which let you easily maintain state and contextualize user requests, a built-in simulator, and machine learning algorithms to better understand user input.<br />
<br />
Ready to dig in? Follow the links below to get started.<br />
<br />
- Siri: <a href="https://developer.apple.com/sirikit/" target="_blank">https://developer.apple.com/sirikit/</a><br />
- Google assistant: <a href="https://developers.google.com/actions/develop/conversation" target="_blank">https://developers.google.com/actions/develop/conversation</a>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com1tag:blogger.com,1999:blog-5074776303445599862.post-69081458309574320472016-10-03T04:07:00.000-07:002022-09-22T00:03:16.015-07:00Distribute builds via Xcode server<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijipc5fl8yR2z0Ww62gGGBNva4Cj4lLlhrxJ53aXkuQdtlZ6BpIIgkhvYuYq06SL5j83Wh446bB8DjiE_18wi9ZVQGjf4plBoGzXnSsLRvQAuL1BJRft7PNnPGY7c7W7IXRCeu8hpaxac/s1600/Screen+Shot+2016-10-03+at+3.57.33+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="82" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijipc5fl8yR2z0Ww62gGGBNva4Cj4lLlhrxJ53aXkuQdtlZ6BpIIgkhvYuYq06SL5j83Wh446bB8DjiE_18wi9ZVQGjf4plBoGzXnSsLRvQAuL1BJRft7PNnPGY7c7W7IXRCeu8hpaxac/s200/Screen+Shot+2016-10-03+at+3.57.33+PM.png" width="200" /></a></div><div class="separator" style="clear: both; text-align: center;">Image 01</div><br />
This is an inbuilt feature of the Xcode server, therefore additional configuration is not required. <br />
<a name='more'></a>Once you have successfully setup the server, open the server dashboard (image 01) and click on the bot. Then use the 'product' button in the pop up (image 02) to download the ipa.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwZmtoe7iD5wFf3faSnnckMrNxeKEppcnFJtI34iSQCmyWmxSGB-hhfSueeFhqR_Xquo6rEyWhWN5alZXtAs-3xHLPA5lAQkOWHQ8NGISuC4yTgoduVNfombSQOqbYieUokEIu3t-jmZU/s1600/Screen+Shot+2016-10-03+at+3.57.59+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwZmtoe7iD5wFf3faSnnckMrNxeKEppcnFJtI34iSQCmyWmxSGB-hhfSueeFhqR_Xquo6rEyWhWN5alZXtAs-3xHLPA5lAQkOWHQ8NGISuC4yTgoduVNfombSQOqbYieUokEIu3t-jmZU/s200/Screen+Shot+2016-10-03+at+3.57.59+PM.png" width="200" /></a></div><div class="separator" style="clear: both; text-align: center;">Image 02</div>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-9433647462441769182016-02-24T07:42:00.002-08:002016-02-24T07:47:12.798-08:00Saving custom objects in NSUserDefaults - SwiftFollowing is how I implemented "remember me" feature in a recent app I did in <b>swift</b>. I'm saving the user details in NSUserDefaults upon requesting to remember.<a name='more'></a><br />
<br />
<script src="https://gist.github.com/Tulakshana/f311efa3cd1c91fd4c46.js"></script>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com2tag:blogger.com,1999:blog-5074776303445599862.post-40199747435585729442015-08-30T21:00:00.000-07:002022-09-22T00:03:33.483-07:00Clean derived data - XcodeWith Xcode 4 came the concept of a workspace that allows multiple projects to be grouped together. Each workspace gets a unique set of symbol indexes, build products, window layouts, etc., otherwise referred to by Xcode as derived data. Since a single project can belong to more than one workspace this derived data is not contained under the individual project directory. This post explains how you could clean derived data.<a name='more'></a><br />
<br />
Option 1: Delete the containing folder using Finder/Terminal<br />
<br />
By default Xcode stores this data for all projects in a single shared folder under your home directory at the following location:<br />
<br />
~/Library/Developer/Xcode/DerivedData<br />
<br />
Option 2: Delete using Xcode<br />
<br />
Open Xcode. Go to Window > Projects<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTvFUtaV1rLkH39v8XBTj46rnhnFYfiOuMBczmNt4bEIl7ka-XLYHs6NUi8iHVCMSjVLIIOQngVXNLACZeU1P5_V-8F4mk4_3m5b1LCFZnxl6ykpFULh_ldIpNcZiKW0ifrnaHWfZFzMg/s1600/Screen+Shot+2015-08-21+at+3.38.01+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTvFUtaV1rLkH39v8XBTj46rnhnFYfiOuMBczmNt4bEIl7ka-XLYHs6NUi8iHVCMSjVLIIOQngVXNLACZeU1P5_V-8F4mk4_3m5b1LCFZnxl6ykpFULh_ldIpNcZiKW0ifrnaHWfZFzMg/s200/Screen+Shot+2015-08-21+at+3.38.01+pm.png" /></a></div><br />
Select the project from the list to your left to delete its derived data. Notice that this is the same place you have to come to restore/delete snapshots you made as well.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbe1zWxFzPCk9RyBecNxZzA5A84ITzKPQSACy8AzVMN4HxFuJzu1dyCcj1CKjvCXHNtVE4BAb3furYpx6Oq269584u98GtyMsAk0abRj44ZfPA95wnryoq1aSUyI47BYIC9g-SOjE_mn8/s1600/Screen+Shot+2015-08-21+at+3.38.27+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbe1zWxFzPCk9RyBecNxZzA5A84ITzKPQSACy8AzVMN4HxFuJzu1dyCcj1CKjvCXHNtVE4BAb3furYpx6Oq269584u98GtyMsAk0abRj44ZfPA95wnryoq1aSUyI47BYIC9g-SOjE_mn8/s200/Screen+Shot+2015-08-21+at+3.38.27+pm.png" /></a></div>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-70759871268461018792015-08-20T04:44:00.000-07:002022-09-22T00:03:49.352-07:00Updating to Mantle 2.0I have been using Mantle for JSON serialisation for quite some time now. I loved it because it appeared to be a clean way to handle JSON objects. However somethings have changed in version 2.0. This is not an attempt to criticise the changes but an attempt to give a heads up before you update.<br />
<a name='more'></a><br />
<br />
<ul><li>Value transformers have changed. Old transformers have been deprecated and new transformers have taken their place. Notice the compiler warnings once you update and compile.</li>
<li>Prior to version 2.0 any type errors in the JSON objects were handled by Mantle. Now the developer has to handle such mismatches. e.g. you need to add a transformer if you need to map a string property in the JSON object to an integer property in your class. Prior to version 2.0 transformers were required only if the transformation is not obvious. e.g. if you need to map a string property to a NSURL property (create a NSURL from a url string in the JSON).</li>
<li>Property map requires all the properties. Previous versions only required this only if the name of the property in your class is not the same as of the one in the JSON.</li>
<li>You cannot have properties that are not mapped to the JSON object</li>
</ul>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-62069144599264459512015-04-13T19:59:00.000-07:002015-04-13T19:59:49.018-07:00Convert any URL or webpage to PDF<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ2tWNVhUN9svTeElCy0g_TcHbzpO_Q7tEI1IcsT1ftkNoTwWhMTC4wr8fdvo_VOq_DEHEoDF4PxoXTgRGsjDg-G46985o3mvQBldIRB7UuhhwESmJPlrWGfS1ZcyZjmVIqeN_XrSsKLA/s1600/Screen+Shot+2015-04-14+at+8.24.47+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ2tWNVhUN9svTeElCy0g_TcHbzpO_Q7tEI1IcsT1ftkNoTwWhMTC4wr8fdvo_VOq_DEHEoDF4PxoXTgRGsjDg-G46985o3mvQBldIRB7UuhhwESmJPlrWGfS1ZcyZjmVIqeN_XrSsKLA/s200/Screen+Shot+2015-04-14+at+8.24.47+AM.png" /></a></div>There are number of services available online for this task. Most of them are free too. But did you know that you can do the same with your <b>Safari</b> browser? I recommend it to be more safe on the privacy side as no third party will see the content you make PDFs out of. ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-84004841987364798702015-02-25T04:07:00.000-08:002022-09-22T00:04:11.008-07:00Manifest for Apple enterprise distributionsGone are the days where Xcode generated the manifest for you when you archive for enterprise distribution. So it is more or less a manual process now and below is a sample manifest. Just Create a .plist file (can use Xcode for this) using the following keys and values appropriately.<a name='more'></a><br />
<br />
<plist version="1.0"><br />
<dict><br />
<key>items</key><br />
<array><br />
<dict><br />
<key>assets</key><br />
<array><br />
<dict><br />
<key>kind</key><br />
<string>software-package</string><br />
<key>url</key><br />
<string>https://yourdomain.com/apps/appname.ipa</string><br />
</dict><br />
</array><br />
<key>metadata</key><br />
<dict><br />
<key>bundle-identifier</key><br />
<string>com.yourdomain.appname</string><br />
<key>kind</key><br />
<string>software</string><br />
<key>title</key><br />
<string>appname</string><br />
</dict><br />
</dict><br />
</array><br />
</dict><br />
</plist><br />
<br />
ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-45996192828710013992015-01-18T01:06:00.001-08:002022-09-22T00:04:24.808-07:00Symposium on Language Technology for South Asia 2015 [HIGHLIGHTS]<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZduiYKa-ETaIs9ej44YVk-DQkVpPVw21lj4msdb7lI6yKgaNxbF9NaSGMPmA0xsGDoKF9AnnJpXwv3_Dc47KOJ6dn8nio0UtPgMuSgyqfAZYeTZr69S3pLXiGbVygc-mLJKloFqAquUg/s1600/10899991_1699406696952243_7921113209848742874_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZduiYKa-ETaIs9ej44YVk-DQkVpPVw21lj4msdb7lI6yKgaNxbF9NaSGMPmA0xsGDoKF9AnnJpXwv3_Dc47KOJ6dn8nio0UtPgMuSgyqfAZYeTZr69S3pLXiGbVygc-mLJKloFqAquUg/s200/10899991_1699406696952243_7921113209848742874_o.jpg" /></a></div><br />
I know its bit late but anyways...<br />
<br />
The symposium was held on 2nd of January 2015 at University of Moratuwa, Sri Lanka. Academics and industry experts participated the event from Universities like University of Colombo and Sri Lanka Institute of Information Technology (SLIIT). The keynote speech was given by <b>Prof. Rohini Paranavitana</b> (emeritus professor, department of Sinhala university of Colombo). This year’s topics were mostly based on languages Sinhala and Tamil with the absence of representatives from other south Asian countries.<a name='more'></a> <br />
<br />
However Mr. Achinthya Bandara enlightened gathering about a small (1000+ according to census department) community which speaks a language he identified as “<b>Sri Lankan Portuguese</b>”. The language is a Creole (<a href="http://en.wikipedia.org/wiki/Creole_language" target="_blank">http://en.wikipedia.org/wiki/Creole_language</a>) which has developed with the Portuguese mixing with the Tamil community living in Batticaloa, Sri Lanka during the colonial era. He find the language endangered and hence methods of documenting Creoles were discussed. According to Achinthya non-availability of a grammar makes it difficult to document Creoles. However ELAR (Endangered Languages Archive) (<a href="http://elar.soas.ac.uk/" target="_blank">http://elar.soas.ac.uk/</a>) does a major role in archiving such languages by digitally storing of videos, audios, texts and images of such languages. I was bit curious and I thought of searching for ‘Sinhala’ in their archives. They had an entry for the ‘Vedda’ language (<a href="http://elar.soas.ac.uk/deposit/0178" target="_blank">http://elar.soas.ac.uk/deposit/0178</a>). However no digital archives were still available.<br />
<br />
<b>Mr. Dhanika Perera</b> who turned his undergraduate project in to a business and now is the proud CEO of Basha Lanka (Pvt) Ltd gave an inspiring speech to the undergraduates present at the Symposium showcasing his latest developments.<br />
<br />
<b>Mr. S. Shanmugarajah</b> (Director, Mobile Architecture, WSO2) who is well known among language technology experts also shared his ongoing project on making Adobe products support Sinhala and Tamil characters. According to statistics the Adobe products are popular in the printing industry.<br />
<br />
I was also able to present my work on a touch screen Sinhala keyboard I was developing with <b>Prof. Gihan Dias</b>. The keyboard allows users to input modifiers using touch gestures and will soon be available in the Google play store.<br />
<br />
The next symposium is expected to be held later this year (2015).<br />
<br />
<a href="https://www.facebook.com/media/set/?set=a.1699384526954460.1073741874.1444756225750626&type=3" target="_blank">Click here to see the full event in pictures.</a>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-51460836552094627932014-07-25T05:05:00.000-07:002022-09-22T00:04:34.233-07:00Test iPhone apps in iPad?<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0VH2b6YaXWsXpwlMD8nhglwbpGS7CdBAqcY8kBnr9X11IWY9Uw1wpIA1SuxWu7veUWqaQSa1DCl6eKo-hml92eUbSa1qYnSNrEIaKq_uY4ABpg7uBHVZpyXx0sUv0l-nvEDQZe5ZB3yE/s1600/hero_test_102011.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0VH2b6YaXWsXpwlMD8nhglwbpGS7CdBAqcY8kBnr9X11IWY9Uw1wpIA1SuxWu7veUWqaQSa1DCl6eKo-hml92eUbSa1qYnSNrEIaKq_uY4ABpg7uBHVZpyXx0sUv0l-nvEDQZe5ZB3yE/s200/hero_test_102011.png" /></a></div><br />
In this post I’ll be covering few important things I think you should know if you decide to test apps made for iPhone/iPod in an iPad.<br />
<a name='more'></a><br />
You can't replicate application interruptions (such as a phone call or SMS text) as easily.<br />
<br />
To test iPhone app on iPad it must be iPhone-only app. Then you will be able to use the zoom capability (which doesn’t look nice btw). If you create universal app (which you should do) and test it on iPad it will switch to iPad views and you will end up with iPhone views untested. <br />
<br />
You will not be able to test status bar (hidden/visible) if you are changing it. E.g. If you make the status bar hidden from “.plist” and run the app (made for the iPhone) on your iPad the status bar will be still visible.ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-41902071315327380302014-07-01T20:43:00.000-07:002022-09-22T00:04:44.836-07:00Be Grateful<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7D5ujLd_7ES3KzxsbtQoh70s3Gm17lUiggloJYmwJOPTQhE3mQmaMJwdM3SUZoIn_DrI9vqQZIVjFBVk9s78JyOvPKmDYDO8PJYP-ssHW10Avuh8CsHyrk04_ROUgKc8FxVApTYe85cc/s1600/Thank-You-2012.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7D5ujLd_7ES3KzxsbtQoh70s3Gm17lUiggloJYmwJOPTQhE3mQmaMJwdM3SUZoIn_DrI9vqQZIVjFBVk9s78JyOvPKmDYDO8PJYP-ssHW10Avuh8CsHyrk04_ROUgKc8FxVApTYe85cc/s200/Thank-You-2012.jpg" /></a><br />
<br />
be grateful to your <br />
competitor<br />
annoying colleague <br />
abusive person<br />
lost opportunities<a name='more'></a> <br />
<br />
all hardships <br />
makes you strong<br />
develops patience<br />
improves skills<br />
<br />
you are privileged <br />
be grateful</div>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-77442471985901502132014-06-14T22:01:00.000-07:002022-09-22T00:04:57.987-07:00Can Tim Cook boast about Apple’s success? - WWDC 2014<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm_yR3-_e-ZQZ4bXQe2WsmuaaNIDVNWYmfCn3wVmSvzsXaboJxpaEnTou3gIfu_xBthAgrU4NAa7a6dAFYbp7_a1mVAPa-tHqqiDmjevzGMn1hZfXbrAdJCS0ko3KY1rA9jErujojaoAw/s1600/ios.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm_yR3-_e-ZQZ4bXQe2WsmuaaNIDVNWYmfCn3wVmSvzsXaboJxpaEnTou3gIfu_xBthAgrU4NAa7a6dAFYbp7_a1mVAPa-tHqqiDmjevzGMn1hZfXbrAdJCS0ko3KY1rA9jErujojaoAw/s200/ios.png" /></a></div>Just finished watching <b>WWDC 2014</b> and thought I might have slightly different opinion than of Tim regarding Apple’s success. Tim boasts about user base of both <b>iOS 7</b> and <b>Mavericks</b> comparing <b>Android</b> and <b>Windows 8</b>. Can a user base only be an indicator of its success? <a name='more'></a><br />
<br />
One good reason for most Apple users to be using the latest version could be because it’s free compared to Windows. When it comes to Android not all devices receive latest updates. This could also be because of hardware compatibility issues and based on marketing decisions individual companies (Samsung, Sony and etc.) make. Tim also calls Android users ancient because they are not using the latest version. But one should not also forget that most features Apple introduces in their latest version were already available in old Android versions. E.g. customizable device wide keyboards, widgets and etc. <br />
<br />
But I shouldn’t fail to mention that Apple will always stand out from the rest because of how they integrate their software and hardware with the environment and social status (<b>Steve Jobs</b> would say) they create. Also Apple is very serious about <b>privacy</b> and <b>security</b>. Users still trust Apple compared to Windows or Android when it comes to privacy and security. Once I had a hard time getting people to install a keyboard I designed for Android. Even my supervisor had cold feet at first thinking that it might harm the OS or I might steal his passwords.ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-29189397390745944522014-05-29T21:51:00.000-07:002014-05-30T00:04:42.521-07:00New yahoo mail<div class="separator" style="clear: both; text-align: center;"><a href="http://screenshots.en.sftcdn.net/en/scrn/66000/66424/yahoo-messenger-36.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://screenshots.en.sftcdn.net/en/scrn/66000/66424/yahoo-messenger-36.jpg" /></a></div>Today when I typed in mail.yahoo.com in my browser (that’s what I normally type to log in to my mail) it took me yahoo.com. I’m started to think that they are quite serious about marketing other than identifying it as a bug. It is not the only change I saw so far. I have listed some interesting changes they have made below.<a name='more'></a><br />
<br />
<a href="http://yahoomail.tumblr.com/post/76528576183/100-million-ways-to-say-i-love-you-with-yahoo-mail" target="_blank">The love templates</a>: They had this for the last valentines I think it would be handy to have it everyday and with more templates like cover letters for job interviews.<br />
<br />
Loving the <a href="http://yahoomail.tumblr.com/post/80685050684/a-new-view-when-you-search-for-people-in-yahoo-mail" target="_blank">new in mail search</a><br />
<br />
Its quite interesting to <a href="http://yahoomail.tumblr.com/post/79395405926/bringing-links-to-life-with-link-preview" target="_blank">generate and embed a preview of the links you include in mails</a>. Anyhow I think something is wrong somewhere because I sometimes find this bit buggy.<br />
<br />
Now you can <a href="http://yahoomail.tumblr.com/post/75704865117/view-attachments-more-easily" target="_blank">view attachments easily</a><br />
<br />
Finally they have decided to support the chain mail concept. My Gmail friends always use to bug me for not having it yahoo.<br />
<br />
As a yahoo mail fan I’m happy the way things are turning out or at least the attempt they are putting. It would be great if they could also look into the crashes in the mail. I sometimes have to stare at it for a while till it decides to open up the composing window or a selected mail. Typing in mail.yahoo.com has always been the fix for me luckily they don't redirect it to their home page when you are logged in to the mail.<br />
ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com1tag:blogger.com,1999:blog-5074776303445599862.post-79150650111520788732014-03-29T08:03:00.001-07:002014-03-29T08:09:36.850-07:00Open URL - AndroidThe following three lines of code will open a URL in an Android app. I'm guessing you cannot open a local html file using the same method in your browser.<br />
<br />
Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("http://www.google.com"));<br />
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);<br />
startActivity(intent);ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-75790198807473764882014-02-26T21:54:00.001-08:002014-02-27T01:51:24.457-08:00iOS closes the active Wi-Fi connection after 30 minutes<div class="separator" style="clear: both; text-align: center;"><a href="http://www.webestigate.com/wp-content/uploads/2011/12/select-a-wireless-network.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.webestigate.com/wp-content/uploads/2011/12/select-a-wireless-network.png" /></a></div><br />
These days I’m involved developing an app (runs on both iPad and iPhone) that relies on Wi-Fi network a lot. I happen to notice that sometimes device looses its connection to the Wi-Fi network. Rest of the team was complaining that they have to keep on going to device settings to re-connect.<a name='more'></a> <br />
<br />
This behavior is because iOS closes the active Wi-Fi connection after 30 minutes of idling. In order to stop this behavior you need to tell iOS that your app requires Wi-Fi. You can do this by setting the property “Application uses Wi-Fi” to YES in your app’s info.plist file. Once set iOS will prompt for network selection when your app tries to connect to Wi-Fi while it is been temporary disabled by iOS. If Wi-Fi is switched off from the device settings this popup won’t appear. So I think it doesn’t force you to use Wi-Fi but prompts for network selection if Wi-Fi is switched on from device settings and the device is not connected to any network.<br />
<br />
Also note that this is the same property you need to set if you need to prohibit the app from using Wi-Fi. In this case set “Application uses Wi-Fi” to NO.ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-56455283524757691102014-01-29T21:05:00.000-08:002014-05-30T00:05:07.517-07:00Google -> Account Chooser -> Automatic Sign In<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipXxMnUUoSZ5_VUMCbcdTkoxCemhETHUa-AIjxX5cgWbRoy1k9nBgGkmyXxX7zGLFqO4Qan0jmYcSrARtzc-NjIZxqdsjSeFBArecIs0m9-SjWMCwvCn95R2FDMkbOh2QwktnUf9Dvsr0/s1600/Screen+Shot+2014-01-30+at+10.41.15+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipXxMnUUoSZ5_VUMCbcdTkoxCemhETHUa-AIjxX5cgWbRoy1k9nBgGkmyXxX7zGLFqO4Qan0jmYcSrARtzc-NjIZxqdsjSeFBArecIs0m9-SjWMCwvCn95R2FDMkbOh2QwktnUf9Dvsr0/s200/Screen+Shot+2014-01-30+at+10.41.15+AM.png" /></a></div>If you are experiencing automatic sign in to your Google services and you can't find the "stay signed in" checkbox when you sign out, you have come to the correct place for answers. <a name='more'></a>Once Google pair up a device with an account you are required to go to the account chooser to undo it. You should get a link below the sign in page to view all the Google accounts paired with the device you are using. Go to it and remove the account required. Once removed, everything should be back to normal.<br />
<br />
<br />
Further reading:<br />
<a href="https://support.google.com/accounts/answer/1691641?hl=en" target="_blank">https://support.google.com/accounts/answer/1691641?hl=en</a>ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-40752559951779649802014-01-01T23:34:00.000-08:002014-01-01T23:36:08.363-08:00Can’t declare variables inside a switch case – Objective CIf you were under the above impression then the answer is <strong>you can</strong> with a little bit of change to your code. I’m guessing you are used to the following format,<br />
<br />
switch (value){<br />
case value1:<br />
//some code<br />
break;<br />
case value2:<br />
//some code<br />
break;<br />
default:<br />
//some code<br />
break;<br />
}<a name='more'></a><br />
<br />
If you want to declare variables inside a case you need to follow the format below<br />
<br />
switch (value){<br />
case value1:{<br />
//some code<br />
//you can declare any variables here<br />
//(notice the additional brackets)<br />
break;<br />
}<br />
case value2:<br />
//some code<br />
break;<br />
default:<br />
//some code<br />
break;<br />
}ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-14015067869352214292013-12-31T01:08:00.000-08:002013-12-31T01:11:48.142-08:00The truth about iOS7 being slowI have seen many posts describing the above fact, mostly regarding running iOS7 on older devices like the iPhone 4. One truth I found in all the stuff I read is the fact about how iOS7 loads its frameworks. As you all know iOS7 is designed for 64bit. Running 32bit apps might consume more memory than otherwise. Bit hard to believe, right? But it is true. This is how it happens.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja_y0pyoxRXhxFrQTnQeSDq25ITH4lgRURtOGU02PlqMycxlhOfFBfd8DDAVvDffrWGVa4CLD6h-26OMWRCh3ARIXqe8U_q0aIxHpA1XCiBoFN7RUWZsj2B287VzK2hNPSzmdOCYKH-_s/s1600/IMG_0203.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja_y0pyoxRXhxFrQTnQeSDq25ITH4lgRURtOGU02PlqMycxlhOfFBfd8DDAVvDffrWGVa4CLD6h-26OMWRCh3ARIXqe8U_q0aIxHpA1XCiBoFN7RUWZsj2B287VzK2hNPSzmdOCYKH-_s/s200/IMG_0203.PNG" /></a></div><a name='more'></a><br />
iOS7 loads its frameworks to the memory and allows its apps to use it. This way each individual app is not required to load the required frameworks. By default iOS7 loads the 64bit frameworks. 32bit frameworks are loaded upon demand only. When loading a 32bit version is required additional memory get used up as now the memory has to hold both 32bit and 64bit versions.ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0tag:blogger.com,1999:blog-5074776303445599862.post-31756461354743908362013-12-30T00:32:00.003-08:002013-12-30T00:44:28.410-08:00Arranging UI elements for iPad split keyboardIt is a common practice to rearrange the UI Elements when the keyboard appears (if required). If you split the keyboard (Do the split gesture or long press the keyboard hide button) the keyboard notifications UIKeyboardDidShowNotification and UIKeyboardDidHideNotification will not get called. <br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJbIcXNNK1vuxD9jXdy0AX7KHseFnzd3cBVh5cVkChkPQ1QHT1HoWcqaFj8It8xpIjK88MX93z1DZchvS6iD0EXWFwSyR1PjKyKnGa4ohYZ_CuYAXlDiVJy_uFk7Om9JC8eOCwrvlzDpE/s1600/IMG_0008.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJbIcXNNK1vuxD9jXdy0AX7KHseFnzd3cBVh5cVkChkPQ1QHT1HoWcqaFj8It8xpIjK88MX93z1DZchvS6iD0EXWFwSyR1PjKyKnGa4ohYZ_CuYAXlDiVJy_uFk7Om9JC8eOCwrvlzDpE/s200/IMG_0008.PNG" /></a></div><a name='more'></a><br />
If you were wondering how could you re-arrange the UI to appear above the keyboard, the answer is "<strong>you don't have to</strong>". You are not required to re-arrange the elements because the split keyboard is movable. If a certain element is hiding behind the keyboard you can simply move the keyboard up or down so the keyboard will no longer be covering it. You can move the keyboard by dragging it from the keyboard hide button.ThE uSeFuLhttp://www.blogger.com/profile/14639950902262794968noreply@blogger.com0