Friday, November 21, 2014

Android: Custom toast example (different text size, different background color)


toast.getView().setBackgroundColor() will change the background color.
toast.getView().findViewById(android.R.id.message).setTextColor() will change the text color.

    private void makeCustomToast(String message) {
        Toast toast = Toast.makeText(mContext, message, Toast.LENGTH_SHORT);
        TextView v = (TextView) toast.getView().findViewById(android.R.id.message);
        toast.getView().setBackgroundColor(mContext.getResources().getColor(R.color.blue));
        v.setTextColor(mContext.getResources().getColor(R.color.white));
        toast.show();
    }

Android: Get app version programmatically example


Get version name & version code

String sVersionName;
int iVersionCode;
try {
    PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
    sVersionName = pInfo.versionName;
    iVersionCode = pInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
    sVersionName = "Unknown";
    iVersionCode = 1;
}

Version name & version code is from what you have defined in AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.example1"
    android:versionCode="14"
    android:versionName="1.2.2">





Friday, September 26, 2014

Support Chinese Translation (Support Traditional & Simplified) but also Hong Kong

Up until now I was using:

"values-zh-rCN" -> Simplified Chinese
"values-zh-rTW" -> Traditional Chinese

I thought I covered entire Chinese. I was wrong!
There's 'zh-rHK' for Hong Kong.

Since I didn't cover Hong Kong Traditional Chinese, it showed default language which is English.

Hong Kong Traditional Chinese is same as Taiwan Traditional Chinese.
So, you should change to:

"values-zh-rCN" -> Simplified Chinese
"values-zh" -> Traditional Chinese (Covering Taiwan, Hong Kong, Macau, and other places)

Why I didn't add 'zh-rHK'?
Because if there is some other Chinese languages that I wasn't aware, then values-zh should cover them all.

Thursday, May 1, 2014

[Android] How to code 'android:background="?android:attr/selectableItemBackground"' programmatically example

You can't (umm.. there is but hard). So there's a work around.

layout\textview_selectable.xml
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"  
   android:background="?android:attr/selectableItemBackground"
   android:layout_width="match_parent"  
   android:layout_height="wrap_content"  
   />  

then in your code:
 TextView tv = (TextView)getLayoutInflater().inflate(R.layout.textview_selectable, null);

[Android] Set style programatically example

You can't. So there's a work around.

layout\textview_mystyle.xml
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"  
   style="@style/myStyleText"  
   android:layout_width="match_parent"  
   android:layout_height="wrap_content"  
   />  

then in your code:
 TextView tv = (TextView)getLayoutInflater().inflate(R.layout.textview_mystyle, null);

Wednesday, April 30, 2014

Java Date long to C# Datetime long


java date long -> C# datetime long:
 TimeSpan ts = TimeSpan.FromMilliseconds(javaNum);  
 DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local);  
 DateTime dateTime2 = dateTime.Add(ts);  
 DateTime final = dateTime2.ToUniversalTime(); // Change to local-time  

C # -> java long
  private static DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);   
     
  public static long CurrentTimeMillis()   
  {   
     return (long) (DateTime.UtcNow - Jan1st1970).TotalMilliseconds;   
  }   

[Android] How to send email(Gmail) programmatically without launching a third party app (new intent)

First you need following "javamail-android" libraries: (download)
  • additionnal.jar 
  • mail.jar
  • activation.jar

Copy them to \libs folder and add copied jars to your project library.

You need following permissions:
   <uses-permission android:name="android.permission.GET_ACCOUNTS" />  
   <uses-permission android:name="android.permission.INTERNET" />  
   <uses-permission android:name="android.permission.USE_CREDENTIALS" />  

