Oct 142014
 

When you first start working with Inversion of Control (might have to write a blog post on that…) & unit testing you’ll probably go interface crazy! Interfaces are great, and they really make it easy to unit test and take full advantage of inversion of control. However, sometimes you don’t want to use one. Fortunately if you’re using design patterns properly, it’s easy to decide if you need to use an interface or not:

  • Is the class just data storage? If so, it should always just be a concrete type
  • Is the class a service that does something? If so it should always have an interface

The problem is when you’ve got a class that’s mostly data storage but also has some logic to it. In this case you’ve broken ‘Separation of Concerns’ and now have a problem! In this case you’ll want to expose an interface simply to make testing easier. However, ideally you want to refactor the logic out into one or more new classes.

Oct 142014
 

It’s been a while since I’ve written anything, mostly because almost any ‘how to’ has already been written. So let’s start looking into the more difficult realm of architecture of the application rather than how to do this or that.

Encapsulation is one of the basic tenants of Object Oriented programming, and is often over-looked. In short encapsulation means hiding the internal functionality away from the consumer of your class. But what does that actually mean?

It means you’ve got some difficult decisions to make with each class you build! To make my point I’m going to use a class that accesses a database. In this class we’re going to save people, think of it like a contacts list. This ‘PeopleList’ is going to save people, and then later fetch people from a database. A naive approach might have the following methods:

  • InsertPerson
  • UpdatePerson
  • DeletePerson
  • GetPerson
  • GetPeople

Where has the encapsulation been lost? It’s been lost with the Insert/Update methods, both of which are shouting that you’re using a SQL database and you’re exposing the underlying requirements of the DB to the user of your class. The users of your class doesn’t care about the database you use, just that they want to persist people and then fetch them. Differentiating between inserts and updates is not relevant to the users of the class, and just adds additional work for them.

This is one of the most important things you must do each time you design a class – consider what does the user of the class want to do? You then make that task as easy as possible. You only write the class once, but this class might be used many thousands of times over many years. A better approach would be to have the following methods instead:

  • SavePerson
  • DeletePerson
  • GetPerson
  • GetPeople

Now your class is much simpler to use. Does the user have to consider if they have already saved the person anymore? No, now all they need to do is throw a person at your class and you worry about the details. You’ve encapsulated the functionality so that the user doesn’t have to worry about logic you should be worrying about.

The great thing about doing this, is the underlying data store of this class can now be changed without any changes to the public interface of the class. We could use web service instead, and you’re still handling the logic yourself. If the web services are well designed you’ve effectively reduced the query count by one since the user of your class won’t have to check if the person exists already or not. Not only is it easier to use your new class, but it’s faster too!

Coming soon: Encapsulation Part 2!

Jan 222013
 

AutoResizeTextView Example

If you need your TextView to automatically shrink the text to fit (rather than truncating), Andreas Krings has a great solution. However, this solution is written in Java rather than C#. So here I present a version ported to Mono for Android using C#.

The implementation is pretty much identical, with a default minimum size of 10dp. The TextView will then attempt to find the largest size possible for the text that will fit the frame (the one limitation is that only the horizontal dimension is taken into account), with a maximum size of the initial size; and a minimum size of 10dp (unless overridden).  Usage is the same as well:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.danclarke.AutoResizeTextView"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:text="Normal TextView"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView1" />
    <TextView
        android:text="@string/longString"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="25dp"
        android:height="50dp" />

    <TextView
        android:text="AutoResizeTextView"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView2"
        android:layout_marginTop="20dp" />
    <AutoResizeTextView.Controls.AutoResizeTextView
        android:text="@string/longString"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="25dp"
        android:height="50dp" />

    <TextView
        android:text="AutoResizeTextView with minTextSize"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView3"
        android:layout_marginTop="20dp" />
    <AutoResizeTextView.Controls.AutoResizeTextView
        android:text="@string/longString"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="25dp"
        android:height="50dp"
        app:minTextSize="20dp" />
</LinearLayout>

Don’t forget to view the full project at GitHub!

Code Listing

For the impatient, here’s the full control’s code listing:

using System;

