Archive

Archive for the ‘Android’ Category

Material icons in Android

I’m not frequently working as Android Developer, and now i updated Android Studio to the latest version (3.0.1) and tried to create a new project starting with a “Navigation Drawer Activity”.
I also noticed that now in a project you can insert support for C++ and Kotlin, a more concise Java.
The first problem raised were the icons for the lateral menu (the “drawer”) , by default are created some icons

and examining these icons you can see that are not images, but an XML definition , for example ic_menu_camera.xml:

The problem is: ok, i want to use another icon, but how?
The solution is to use this site for the Material icons, where you can right click on an icon, choose “View Vector Drawable” from menu, then copy the XML definition and paste it in the new res/drawable created in Android Studio; for example a winner cup image is defined as:

Advertisements
Categories: Android

Update Android Studio project – Gradle issues

Last year i worked to an Android Studio project, now i should make changes and reopened Android Studio after some months.
Obvious, there were a lot of updates and Gradle began to complain about the settings, recommending changes.
The best approach is to copy the current Gradle section in some text editor, delete the lines below compile ‘junit:junit:’, right clic on App node and choose “open Module Settings”

Here on Dependencies Tab clic the green “+” and add a library dependency

then search :

So are catched the right versions.

Categories: Android

Android 6.01 on Samsung S5

Finally the new Android release on my Tim phone.
Better interface with the new rounded icons, the only problem was TomTom not starting, blocked on “Loading app”: after an uninstall and then reinstall all ok.
Update 07/28: everything seems ok, also the mirrorlink connection to my car stereo Kenwood DDX7015BT.

Categories: Android

Microsoft Xamarin

Microsoft has acquired Xamarin.
Interesting project, but you must have a solid mobile business for each major environment (Ios-Android-Windows Phone): even for MSDN subscribers the editions are still not cheap, Xamarin Business is 799$ for developer.

Categories: Android, WinPhone

Firefox needs UTF8 for GeoXml3


I’m developing an WebAPI2 solution with Angular that receives, from Android devices, GPS points.
So Latitude , Longitude , Speed etc. are saved in a SQL Server 2014 table; from the web site the headquarter would follow the real time navigation.
The chosen approach was to write an KML file from the db data, and show this KML in a GoogleMaps window.
Having local files (not on another domain) the best solution is to use the GeoXml3 library, which is relatively easy to use (examples here and here).
For the javascript part, no problem: there is an simple setTimeout that calls via jQuery an WebAPI2 method that re-write the kml (the name is the record ID), and then adding an random number to the querystring the boss can follow the persons wandering somewhere, seeing the kml line that slowly progress on the map.
But there was an problem: On IE10 , 11, Chrome, Edge all ok; instead on Firefox (Windows or Ubuntu or Android tablet) no kml shown.
Sometimes the things are discovered in a very casual manner…i tried to save the KML with another name using Notepad++ , and change the code in order to not use the API call and instead try to show immediately this second file where i wanted to test some change, in order to understand what was wrong : bingo, KML immediately visible!
This was a real head scratching…the files (the generated from the system, and the one saved from the previous) in Notepad++ was absolutely the same, no visible difference.
So i tried to compare them with WinMerge and surprise, the generated file was full of strange characters before every normal character, immediately i realized “oops, the encoding?” and only after i noticed in Notepad++ that in the Encoding menu the file saved from Notepad++ itself was UTF8 encoded, and the one produced from the C# code was without encoding.
In the WebAPI2 method effectively i used Unicode: so now, all the browsers shows the KML writing the file as:

private async Task WriteTextAsync(string filePath, string text)
{
    //byte[] encodedText = Encoding.Unicode.GetBytes(text);
    byte[] encodedText = Encoding.UTF8.GetBytes(text);
    //
    using (FileStream sourceStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 1048576, useAsync: true))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    };
}

Eclipse Luna memory problem

