Monday, 21 November 2016

Android material theme text input - changing colours of underline and floating label

Android styling/theming is an unholy mess, and I battled once again today with trying to get an input field to look like this:


This is how it's done - the important thing to note is that these style items go on your main app theme. Applying them directly to a specific input-field-only style/theme will not work.


Thursday, 3 November 2016

Android configuration qualifiers for alternative resources

I discovered today that there's loads more configuration qualifiers for providing alternative Android resources than I was aware of. Here's a handy table, taken from here.


Configuration
Example Qualifier Values
MCC and MNC
Examples:mcc310
mcc310-mnc004
mcc208-mnc00
etc.
Language and region
Examples:en
fr
en-rUS
fr-rFR
fr-rCA
etc.
Layout Direction
ldrtl
ldltr
smallestWidth
swdp

Examples:
sw320dp
sw600dp
sw720dp
etc.
Available width
wdp

Examples:
w720dp
w1024dp
etc.
Available height
hdp

Examples:
h720dp
h1024dp
etc.
Screen size
small
normal
large
xlarge
Screen aspect
long
notlong
Round screen
round
notround
Screen orientation
port
land
UI mode
car
desk
television
appliancewatch
Night mode
night
notnight
Screen pixel density (dpi)
ldpi
mdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
nodpi
tvdpi
anydpi
Touchscreen type
notouch
finger
Keyboard availability
keysexposed
keyshidden
keyssoft
Primary text input method
nokeys
qwerty
12key
Navigation key availability
navexposed
navhidden
Primary non-touch navigation method
nonav
dpad
trackball
wheel
Platform Version (API level)
Examples:v3
v4
v7
etc.

Monday, 31 October 2016

Creating a Toast equivalent in Xamarin iOS

I love Android's toast - the UI widget that allows us to tell the user something. It's simple and accessible, and something iOS lacks unless you find a plugin of some sort.

I've started a new app build recently, using Xamarin with native Android and iOS interfaces, and it was with great joy today that I found a Xamarin toast component for iOS.

Really simple to use with a using ToastIOS statement, and then the following kind of call:
Toast.MakeText("Toast!", Toast.LENGTH_LONG)
.SetFontSize(10)
.SetGravity(ToastGravity.Center)
.Show(ToastType.None);

Wednesday, 21 September 2016

Cordova phone keyboard 'Go' button event

If you need to capture the keyboard's 'Go' button event to automatically submit your form, do something like this, where I'm capturing keypresses during password field focus and checking to see if it's the Go button:

 $('#loginPassword').on('keypress',function(e){ if (e.keyCode == 13)
                {
                    loginPress(); 
                } });

Monday, 29 August 2016

Headspace app store subscription phishing scam

I got quite a convincing phishing email today, it looked like a legit app store receipt for the Headspace, for £9.99. Many of the links go to Apple, and the link at the bottom 'Cancel and Manage my subscriptions' goes to something that looks like the App Store, but had my email address appended to the end.

I removed my address and visited the link http://rdr34623.3ls-secureapp.com/index.php If you input realistic data, it asks you for more information about yourself, and you may think you're correcting your erroneous headspace subscription.

If however, like me, you put in something like f***off@youscammers.com they redirect you to google with a search for something child-porn related. These people are real nice huh.

Tuesday, 12 July 2016

JQuery mobile go back in history using Javascript

It's not clear at all in the documentation, but here it is:
$.mobile.back();
And if you want to use this to make the Android back button work properly on your Cordova app, do this:

document.addEventListener("backbutton", onBackKeyDown, false);
        function onBackKeyDown() {
            $.mobile.back();
        }

Tuesday, 21 June 2016

Cordova CLI build commands for Windows 10 and Windows Phone 8.1

By default the cordova build command produces two packages: Windows 8.1 and Windows Phone 8.1. To upgrade Windows package to version 10 the following configuration setting must be added to configuration file (config.xml).
 name="windows-target-version" value="10.0" />

Once you add this setting build command will start producing Windows 10 packages.
You may decide that you want to build a particular version of your application targeting a particular OS (for example, you might have set that you want to target Windows 10, but you want to build for Windows Phone 8.1). To do this, you can use the --appx parameter:
cordova build windows -- --appx=8.1-phone
The build system will ignore the preference set in config.xml for the target Windows version and strictly build a package for Windows Phone 8.1.
Valid values for the --appx flag are 8.1-win8.1-phone, and uap (for Windows 10 Universal Apps). These options also apply to the cordova run command.

Component requires .NET Native compilation, not available targeting Windows10, AnyCPU

I've been having more pain with Cordova for Windows phones. I updated my Cordova CLI to 6.2.0 to overcome a problem I was having in Cordova/lib/prepare.js when building. It seems to work, but then a new problem appeared - or perhaps it was the same problem but with a more friendly message:

The following component requires .NET Native compilation which is not available when targeting 'Windows10' and 'AnyCPU'. Please consider changing the targeted processor architecture to one of the following: 'x86, x64, ARM' (if you are using command line this could be done by adding '--archs' parameter, for example: 'cordo va build windows --archs=x64'). C:\PathToMyProject\platforms\windows\plugins\cordova-plugin-globalization\GlobalizationProxy.winmd Error: C:\Program Files (x86)\MSBuild\14.0\bin\msbuild.exe: Command failed with exit code 1
To overcome this I had to do what it said. The following command created a successful build:

cordova build windows --archs=x86 

Implementing the Adobe DMP mobile SDKs on iOS and Android

If you're unfortunate enough to be tasked with this be prepared for it to take ten times longer than it should. You'll receive documentation that is woefully unclear, from different sources, and contradictory. When you need help you'll either not get it or wait ages for it. This has been nearly-the-worst DX I've had implementing a vendor's code, hot on the heels of ExactTarget.

But here's a tip, which I only found by rummaging through the code. Instead of silently failing, if you do this:

    [ADBMobile setDebugLogging:YES];

Then a whole new world of information will be available to you, and if you're lucky ADBMobile will actually tell you what's wrong.  This debug setting was not mentioned in any of the setup documentation I was given (there is no troubleshooting section), and neither when I sought support several times from Adobe.

Had I been told about this from the start it would have saved several people a whole lot of time.


Wednesday, 8 June 2016

Android Xamarin AddJavascriptInterface

A recent change in my codeset to API 17 caused my javascript interfaces in a webview to fail. From 17 onwards it is now a requirement that you annotate your method with [Export] and [JavascriptInterface], like so:

    [Export]
    [JavascriptInterface]
    public void Run()
    {
        //do your stuff
    }

Thursday, 26 May 2016

Get boolean value from JSON, in Obj C

myObject.MyBoolValue = [[myDictionary objectForKey:@"TheNameOfTheJsonValue"]boolValue];

Friday, 13 May 2016

Android Xamarin ViewPager cacheing - unable to renew contents

I just 'wasted' a couple of hours trying to resolve a problem with a ViewPager. I have a fragment that hosts a ViewPager of 7-10 fragments, all of which needed to be swapped out if the user selects a different value/setting. This was working fine in some cases, but when navigating away from the parent fragment and then returning, I seemed unable to get the ViewPager to update properly, showing either old or blank content.

I spent too long stepping through my code trying to work out if I'd missed something, but came up with nothing, and figured that there must be some kind of cacheing going on.

After some reading I learnt that I should be using FragmentStatePagerAdapter and not FragmentPagerAdapter. The difference is explained here.

This, combined with the below override in my adapter, finally solved my problem.
public override int GetItemPosition(Object objectValue)
        {
            return PositionNone;
        }

As ever, I remained indebted to StackOverflow, and this is the answer that resolved it for me.

Thursday, 12 May 2016

Friday, 22 April 2016

Invoke C# from Javascript in Android using Export attribute

I've been happily invoking C# from a webview in my Xamarin Android app for some while now, but this time I needed to send a parameter from my js. Using the basic Run() was no longer an option and so I turned to a solution using the Export attribute, which I first read about here.
Instead of:
public void Run()
    {
        //do your stuff
    }
you do this:
[Export("customInvoke")]
public void CustomInvoke(string myParameter)
    {
       //do your stuff
    }

Tuesday, 29 March 2016

autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"

How to stop an HTML input field thinking it knows best on mobile:
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"

Monday, 28 March 2016

Capturing touch events in iOS/Android webviews

I have some HTML content that is shared between a Xamarin Android app and an ObjC/Swift native iOS app. In the HTML are a few buttons that need to trigger events in the native code of each app. The techniques for capturing these events are well documented, but one has to remember that a touch event might actually be a scroll or a swipe, and the use may not be trying to 'click' the element in question. I overcame this problem by using a boolean flag to see if we're moving:
var moved;
$('.myElement').on('touchend', function(e){
    if(moved != true){
        //do your thing
    }
}).on('touchmove', function(e){
    moved = true;
}).on('touchstart', function(){
    moved = false;
});

Thursday, 10 March 2016

Xamarin Android get image from Assets


            var ims = AppContext.Assets.Open("path/to/my/file/in/assets");
            var drawable = Drawable.CreateFromStream(ims, null);
            imageView.SetImageDrawable(drawable);


Monday, 7 March 2016

Showing layout using bootstrap

Here's a cheeky little bit of css that will make your layout clear when you're building UI with bootsrap. Really handy for building and debugging:

[class*="span"] { background: #EEF; }
[class*="span"] [class*="span"] { background: #FEE; }

Wednesday, 2 March 2016

Xamarin Android get drawable by filename

I had to tap into a carousel event and change a background image when it was swiped. The contents of the carousel were populated by a list of objects, with the image file name as a string property. I had to get the drawable with its filename, and prevent the process (getting the file from disk) blocking the UI. Once obtained I had to update the image on the UI thread.

 ThreadPool.QueueUserWorkItem(o =>
            {
                var imgId = Resources.GetIdentifier(_tips[position].Image, "drawable", AppContext.PackageName);
                var drawable = Resources.GetDrawable(imgId);
                RunOnUiThread(() =>
                {
                    _bgImage.SetImageDrawable(drawable);
                });
            });

Monday, 29 February 2016

iOS Swift animation - shake element on input error

We're used to seeing an input element shake when input has failed validation, like a person shaking their head at you. Here's the code to create the animation:

public func errorShake(element: UIView!){
    
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.07
        animation.repeatCount = 4
        animation.autoreverses = true
        animation.fromValue = NSValue(CGPoint: CGPointMake(element.center.x - 10, element.center.y))
        animation.toValue = NSValue(CGPoint: CGPointMake(element.center.x + 10, element.center.y))
        element.layer.addAnimation(animation, forKey: "position")
    
    }

Thursday, 25 February 2016

Morph a view into another in Swift

Today I was tasked with morphing a button, when tapped, into another view that I'd already constructed of a bespoke date selector.

I achieved this by adding an extension to UIView, with a function that animates the bounds, frame and background colour of one view to another:


   func morphToView(duration:Double,delay:Double,toView:UIView){
        
        UIView.animateWithDuration(duration, delay: delay, usingSpringWithDamping: 1.0, initialSpringVelocity: 10, options: [], animations: {
            self.bounds = toView.bounds;
            self.frame = toView.frame
            self.backgroundColor = toView.backgroundColor
            
            }, completion: nil)

    }

making it super easy to use:

btnYes.morphToView(1.3, delay: 0.0, toView: dateSelector)

Thursday, 18 February 2016

Xamarin Android aapt.exe exited with code 1, Resources.deisgner.cs not regenerating

Wasted ages on this - Resources.deisgner.cs was not updating and eventually I was getting an error "Xamarin Android aapt.exe exited with code 1".

The solution turned out be simple - I'd added some images to my resources folder that contained hyphens in their filenames. I removes these, and everything started working again.

Xamarin Android detect Android version and perform circular reveal

Working on a simple circular reveal using Animator, I needed to detect if we were running  >=Lollipop or not. This is how I did it. I'll probably refactor this somewhat but here's the headlines:


           
           var _circle = FindViewById<imageview>(Resource.Id.imageView1);
           var currentapiVersion = Android.OS.Build.VERSION.SdkInt;
            if (currentapiVersion >= Android.OS.BuildVersionCodes.Lollipop)
            {
                // Do something for lollipop and above versions
                // get the center for the clipping circle
                int cx = _circle.Width / 2;
                int cy = _circle.Height / 2;

                // get the final radius for the clipping circle
                var finalRadius = (float)Math.Sqrt(cx * cx + cy * cy);

                // create the animator for this view (the start radius is zero)
                Animator anim =
                    ViewAnimationUtils.CreateCircularReveal(_circle, cx, cy, 0, finalRadius);

                // make the view visible and start the animation
                _circle.Visibility = ViewStates.Visible;
                anim.Start();
            }
            else
            {
                _circle.Visibility = ViewStates.Visible;
           }

Xamarin C# equivalent of Math.hypot

I'm doing some more Xamarin for the next few weeks, and today I'm working on a material-design circular reveal for an Android app. I had to convert this line of Java (amongst others) into C#:

float finalRadius = (float)Math.hypot(cx, cy);

And...the answer is.....

var finalRadius = (float)Math.Sqrt(cx * cx + cy * cy);

Thursday, 28 January 2016

Building a row-based email template in Adestra MessageFocus Email Editor

I had the pleasure of using Adestra's new (still in beta, apparently) email editor this week, and it seemed to do everything we wanted. I inherited an HTML template with 10 different rows, and the brief was to allow the user to create an email using any number of any of these rows, in any order, with editable text, links images etc.

As an overview, if you wish to implement a typical row-based system you probably have the following html structure:

 <table>  
 <!-- row type 1 here -->  
 </table>  
 <table>  
 <!-- row type 2 here -->  
 </table>  
 <table>  
 <!-- row type 3 here -->  
 </table>  
 <table>  
 <!-- row type 4 here -->  
 </table>  

To use this in the Email Editor, where you can place any type of row wherever you want any number of times you need to use alternates and a single overall repeater of the same name, like so:

 <table amf:alternate="row" amf:option="row_1" amf:repeat="row">  
 <!-- row type 1 here -->  
 </table>  
 <table amf:alternate="row" amf:option="row_2">  
 <!-- row type 2 here -->  
 </table>  
 <table amf:alternate="row" amf:option="row_3">  
 <!-- row type 3 here -->  
 </table>  
 <table amf:alternate="row" amf:option="row_4">  
 <!-- row type 4 here -->  
 </table>  

This creates the following type of experience in the Email Editor, allowing you to create/delete rows at will, and selecting which alternative you desire:



The following amf attributes create dynamic (editable) links, images, single text lines and text areas:

 <a href="www.mysite.com" amf:link="my_link">Go here</a>  
 <img src="/img/myimage.png" amf:image="image_1">  
 <span amf:textline="header_line">This is a single line</span>  
 <p amf:textbox="a_paragrpah_of_text">lots of text...</p>