using Android.Content;
using Android.Runtime;
using Android.Widget;
using Android.Util;
using Android.Graphics;

namespace AutoResizeTextView.Controls
{
    /// <summary>
    /// TextView that automatically resizes it's content to fit the layout dimensions
    /// </summary>
    /// <remarks>Port of: http://ankri.de/autoscale-textview/</remarks>
    public class AutoResizeTextView : TextView
    {
        /// <summary>
        /// How close we have to be to the perfect size
        /// </summary>
        private const float Threshold = .5f;
        
        /// <summary>
        /// Default minimum text size
        /// </summary>
        private const float DefaultMinTextSize = 10f;
        
        private Paint _textPaint;
        private float _preferredTextSize;
        
        public AutoResizeTextView(Context context) : base(context)
        {
            Initialise(context, null);
        }
        
        public AutoResizeTextView(Context context, IAttributeSet attrs) : base(context, attrs)
        {
            Initialise(context, attrs);
        }
        
        public AutoResizeTextView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
        {
            Initialise(context, attrs);
        }
        
        // Default constructor override for MonoDroid
        public AutoResizeTextView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
            Initialise(null, null);
        }
        
        private void Initialise(Context context, IAttributeSet attrs)
        {
            _textPaint = new Paint();
            
            if (context != null && attrs != null)
            {
                var attributes = context.ObtainStyledAttributes(attrs, Resource.Styleable.AutoResizeTextView);
                MinTextSize = attributes.GetDimension(Resource.Styleable.AutoResizeTextView_minTextSize, DefaultMinTextSize);
                attributes.Recycle();
                
                _preferredTextSize = TextSize;
            }
        }
        
        /// <summary>
        /// Minimum text size in actual pixels
        /// </summary>
        public float MinTextSize { get; set; }
        
        /// <summary>
        /// Resize the text so that it fits.
        /// </summary>
        /// <param name="text">Text</param>
        /// <param name="textWidth">Width of the TextView</param>
        protected virtual void RefitText(string text, int textWidth)
        {
            if (textWidth <= 0 || string.IsNullOrWhiteSpace(text))
                return;
            
            int targetWidth = textWidth - PaddingLeft - PaddingRight;
            _textPaint.Set(this.Paint);
            
            while ((_preferredTextSize - MinTextSize) > Threshold)
            {
                float size = (_preferredTextSize + MinTextSize) / 2f;
                _textPaint.TextSize = size;
                
                if (_textPaint.MeasureText(text) >= targetWidth)
                    _preferredTextSize = size; // Too big
                else
                    MinTextSize = size; // Too small
            }
            
            SetTextSize(ComplexUnitType.Px, MinTextSize);
        }
        
        protected override void OnTextChanged(Java.Lang.ICharSequence text, int start, int before, int after)
        {
            base.OnTextChanged(text, start, before, after);
            
            RefitText(text.ToString(), Width);
        }
        
        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(w, h, oldw, oldh);
            
            if (w != oldw)
                RefitText(Text, Width);
        }
    }
}
Jan 182013
 

This problem is due to the case sensitivity of the iPhone device. The splash screen must be named ‘Default.png’, capital D included. Once you’ve renamed the file you need to remove the app from your device; you also need to go into the project folder and delete everything within the ‘Build’ folder.

 Posted by at 22:46  Tagged with:
Jan 182013
 

If you’re developing a theme for Moodle 2, you probably want to support the new custom menu functionality. Problem is it uses YUI3 to make the menu ‘work’ rather than CSS alone. This means it’s a) Harder to style and b) results in a nasty ‘jump’ as the JS kicks in – you have to style both pre- and post- JS menu. Instead why not just output a normal nested unordered list and use CSS to provide the functionality? Well, you can! You need to add a little PHP first though. Create a file ‘renderers.php’ in your theme directory and add this code (make sure to change <theme name> to the name of your theme):