Some weeks ago i fought a battle with Eclipse.
I made updates to my PlaceMinder project , updating the Android SDK and some code changes.
The project was working before, now trying to launch on the device (anyone was ever able to get the emulator working?…) errors about “heap space” and “out of memory”.
I was not understanding , because another project even more complex, in another workspace, was working after the SDK updates.
The solution was to modify the eclipse.ini, by changing the last lines from the default values to:

-Xms512m
-Xmx1024m

And my app was finally installed on the device.

Categories: Android

Outlining in Eclipse

In Visual Studio 2013 by right clicking the code there is an useful menu for code outlining


Is so useful CTRL+MO…

In Eclipse Luna i struggled for a while in order to find the same feature, not easy to discover;

CTRL+SHIFT+/ on numeric keypad is the same of CTRL+MO on Vs2013; CTRL+SHIFT+* (on numeric keypad) is the same of CTRL+ML

The trick works only if is activated the folding: by right clicking on the line numbers appears the menu, with the folding activated are visible the controls (in yellow in the image) for expanding and retracting the code.

eclipsefolding

Categories: .NET, Android

WebAPI2 errors management in Android

In my last project is used an SQL Server 2014 Phones table, with an Android app that register the device itself in this table via an WebAPI2 call.

The table is simple:


We have an primarykey named “PK_Phones” on PhoneId.

The PhoneID is the IMEI code, if the device has phone capabilities and is installed an working SIM, or the Android ID.

There are other common fields for multiuser management as the timestamp which is very useful with the Entity Framework 6 used in this solution.

The Web counterpart (created with Visual Studio 2013) is a common MVC5 project using WebAPI2 with Breeze, Angular , Bootstrap, Modernizr, Moment, Ninject, Toastr…an huge blob installed from NuGet using the HotTowel package (Ninject , Jasmine for testing and other package needs to be installed by hand).

The first move was to create an ADO.NET Entity Data Model (here i don’t speak in detail of the project organization) and using “Code first from Database” i created the class representing my table:

namespace net.studioalessi.walkad.dl
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public partial class Phones
    {
        [Key]
        [StringLength(20)]
        public string PhoneId { get; set; }

        [StringLength(30)]
        public string Manufacturer { get; set; }

        [StringLength(20)]
        public string Model { get; set; }

        [StringLength(150)]
        public string Notes { get; set; }

        [StringLength(20)]
        public string PhoneNumber { get; set; }

        public bool Banned { get; set; }

        [StringLength(50)]
        public string UtenteModifica { get; set; }

        public DateTime? OraModifica { get; set; }

        [StringLength(50)]
        public string UtenteInserimento { get; set; }

        public DateTime? OraInserimento { get; set; }

        [Column(TypeName = "timestamp")]
        [MaxLength(8)]
        [Timestamp]
        public byte[] SSMATimeStamp { get; set; }
    }
}

Then the necessary Controller (right click on Controllers folder->add new scaffolded item->


Web API 2 Controller with actions, using Entity Framework as in figure.

Using the previous Phones class we generate the Controller and the Context, in our case the context is the class PhonesContext:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace net.studioalessi.walkad.dl
{
    public class PhonesContext : DbContext
    {
        public PhonesContext()
            : base("name=WalkadConn")
        {
        }

        // no virtual ? in the previous EF version was generated as virtual...
        public DbSet<Phones> Phones { get; set; }

        // Also the code below was generated automatically in the previous version.
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteModifica)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteInserimento)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.SSMATimeStamp)
                .IsFixedLength();
        }
    }
}

And the Controller is changed from the generated version to an BreezeController

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Walkad.Models;
using net.studioalessi.walkad.dl;
using Breeze.WebApi2;

namespace Walkad.Controllers
{
    [BreezeController]
    public class PhonesController : ApiController
    {
        private IPhoneRepository _repo;

        public PhonesController(IPhoneRepository repo)
        {
            _repo = repo;
        }

