micromax canvas hd lollipop update
I rewrite this tutorial from forum.xda-developers.com : The users of Micromax A116 Canvas Hd can now update their handsets to Android 5.0 Lo...
The Blog You Will Love To Revisit.
NSURLSession. I talked about the advantages it has over NSURLConnection and how to use NSURLSession for simple tasks, such as fetching data from a web service and downloading an image from the web. In this tutorial, we'll take a closer look at the configuration options of NSURLSession and how to cancel and resume a download task. We've got a lot of ground to cover so let's get started.NSURLSession, is a configurable container for putting network requests into. The configuration of the session is handled by an instance of NSURLSessionConfiguration.NSURLConnection, which relied on a global configuration object with much less flexibility.NSURLSessionConfiguration instance, the session's configuration cannot be modified. If you need to modify a session's configuration, you have to create a new session. Keep in mind that it is possible to copy a session's configuration and modify it, but the changes have no effect on the session from which the configuration was copied.NSURLSessionConfiguration class provides three factory constructors for instantiating standard configurations, defaultSessionConfiguration, ephemeralSessionConfiguration, and backgroundSessionConfiguration. The first method returns a copy of the default session configuration object, which results in a session that behaves similarly to an NSURLConnection object in its standard configuration. Altering a session configuration obtained through the defaultSessionConfiguration factory method doesn't change the default session configuration which it's a copy of.ephemeralSessionConfiguration factory method ensures that the resulting session uses no persistent storage for cookies, caches, or credentials. In other words, cookies, caches, and credentials are kept in memory. Ephemeral sessions are therefore ideal if you need to implement private browsing, something that simply wasn't possible before the introduction of NSURLSession.backgroundSessionConfiguration: factory method creates a session configuration object that enables out-of-process uploads and downloads. The upload and download tasks are managed by a background daemon and continue to run even if the application is suspended or crashes. We'll talk more about background sessions later in this series.defaultSessionConfiguration factory method to create a NSURLSessionConfiguration instance. Configuring a session configuration object is as simple as modifying its properties as shown in the example. We can then use the session configuration object to instantiate a session object. The session object serves as a factory for data, upload, and download tasks, with each task corresponding to a single request. In the example below, we query the iTunes Search API as we did in the previous tutorial.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | // Create Session ConfigurationNSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];// Configure Session Configuration[sessionConfiguration setAllowsCellularAccess:YES];[sessionConfiguration setHTTPAdditionalHeaders:@{ @"Accept" : @"application/json" }];// Create SessionNSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];// Send Request[[session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]);}] resume]; |
HTTPAdditionalHeaders property of the session configuration object. The beauty of the NSURLSession API is that every request that passes through the session is configured by the session's configuration object. Adding authentication headers to a set of requests, for example, becomes easy as pie.NSURLSession API. However, network connections are unreliable and it happens all too often that a download fails due to a flaky network connection. Fortunately, resuming a download isn't difficult with the NSURLSession API. In the next example, I'll show you how to cancel and resume the download of an image.cancelByProducingResumeData: on it. It accepts a completion handler that accepts one parameter, an NSData object that is used to resume the download at a later time by invoking downloadTaskWithResumeData: or downloadTaskWithResumeData:completionHandler: on a session object. The NSData object contains the necessary information to resume the download task where it left off.01 02 03 04 05 06 07 08 09 10 11 12 13 | #import <UIKit/UIKit.h>@interface MTViewController : UIViewController@property (weak, nonatomic) IBOutlet UIButton *cancelButton;@property (weak, nonatomic) IBOutlet UIButton *resumeButton;@property (weak, nonatomic) IBOutlet UIImageView *imageView;@property (weak, nonatomic) IBOutlet UIProgressView *progressView;- (IBAction)cancel:(id)sender;- (IBAction)resume:(id)sender;@end |