OAuth2Authenticator.java:
   
 import android.accounts.Account;  
 import android.accounts.AccountManager;  
 import android.accounts.AccountManagerCallback;  
 import android.accounts.AccountManagerFuture;  
 import android.app.Activity;  
 import android.os.AsyncTask;  
 import android.os.Bundle;  
 import android.util.Log;  
   
 import com.sun.mail.smtp.SMTPTransport;  
 import com.sun.mail.util.BASE64EncoderStream;  
   
 import java.util.Properties;  
   
 import javax.activation.DataHandler;  
 import javax.mail.Message;  
 import javax.mail.Session;  
 import javax.mail.URLName;  
 import javax.mail.internet.InternetAddress;  
 import javax.mail.internet.MimeMessage;  
 import javax.mail.util.ByteArrayDataSource;  
   
   
 public class OAuth2Authenticator {  
   
   private static Session mSession;  
   
   private AccountManager mAccountManager;  
   private String mToken;  
   private Account mAccount = null;  
   private Activity mActivity;  
   private static String TEST_ACCOUNT_EMAIL = "(your_account_that_is_added_to_Android_device)@gmail.com";  
   
   public OAuth2Authenticator(Activity activity) {  
     mActivity = activity;  
     mAccountManager = AccountManager.get(mActivity);  
   
     Account[] accounts = mAccountManager.getAccounts();  
   
     // For this tutorial, we manually set the email account(TEST_ACCOUNT_EMAIL). However, you should create some kind of UI for users to select the Gmail account  
     for (Account a : accounts) {  
       if (a.name.equals(TEST_ACCOUNT_EMAIL) && a.type.equals("com.google")) {  
         mAccount = a;  
       }  
     }  
     if (mAccount != null) {  
       AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount, "oauth2:https://mail.google.com/", null, mActivity, new OnTokenAcquired(), null);  
     }  
   
   }  
   
   private class OnTokenAcquired implements AccountManagerCallback<Bundle> {  
     @Override  
     public void run(AccountManagerFuture<Bundle> result) {  
       try {  
         Bundle bundle = result.getResult();  
         mToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);  
         new SendMailTask().execute();  
       } catch (Exception e) {  
         Log.e("OnTokenAcquired", e.getMessage());  
       }  
     }  
   }  
   
   private class SendMailTask extends AsyncTask<Void, Void, Void> {  
   
     @Override  
     protected Void doInBackground(Void... params) {  
       try {  
         sendMail("subject", "body message", TEST_ACCOUNT_EMAIL, mToken, "(recipient_address)@(somemail).com");  
       } catch (Exception e) {  
         Log.e("SendMailTask", "error:" + e.getMessage());  
       }  
       return null;  
     }  
   }  
   
   private SMTPTransport connectToSmtp(String host, int port, String userEmail,  
                     String oauthToken, boolean debug) throws Exception {  
   
     Properties props = new Properties();  
     props.put("mail.smtp.starttls.enable", "true");  
     props.put("mail.smtp.starttls.required", "true");  
     props.put("mail.smtp.sasl.enable", "false");  
     mSession = Session.getInstance(props);  
     mSession.setDebug(debug);  
     final URLName unusedUrlName = null;  
     SMTPTransport transport = new SMTPTransport(mSession, unusedUrlName);  
     // If the password is non-null, SMTP tries to do AUTH LOGIN.  
     final String emptyPassword = null;  
     transport.connect(host, port, userEmail, emptyPassword);  
     byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", userEmail, oauthToken).getBytes();  
     response = BASE64EncoderStream.encode(response);  
     transport.issueCommand("AUTH XOAUTH2 " + new String(response), 235);  
     return transport;  
   }  
   
   public synchronized void sendMail(String subject, String body, String user,  
                    String oauthToken, String recipients) {  
     try {  
   
       SMTPTransport smtpTransport = connectToSmtp("smtp.gmail.com",  
           587,  
           user,  
           oauthToken,  
           true);  
       MimeMessage message = new MimeMessage(mSession);  
       DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain"));  
       message.setSender(new InternetAddress(user));  
       message.setSubject(subject);  
       message.setDataHandler(handler);  
       if (recipients.indexOf(',') > 0)  
         message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));  
       else  
         message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));  
       smtpTransport.sendMessage(message, message.getAllRecipients());  
     } catch (Exception e) {  
       Log.e("sendMail", e.getMessage());  
     }  
   }  
 }  

MainActivity.java:
 new OAuth2Authenticator(this);  


Friday, April 18, 2014

Android: how to send SMS(text message) programmatically


You need SEND_SMS permission:

Code:
        try {
            SmsManager smsManager = SmsManager.getDefault();
            String sPhoneNumber = "1-222-3333";
            String sMessage = "Hello World";
            smsManager.sendTextMessage(sPhoneNumber, null, sMessage, null, null);
            Toast.makeText(this, "Message has been sent", Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(this, "Message send failed", Toast.LENGTH_LONG).show();
        }

Warning: Make sure your app doesn't send SMS without user's knowledge. It will violate the Google's policy and your app will be suspended.

  • Do not send SMS, email, or other messages on behalf of the user without providing the user with the ability to confirm content and intended recipient.


Developer's Guide: What API level should you target for your app? (minSdkVersion)

This is my personal opinion. (Not a correct answer)
(Note: I am writing this on April 18, 2014. Things might be different later)

Please check the latest version distribution dashboard first.

Free app or paid app?
If you are making a paid app, it is OK to target relatively latest versions only. I say version 14 (Android 4.0+) [or version 15 (Android 4.0.3+) since not many devices use version 14 anyways]. There is a big jump from 2.x to 4.x no need an extra work to support 2.x devices when your main income is from the paid app (or in-app-purchase).

Why? I found most paid apps are purchased from the latest devices anyways. People who use older devices are pretty much satisfied with apps they already have and they don't tend to look for paid apps (compared to people who uses the latest devices). I don't make games so I am not sure if above logic applies for games too.

If your income is a free app with ad then you want to cover as much as possible. Still, I believe 2.x devices are not worth it at this time.

Think about testing
You might think the more device support the better. Not always! If your app doesn't work properly on some devices (or some older versions), they will give you bad ratings/reviews and your overall ratings will decrease. One of my apps got more popular when I actually removed the support for older devices for this reason.

Again, this is my personal opinion from my experience.

Wednesday, April 16, 2014

Android: Show notification example


Show notification:

        Intent intent = new Intent(context, ExampleReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
        Notification n;
        Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            n = new Notification.Builder(context)
                    .setContentTitle("title")
                    .setContentText("Short message\nContent message")
                    .setTicker("ticker message")
                    .setSmallIcon(R.drawable.notification_icon)
                    .setLargeIcon(bm)
                    .setStyle(new Notification.BigTextStyle().bigText("Long message.\nTesting..Testing..\nlooooooooooooooooon message"))
                    .setContentIntent(paramIntent)
                    .setAutoCancel(true).build();
        } else {
            n = new Notification.Builder(context)
                    .setContentTitle("title")
                    .setContentText("Short message\nContent message")
                    .setTicker("ticker message")
                    .setSmallIcon(R.drawable.notification_icon)
                    .setLargeIcon(bm)
                    .setContentIntent(paramIntent)
                    .setAutoCancel(true).getNotification();
        }
        n.flags = n.flags | Notification.FLAG_NO_CLEAR;
        notificationManager.notify(0, n);


Show only the ticker and make it go disappear:

        Intent intent = new Intent(context, ExampleReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
        Notification n;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            n = new Notification.Builder(context)
                    .setTicker("ticker message")
                    .setSmallIcon(R.drawable.notification_icon)
                    .setAutoCancel(true)
                    .build();
        } else {
            n = new Notification.Builder(context)
                    .setTicker("ticker message")
                    .setSmallIcon(R.drawable.notification_icon)
                    .setAutoCancel(true)
                    .getNotification();
        }
        notificationManager.notify(0, n);
        notificationManager.cancel(0);


How to uninstall through adb command when multiple devices are connected

Sometimes when you have a physical device and an emulator connected to your computer, adb command doesn't work.

This will display serial number of connected devices:
adb devices -l









Now knowing the serial number of  my physical device (in this case: 8d5351c6062aa124), type adb uninstall command:

adb -s (serial number) uninstall (com.package.name)

in my case:
adb -s 8d5351c6062aa124 uninstall (com.package.name)

Monday, April 14, 2014

How to outsource/freelance Android app icon design for a cheap price?

You make apps and you have a terrible icon so no one downloads your app. Here's a solution:

Here's what I've learned (so far).

Fiverr
The cheapest place to do is fiverr.
Watch out! Not everything is $5.
They make you add this and that feature for extra $5~$20. But if you search carefully, some designers satisfies all your needs in $5.

Warning: Make sure you provide EVERYTHING you want your icon to be like. Don't expect them to read your mind and receive an awesome icon. Sometimes you have no idea what kind of icon is suitable. Even so, do some research because most likely they don't spend time to do research either. You end up, wasting your time and their time, and your money. (Been there, done that).

Provide either:

  • A pencil sketch of expected icon (take a photo and send to the designer)
  • A link or screenshot of an existing icon (maybe some other competitor icon) and tell them you want this style.
  • A detail description. (Color of the icon, add a shadow effect or flat icon, etc etc)
You still have a chance to go back and forth and request modification until you're satisfied but you should provide detail expectation ahead of time so that you don't waste the designer's time.

In my experience, when I requested for an awesome icon and explained what my app does, they made me a crappy icon. I had to provide EXACTLY what to do, they did a good job.

It seems you pay hourly, I haven't done it.

You provide a description and multiple designers make an icon for you. You simply pick the best one and give the money to the winner. The benefit is that you can compare and pick the best icon that you like. Not cheap so I haven't done it.

Use license-free free icons. See my 'Where to get a free Android icons?'



Please add a comment if you know other places.

Where to get a free Android icons?

Here's the list of places you can search for the free icons.
Make sure you set filter for "Commercial free" if your app is 'paid' app or has an 'ad' (you make some sort of profit)

http://www.iconarchive.com/
https://www.iconfinder.com/
http://findicons.com/
http://iconizer.net/en/free_icons

You can also search 'Google image' with "(search word) + icon" and set 'Labeled for reuse' filter.



Please add a comment if you know other places.