        [HttpGet]
        public string Metadata()
        {
            return _repo.MetaData;
        }

        [HttpPost]
        [AllowAnonymous]
        public async Task<IHttpActionResult> RegisterImei(PhoneModel model)
        {
            PhonesContext objT = new PhonesContext();
            try
            {
                Phones objTrace = new Phones
                {
                    UtenteInserimento = "RemoteUser",
                    OraInserimento = DateTime.Now,
                    Model = model.Model,
                    Manufacturer = model.Manufacturer,
                    PhoneId = model.Imei
                };
                //
                objT.Phones.Add(objTrace);
                await objT.SaveChangesAsync();
                return Ok();
            }
            catch (Exception ex)
            {
                return BadRequest(ex.ToString());
            }
            finally
            {
                //
            }
        }
    }
}

The PhoneModel was created by hand and is the model representing the JSON string sent from the client (Web site or Android app, as in this case)

public class PhoneModel
{
    [Required]
    [StringLength(15)]
    public string Imei { get; set; }

    [StringLength(30)]
    public string Manufacturer { get; set; }

    [StringLength(20)]
    public string Model { get; set; }
}

At this point we can try the service from Fiddler


By clicking “Execute” we can see that our call was successful


And in SQL Management Studio we can see the new record:


Ok , we can create the Android app with Eclipse Luna (with the Android Developer Tools).

I don’t show the details, i created an simple Android app with an menu item that calls the routine named registerThisDevice:

private void registerThisDevice() {
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    String strBasePath = sharedPrefs.getString((String)getText(R.string.pref_httpbase), "http://192.168.0.77/Walkad");
    new WebApiRegisterImei().execute(strBasePath + "/api/Phones/RegisterImei");
}

Note the i’m using the IP , the tablet used for the test is in the same WiFi network but is not Windows, is not able to resolve the NETBIOS name.

Note also that we web address is registered in a Preference that defaults to my WiFi address.

In order to follow the best practices , the routine WebApiRegisterImei is an AsyncTask so is called with .execute() passing the server address as String argument (in every case , if we don’t use an AsyncTask we get an NetworkOnMainThread exception):

private class WebApiRegisterImei extends AsyncTask<String, Void, JSONObject> {
    ProgressDialog myProgressBar;
    public String ExportResult = "";
    public String LastException = "";
    public Exception LastExceptionObject;       