MTViewController.m and declare one instance variable and two properties. The instance variable, session, will keep a reference to the session we'll use for downloading the image.01 02 03 04 05 06 07 08 09 10 | #import "MTViewController.h"@interface MTViewController () <NSURLSessionDelegate, NSURLSessionDownloadDelegate> { NSURLSession *_session;}@property (strong, nonatomic) NSURLSessionDownloadTask *downloadTask;@property (strong, nonatomic) NSData *resumeData;@end |
viewDidLoad method, but first I'd like to implement a getter method for the session. Its implementation is pretty straightforward as you can see below. We create a session configuration object using the defaultSessionConfiguration factory method and instantiate the session object with it. The view controller serves as the session's delegate.01 02 03 04 05 06 07 08 09 10 11 | - (NSURLSession *)session { if (!_session) { // Create Session Configuration NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Create Session _session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil]; } return _session;} |
session accessor implemented, the viewDidLoad method becomes much simpler. We create a download task, as we did in the previous tutorial, and store a reference to the task in downloadTask. We then tell the download task to resume.1 2 3 4 5 6 7 8 9 | - (void)viewDidLoad { [super viewDidLoad]; // Create Download Task self.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:@"http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Resume Download Task [self.downloadTask resume];} |
cancel: action contains the logic for canceling the download task we just created. If downloadTask is not nil, we call cancelByProducingResumeData: on the task. This method accepts one parameter, a completion block. The completion block also takes one parameter, an instance of NSData. If resumeData is not nil, we store a reference to the data object in view controller's resumeData property.resumeData parameter is nil. Not every download is resumable so it's important to check if resumeData is a valid NSData object.01 02 03 04 05 06 07 08 09 10 11 12 | - (IBAction)cancel:(id)sender { if (!self.downloadTask) return; // Hide Cancel Button [self.cancelButton setHidden:YES]; [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) { if (!resumeData) return; [self setResumeData:resumeData]; [self setDownloadTask:nil]; }];} |
resume: action, we check if the view controller's resumeData property is set. If resumeData is a valid NSData object, we tell the session object to create a new download task and pass it the NSData object. This is all the session object needs to recreate the download task that we canceled in the cancel: action. We then tell the download task to resume and set resumeData to nil.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | - (IBAction)resume:(id)sender { if (!self.resumeData) return; // Hide Resume Button [self.resumeButton setHidden:YES]; // Create Download Task self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData]; // Resume Download Task [self.downloadTask resume]; // Cleanup [self setResumeData:nil];} |
viewDidLoad, hide the buttons and add the view controller as an observer of itself for the resumeData and downloadTask key paths.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | - (void)viewDidLoad { [super viewDidLoad]; // Add Observer [self addObserver:self forKeyPath:@"resumeData" options:NSKeyValueObservingOptionNew context:NULL]; [self addObserver:self forKeyPath:@"downloadTask" options:NSKeyValueObservingOptionNew context:NULL]; // Setup User Interface [self.cancelButton setHidden:YES]; [self.resumeButton setHidden:YES]; // Create Download Task self.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:@"http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Resume Download Task [self.downloadTask resume];} |
observeValueForKeyPath:ofObject:change:context:, we hide the cancel button if resumeData is nil and we hide the resume button if downloadTask is nil. Build the project and run the application one more time to see the result. This is better. Right?01 02 03 04 05 06 07 08 09 10 11 12 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"resumeData"]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.resumeButton setHidden:(self.resumeData == nil)]; }); } else if ([keyPath isEqualToString:@"downloadTask"]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.cancelButton setHidden:(self.downloadTask == nil)]; }); }} |
observeValueForKeyPath:ofObject:change:context: is called on the main thread. It is therefore important to update the user interface in a GCD (Grand Central Dispatch) block that is invoked on the main queue. NSURLSession that I haven't talked about yet, session invalidation. The session keeps a strong reference to its delegate, which means that the delegate isn't released as long as the session is active. To break this reference cycle, the session needs to be invalidated. When a session is invalidated, active tasks are canceled or finished, and the delegate is sent a URLSession:didBecomeInvalidWithError: message and the session releases its delegate.URLSession:downloadTask:didFinishDownloadingToURL:. The cancel button is also hidden when the download finishes.01 02 03 04 05 06 07 08 09 10 11 12 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSData *data = [NSData dataWithContentsOfURL:location]; dispatch_async(dispatch_get_main_queue(), ^{ [self.cancelButton setHidden:YES]; [self.progressView setHidden:YES]; [self.imageView setImage:[UIImage imageWithData:data]]; }); // Invalidate Session [session finishTasksAndInvalidate];} |
resumeData object to disk for later use and it may be possible that several download tasks are running at the same time. Even though this adds complexity, the basic principles remain the same. Be sure to prevent memory leaks by always invalidating a session that you no longer need.Child classes should never break the parent class' type definitions.The concept of this principle was introduced by Barbara Liskov in a 1987 conference keynote and later published in a paper together with Jannette Wing in 1994. Their original definition is as follows:
Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.Later on, with the publication of the SOLID principles by Robert C. Martin in his book Agile Software Development, Principles, Patterns, and Practices and then republished in the C# version of the book Agile Principles, Patterns, and Practices in C#, the definition became known as the Liskov Substitution Principle.
Subtypes must be substitutable for their base types.As simple as that, a subclass should override the parent class' methods in a way that does not break functionality from a client's point of view. Here is a simple example to demonstrate the concept.
01 02 03 04 05 06 07 08 09 10 | class Vehicle { function startEngine() { // Default engine start functionality } function accelerate() { // Default acceleration functionality }} |
Vehicle - it may be abstract - and two implementations:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Car extends Vehicle { function startEngine() { $this->engageIgnition(); parent::startEngine(); } private function engageIgnition() { // Ignition procedure }}class ElectricBus extends Vehicle { function accelerate() { $this->increaseVoltage(); $this->connectIndividualEngines(); } private function increaseVoltage() { // Electric logic } private function connectIndividualEngines() { // Connection logic }} |
Vehicle.1 2 3 4 5 6 | class Driver { function go(Vehicle $v) { $v->startEngine(); $v->accelerate(); }} |

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Rectangle { private $topLeft; private $width; private $height; public function setHeight($height) { $this->height = $height; } public function getHeight() { return $this->height; } public function setWidth($width) { $this->width = $width; } public function getWidth() { return $this->width; }} |
Rectangle. It is just a simple data object with setters and getters for width and height. Imagine that our application is working and it is already deployed to several clients. Now they need a new feature. They need to be able to manipulate squares.Square class that extends a Rectangle class. It is frequently said that a child class is a parent class, and this expression also conforms to LSP, at least at first sight.
Square really a Rectangle in programming?01 02 03 04 05 06 07 08 09 10 11 12 | class Square extends Rectangle { public function setHeight($value) { $this->width = $value; $this->height = $value; } public function setWidth($value) { $this->width = $value; $this->height = $value; }} |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | class Client { function areaVerifier(Rectangle $r) { $r->setWidth(5); $r->setHeight(4); if($r->area() != 20) { throw new Exception('Bad area!'); } return true; }} |
1 2 3 | function area() { return $this->width * $this->height;} |
Rectangle class to provide the area.1 2 3 4 5 6 7 8 9 | class LspTest extends PHPUnit_Framework_TestCase { function testRectangleArea() { $r = new Rectangle(); $c = new Client(); $this->assertTrue($c->areaVerifier($r)); }} |
Square class is correctly defined, sending it to the Client's areaVerifier() should not break its functionality. After all, a Square is a Rectangle in all mathematical sense. But is our class?1 2 3 4 5 | function testSquareArea() { $r = new Square(); $c = new Client(); $this->assertTrue($c->areaVerifier($r));} |
1 2 3 4 5 | PHPUnit 3.7.28 by Sebastian Bergmann.Exception : Bad area!#0 /paht/: /.../.../LspTest.php(18): Client->areaVerifier(Object(Square))#1 [internal function]: LspTest->testSquareArea() |
Square class is not a Rectangle after all. It breaks the laws of geometry. It fails and it violates the Liskov Substitution Principle.
Car or Bus class implementing all the methods on the Vehicle interface. Only the sheer dimensions of such classes should tell us to avoid them at all costs.LightsControl, SpeedControl, or RadioCD which are all implementing the whole interface but actually providing something useful only for the parts they implement.

Car becomes the client instead of the implementation. We still want to provide to our clients ways to use our whole module, that being a type of vehicle.
BusStation, HighWay, Driver and so on, to use whatever thew want from the interface's implementation. Basically, this shifts the behavior selection responsibility to the clients. You can find this kind of solution in many older applications.The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.However, this solution has its problems. Now all the clients depend on all the methods. Why should a
BusStation depend on the state of lights of the bus, or on the radio channels selected by the driver? It should not. But what if it does? Does it matter? Well, if we think about the Single Responsibility Principle, it is a sister concept to this one. If BusStation depends on many individual implementations, not even used by it, it may require changes if any of the individual small implementations change. This is especially true for compiled languages, but we can still see the effect of the LightControl change impacting BusStation. These things should never happen.


Service in AndroidManifest.xml that uses the BIND_INPUT_METHOD permission, and responds to the action android.view.InputMethod.application tag of the manifest:1 2 3 4 5 6 7 8 9 | <service android:name=".SimpleIME" android:label="@string/simple_ime" android:permission="android.permission.BIND_INPUT_METHOD" > <meta-data android:name="android.view.im" android:resource="@xml/method"/> <intent-filter> <action android:name="android.view.InputMethod" /> </intent-filter> </service> |
service tag in the manifest file containes a meta-data tag that references an XML file named method.xml. Without this file, the Android operating system won't recognize our Service as a valid IME service. The file contains details about the input method and its subtypes. For our keyboard, we define a single subtype for the en_US locale. Create the directory res/xml if it doesn't exist, and add the file method.xml to it. The contents of the file should be:1 2 3 4 5 6 7 | <?xml version="1.0" encoding="utf-8"?> <subtype android:label="@string/subtype_en_US" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" /></input-method> |
1 2 3 4 5 | <resources> <string name="app_name">SimpleKeyboard</string> <string name="simple_ime">Simple IME</string> <string name="subtype_en_US">English (US)</string></resources> |
KeyboardView. The layout_alignParentBottom attribute is set to true so that keyboard appears at the bottom of the screen.1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="UTF-8"?><android.inputmethodservice.KeyboardView android:id="@+id/keyboard" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:keyPreviewLayout ="@layout/preview"/> |
keyPreviewLayout is the layout of the short-lived pop-up that shows up whenever a key on the keyboard is pressed. It contains a single TextView. Create a file named res/layout/preview.xml and add the following to it:01 02 03 04 05 06 07 08 09 10 | <?xml version="1.0" encoding="utf-8"?> android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:background="#ffff00" android:textStyle="bold" android:textSize="30sp" > </TextView> |
keyLabel: This attribute contains the text that is displayed on the key.codes: This attribute contains the unicode values of the characters that the key represents.codes attribute should have the value 97 and the keyLabel attribute should be set to A.keyEdgeFlags: This attribute can take the value left or right. This attribute is usually added to the leftmost and rightmost keys of a row.keyWidth: This attribute defines the width of a key. It's usually defined as a percentage value.isRepeatable: If this attribute is set to true, long-pressing the key will repeat the action of the key multiple times. It is usually set to true for the delete and spacebar keys.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | android:keyWidth="10%p" android:horizontalGap="0px" android:verticalGap="0px" android:keyHeight="60dp"> <Row> <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/> <Key android:codes="50" android:keyLabel="2"/> <Key android:codes="51" android:keyLabel="3"/> <Key android:codes="52" android:keyLabel="4"/> <Key android:codes="53" android:keyLabel="5"/> <Key android:codes="54" android:keyLabel="6"/> <Key android:codes="55" android:keyLabel="7"/> <Key android:codes="56" android:keyLabel="8"/> <Key android:codes="57" android:keyLabel="9"/> <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/> </Row> <Row> <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> <Key android:codes="119" android:keyLabel="w"/> <Key android:codes="101" android:keyLabel="e"/> <Key android:codes="114" android:keyLabel="r"/> <Key android:codes="116" android:keyLabel="t"/> <Key android:codes="121" android:keyLabel="y"/> <Key android:codes="117" android:keyLabel="u"/> <Key android:codes="105" android:keyLabel="i"/> <Key android:codes="111" android:keyLabel="o"/> <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> </Row> <Row> <Key android:codes="97" android:keyLabel="a" android:keyEdgeFlags="left"/> <Key android:codes="115" android:keyLabel="s"/> <Key android:codes="100" android:keyLabel="d"/> <Key android:codes="102" android:keyLabel="f"/> <Key android:codes="103" android:keyLabel="g"/> <Key android:codes="104" android:keyLabel="h"/> <Key android:codes="106" android:keyLabel="j"/> <Key android:codes="107" android:keyLabel="k"/> <Key android:codes="108" android:keyLabel="l"/> <Key android:codes="35,64" android:keyLabel="\# \@" android:keyEdgeFlags="right"/> </Row> <Row> <Key android:codes="-1" android:keyLabel="CAPS" android:keyEdgeFlags="left"/> <Key android:codes="122" android:keyLabel="z"/> <Key android:codes="120" android:keyLabel="x"/> <Key android:codes="99" android:keyLabel="c"/> <Key android:codes="118" android:keyLabel="v"/> <Key android:codes="98" android:keyLabel="b"/> <Key android:codes="110" android:keyLabel="n"/> <Key android:codes="109" android:keyLabel="m"/> <Key android:codes="46" android:keyLabel="."/> <Key android:codes="63,33,58" android:keyLabel="\? ! :" android:keyEdgeFlags="right"/> </Row> <Row android:rowEdgeFlags="bottom"> <Key android:codes="44" android:keyLabel="," android:keyWidth="10%p" android:keyEdgeFlags="left"/> <Key android:codes="47" android:keyLabel="/" android:keyWidth="10%p" /> <Key android:codes="32" android:keyLabel="SPACE" android:keyWidth="40%p" android:isRepeatable="true"/> <Key android:codes="-5" android:keyLabel="DEL" android:keyWidth="20%p" android:isRepeatable="true"/> <Key android:codes="-4" android:keyLabel="DONE" android:keyWidth="20%p" android:keyEdgeFlags="right"/> </Row> </Keyboard> |
codes attribute. Negative values are equal to predefined constants in the Keyboard class. For example, the value -5 is equal to the value of Keyboard.KEYCODE_DELETE.Service ClassInputMethodService class and implement the OnKeyboardActionListener interface. The OnKeyboardActionListener interface contains the methods that are called when keys of the soft keyboard are tapped or pressed.SimpleIME class should have three member variables:KeyboardView referencing the view defined in the layoutKeyboard instance that is assigned to the KeyboardViewboolean telling us if the caps lock is enabledOnKeyboardActionListener interface, the SimpleIME class should look like this:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class SimpleIME extends InputMethodService implements OnKeyboardActionListener{ private KeyboardView kv; private Keyboard keyboard; private boolean caps = false; @Override public void onKey(int primaryCode, int[] keyCodes) { } @Override public void onPress(int primaryCode) { } @Override public void onRelease(int primaryCode) { } @Override public void onText(CharSequence text) { } @Override public void swipeDown() { } @Override public void swipeLeft() { } @Override public void swipeRight() { } @Override public void swipeUp() { }} |
onCreateInputView method is called. All the member variables of the Service can be initialized here. Update the implementation of the onCreateInputView method as shown below:1 2 3 4 5 6 7 8 | @Overridepublic View onCreateInputView() { kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null); keyboard = new Keyboard(this, R.xml.qwerty); kv.setKeyboard(keyboard); kv.setOnKeyboardActionListener(this); return kv;} |
AudioManager class to play the sounds. The Android SDK includes a few default sound effects for key presses and those are used in the playClick method.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | private void playClick(int keyCode){ AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE); switch(keyCode){ case 32: am.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR); break; case Keyboard.KEYCODE_DONE: case 10: am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN); break; case Keyboard.KEYCODE_DELETE: am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE); break; default: am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD); } } |
onKey method so that our keyboard app can communicate with input fields (usually EditText views) of other applications.getCurrentInputConnection method is used to get a connection to the input field of another application. Once we have the connection, we can use the following methods:commitText to add one or more characters to the input fielddeleteSurroundingText to delete one or more characters of the input fieldsendKeyEvent to send events, like KEYCODE_ENTER, to the external applicationonKey method is called with the unicode value of the key as one of its parameters. Based on this value, the keyboard performs one of the following actions:KEYCODE_DELETE, one character to the left of the cursor is deleted using the deleteSurroundingText method.KEYCODE_DONE, a KEYCODE_ENTER key event is fired.KEYCODE_SHIFT, the value of the caps variable is changed and the shift state of the keyboard is updated using the setShifted method. The keyboard needs to be redrawn when the state changes so that the labels of the keys are updated. The invalidateAllKeys method is used to redraw all keys.caps variable is set to true, then the character is converted to uppercase.onKey method so that it looks like this:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Overridepublic void onKey(int primaryCode, int[] keyCodes) { InputConnection ic = getCurrentInputConnection(); playClick(primaryCode); switch(primaryCode){ case Keyboard.KEYCODE_DELETE : ic.deleteSurroundingText(1, 0); break; case Keyboard.KEYCODE_SHIFT: caps = !caps; keyboard.setShifted(caps); kv.invalidateAllKeys(); break; case Keyboard.KEYCODE_DONE: ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); break; default: char code = (char)primaryCode; if(Character.isLetter(code) && caps){ code = Character.toUpperCase(code); } ic.commitText(String.valueOf(code),1); }} |
Activity, which means that it won't show up in the launcher. To use it, it should first be activated in the device's Settings.

Get our latest updates direct in your inbox.Just enter your wail address below....
Your privacy and email address are safe with us!