<?php
class theme_<theme name>_core_renderer extends core_renderer
{
    /**
     * Renders a custom menu object
     *
     * @staticvar int $menucount
     * @param custom_menu $menu
     * @return string
     */
    protected function render_custom_menu(custom_menu $menu) {
        static $menucount = 0;
        // If the menu has no children return an empty string
        if (!$menu->has_children()) {
            return '';
        }
        // Increment the menu count. This is used for ID's that get worked with
        // in JavaScript as is essential
        $menucount++;
        $content .= html_writer::start_tag('ul', array('class'=>'custommenu'));
        // Render each child
        foreach ($menu->get_children() as $item) {
            $content .= $this->render_custom_menu_item($item);
        }
        // Close the open tags
        $content .= html_writer::end_tag('ul');
        // Return the custom menu
        return $content;
    }
    /**
     * Renders a custom menu node as part of a submenu
     *
     * @see render_custom_menu()
     *
     * @staticvar int $submenucount
     * @param custom_menu_item $menunode
     * @return string
     */
    protected function render_custom_menu_item(custom_menu_item $menunode) {
        // Required to ensure we get unique trackable id's
        static $submenucount = 0;
        if ($menunode->has_children()) {
            // If the child has menus render it as a sub menu
            $submenucount++;
            $content = html_writer::start_tag('li');
            if ($menunode->get_url() !== null) {
                $url = $menunode->get_url();
            } else {
                $url = '#cm_submenu_'.$submenucount;
            }
            $content .= html_writer::link($url, $menunode->get_text(), array('title'=>$menunode->get_title()));
            $content .= html_writer::start_tag('ul');
            foreach ($menunode->get_children() as $menunode) {
                $content .= $this->render_custom_menu_item($menunode);
            }
            $content .= html_writer::end_tag('ul');
            $content .= html_writer::end_tag('li');
        } else {
            // The node doesn't have children so produce a final menuitem
            $content = html_writer::start_tag('li');
            if ($menunode->get_url() !== null) {
                $url = $menunode->get_url();
            } else {
                $url = '#';
            }
            $content .= html_writer::link($url, $menunode->get_text(), array('title'=>$menunode->get_title()));
            $content .= html_writer::end_tag('li');
        }
        // Return the sub menu
        return $content;
    }
}
?>

Then go into your config.php file and make sure $THEME->rendererfactory is set like so:

$THEME->rendererfactory = 'theme_overridden_renderer_factory';

And now you should have nice plain HTML output ready for styling the proper way!

Jan 182013
 

Chances are you want to have slightly different content for the home page of your WordPress blog. In theory it should be easy enough, use is_home() or is_front_page(). Sadly these methods are hit and miss to say the least. Instead try using:

if ($_SERVER["REQUEST_URI"] == '/' || $_SERVER["REQUEST_URI"] == '/index.php')
{
    // Your home page specific code here
}

It’s not ideal, but it works.

Jan 182013
 

[ENV] b0 Your server does not have the mime_magic extension enabled. Mahara may have trouble detecting file types.

Getting that error? You need to enable or install the fileinfo extension. If you’re on Windows, open up the php.ini file and uncomment:

extension=php_fileinfo.dll

You can now install Mahara! The mime_magic extension is no longer available in new versions of PHP.

 Posted by at 22:34  Tagged with:
Jan 172013
 

I’ve been sitting on this for far too long – it’s about time I released it into the wild! After blowing through my usage, my ISP (Aquiss) told me of an undocumented webservice I could use to fetch usage information. Since I was in the middle of a lot of iOS development, I knocked together a Mac app using MonoMac so I could keep track of the usage easily. I really wanted to add some additional functionality, such as logging of usage over time and a little graphing – unfortunately I haven’t been able to find the time to implement these features. Hopefully some time in the future!

Aquiss Usage Checker - Usage Screen

The app is relatively simple, you run it, provide it with your unique hashcode and the app will do the rest. The icon in the menu bar starts out green, and gradually turns red as you approach your limit. It also notifies you at 75% and 95% usage with a popup message box.

Aquiss Usage Checker - Menu Bar Icon

I’ve released the app as a complete opensource project under the GPL license, you can grab the source over at GitHub. When I get a high-res logo from Aquiss I’ll be able to release a binary, as well as a version to the AppStore. I don’t know how many Mac customers Aquiss have, but I hope all 1-10 of them find the app useful.