    protected JSONObject doInBackground(String... params) {
        BufferedInputStream  in = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(params[0]);
            urlConnection = (HttpURLConnection) url.openConnection();
            // the Android docs says that HttpURLConnection uses the GET method by default. It will use
            // POST if setDoOutput(true) has been called. Other HTTP methods
            // (OPTIONS, HEAD, PUT, DELETE and TRACE) can be used with
            // setRequestMethod(String). Anyway forcing POST as request method is useful.
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            //
            JSONStringer vm;
            vm = new JSONStringer()
                    .object().key("Imei").value(MainActivity.ImeiId)
                             .key("Manufacturer").value(android.os.Build.MANUFACTURER)
                             .key("Model").value(android.os.Build.MODEL)
                    .endObject();

            urlConnection.setFixedLengthStreamingMode(vm.toString().getBytes().length);
            urlConnection.setRequestProperty("Content-Type", "application/json");
            urlConnection.setRequestProperty("charset", "utf-8");
            DataOutputStream wr = new DataOutputStream (
                    urlConnection.getOutputStream ());
                    wr.writeBytes (vm.toString());
                    wr.flush ();
                    wr.close ();                           
            //
            int statusCode = urlConnection.getResponseCode();
            if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                // handle unauthorized (if service requires user login)
            } else if (statusCode != HttpURLConnection.HTTP_OK) {
                InputStream errorstream = urlConnection.getErrorStream();
                BufferedReader br = null;
                if (errorstream == null){
                    InputStream inputstream = urlConnection.getInputStream();
                    br = new BufferedReader(new InputStreamReader(inputstream));
                }else{
                    br = new BufferedReader(new InputStreamReader(errorstream));
                }
                String strResponse = "";
                String strTemp;
                while ((strTemp = br.readLine()) != null){
                    strResponse += strTemp;
                }   
                strResponse = strResponse.toLowerCase();
                if(strResponse.contains("pk_phones"))
                {
                    ExportResult = getErrorDuplicated();
                    return null;
                }
            }
            //
            ExportResult = getExportCompleted();
        } catch (Exception e) {
            Mint.logException(e);
            LastException = e.toString();
            LastExceptionObject = e;                
        } finally {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    Mint.logException(e);
                }
            }
            if(urlConnection != null){
                urlConnection.disconnect();  
            }
        }
        return null;
    }

    private String getErrorDuplicated() {
        return (String)getText(R.string.imeiduplicatederr);
    }

    private String getExportCompleted(){
        return (String)getText(R.string.registerimeidone);
    }           

    @Override
    protected void onPreExecute() {
        try {
            myProgressBar = new ProgressDialog(MainActivity.thisMainActivity, ProgressDialog.STYLE_SPINNER);    
            myProgressBar.setMessage((String)getText(R.string.registerimeiwait));
            myProgressBar.show();
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }

    @Override
    protected void onPostExecute(JSONObject result) {
        try {
            myProgressBar.dismiss();
            if(LastException.length() > 0){
                Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();   
                Mint.logException(LastExceptionObject);
            }else{
                Toast.makeText(MainActivity.thisMainActivity, ExportResult, Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }
}

Examining the code , we can note that onPreExecute and onPostExecute are the only places where we can interact with the main thread and relative controls (the progress bar) so in the background part we register the exceptions details in one string and one object, examining them on the onPostExecute.

Note the use of the JSONStringer: debugging the code you can see that is created the JSON string as used in Fiddler.

If all is ok, we can see that in SQL Server is written a new row in the Phones table.

The test for statusCode != HttpURLConnection.HTTP_OK was the difficult part to discover how it works, the subject of this post.

If you reissue the command from the Android app on the same device you violate the primary key, so the WebAPI code goes in the Exception and is sent to the client “BadRequest” (HTTP error 400) instead of “Ok”(200), sending as other info ex.ToString().

The management of the returned error is done as you can read in the code: we need to examine the returned stream , by reading this stream we can create a string that contains the original ex.ToString, for example

{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"System.Data.Entity.Infrastructure.DbUpdateException: 
An error occurred while updating the entries. See the inner exception for details. ---> 
System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. 
See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_Phones'. 
Cannot insert duplicate key in object 'dbo.Phones'. The duplicate key value is…..

So in the Android code we search if the string contains the name of the primary key , giving to the user an scoped message, or we give to the user an generic message and the code uses Splunk Mint Express for registering all infos about the unmanaged exception (that could be an network failure, and so on).

Activity not created in Eclipse

Every time that there is an Android update, i’m prepared to troubles.

Recently i updated ADT to version 24.0.2 , and created a new project.

When i tried to add a new Activity, nothing! no xml for the screen and no Class.

In Logcat only an obscure message “Style contains key with bad entry”.

Also troubles with the appcompat_7 library, importing the new version in the workspace an message “No resource found that matches the given name: attr ‘android:actionModeShareDrawable'”

For appcompat, the solution was to change in manifest.xml the targetSDK to “android-21”, after a Project->Clean problem resolved.

After some attempts, the solution for the Activity not created was to uninstall the ADT (Help->installation details->select all ADT->uninstall)


And then reinstall from Help->Install new software:


Finally, it took some additional time to Eclipse for loading but it seems that adding -clean in the Windows shortcut (“<yourpath>\eclipse.exe -clean”) is useful to prevent minor troubles by deleting old cache entries.

Categories: Android