Pages

Friday, August 28, 2015

Passing data from notification click event to activity android

A lot of time spend until i made it work!

I had lots of notifications and all they should trigger different events in the activity.

The data passed through intent's putExtra methods was not reliable, because of way that intent works. Some extras did not change.

The solution i ended up with is like this:

1) In my custom receiver:
Intent intent = new Intent(null, Uri.parse("some data"), context, MainActivity.class);intent.putExtra("from_notification", true);intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

Create notification here. Btw logo should be set like this, because lollipop and higher logo must be only from white color:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    iconId = R.raw.logo_white;}
else{
    iconId = R.raw.logo;}

mBuilder.setContentIntent(contentIntent);




My MainActivity:
@Overrideprotected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    boolean fromNotification = intent.getBooleanExtra("from_notification", false);
    if (fromNotification){
        String someData = intent.getData().getSchemeSpecificPart();        //some data is the unique string for each notification
    }
}

Also you should call  the method from onCreate, for cases when activity is not yet running when you tap the notification:

onNewIntent(getIntent());

And dont forget to register the receiver in manifest file with appropriate filters.

Saturday, August 22, 2015

android SQLite best practices (how to SQLite, android sqlite guide tutorial)

Some info i feel obliged to persist.

Had lots of problems and knowledge gaps with android SQLite database, now all seems to fit in its places.


I will share the approach i came to in a few months time of debugging and error fixing.


1) To work with android SQLite you must create your own helper class extending SQLiteOpenHelper.


2) You should use a singleton pattern for this helper instance and get the instance where needed in Activity's onCreate method:

public static synchronized DBHelper getInstance(Context context) {
    // Use the application context, which will ensure that you
    // don't accidentally leak an Activity's context.       
    if (sInstance == null) {
        sInstance = new DBHelper(context.getApplicationContext());    
    }
    return sInstance;
}
3) Before each db interaction (insert, delete, etc) open the database: 
db = this.getWritableDatabase();

4) The helper class should have methods for fetching/persisting data. Methods using cursor must close the cursor after using it. Never close the database itself in this class.


5) Use transaction mode for looped interactions:

db = this.getWritableDatabase();
db.beginTransaction();
try {
    for (...) {
        db.insert(...);
    }

    db.yieldIfContendedSafely();    
    db.setTransactionSuccessful();
}catch(Exception exc){
    exc.printStackTrace();
}finally {
    db.endTransaction();
}
6) My approach to Cursors/queries:
db = this.getWritableDatabase();
Cursor c = db.rawQuery("Select * from TABLE where ID = ?",new String[] {id});
c.moveToPosition(-1);
while (c.moveToNext()) {
    String s = c.getString(c.getColumnIndex("col_name"));
}
c.close();
7) onUpgrade method - when you increment the database version, this method is  called. Mine looks like this:
public static final int DATABASE_VERSION = 238;
@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    dropAll(db); 
    makeDB(db);
}

private SQLiteDatabase dropAll(SQLiteDatabase db){
    db.execSQL("DROP TABLE IF EXISTS tasks");
    return db;
}
public static void makeDB(SQLiteDatabase db){
    db.execSQL(CREATE_TABLE_TASKS);
}
private static final String CREATE_TABLE_TASKS = "CREATE TABLE IF NOT EXISTS tasks(user VARCHAR, ID INTEGER PRIMARY KEY AUTOINCREMENT);";
8) onCreate method:
@Overridepublic void onCreate(SQLiteDatabase db) {
    makeDB(db);
}

9) Working with database is very fast, performance drops might be caused by something else usually, so put that code in AsyncTask.

Monday, August 3, 2015

Anonymous messenger Stealthy.ms

Hi!

Last months i'm developing android version of the already popular iphone app Anonymous messenger "Stealthy". (http://stealthy.ms or http://smsSekret.ru)

The initial idea was to enable people to open their hearts to loved ones, anonymously. Or maybe send secret insides to your business partners, or any other kind of 2 way conversation where you wish to remain anonymous.

The realisation came as ability of the app was to send anonymous SMS to anyone from your contact list, and if they download the app they can answer to your anonymous sms, not knowing who it is. You will know who you are talking to, but not they, they will see you as "Secret Friend".

This app acts just like a simple chat/irc, but you are anonymous! There are group chats with people from your locations (also lots of group chats on different topics - cars/games/ etc), where you can troll everybody or even find new friends.

Also a chat roulette functionality is available, where you will be connected to a random active user of the other gender.

Saturday, April 25, 2015

android sqlite made easy tutorial

Hi!

Start using SQLite in your android app is very easy! SQLite is supported from start for any android app, you dont need to configure anything. There are some frameworks, but i found easier to use without them. Also using frameworks have some litimations, for example currently frameworks do not support encryption of the database.

Also check my second post about SQLite: http://tocrva.blogspot.com/2015/08/android-sqlite-best-practices-how-to.html

1) private field in the activity and add the needed imports:

private SQLiteDatabase db;

2) In onCreate method:

db = openOrCreateDatabase("dbname.db", MODE_PRIVATE, null);

As you might understand, this creates or opens a database named dbname.db in private mode. Private mode means that only your app will have access to this database. Note that the database itself is persisted in text file form and can be accesses easily from a rooted device or a emulator.

3) Now you must create a table inside your database:

db.execSQL("CREATE TABLE IF NOT EXISTS mytable (column1 VARCHAR, column2 VARCHAR);");

This also is self explanatory. Standart SQL sintax. The execSQL method is used to execute sql queries that do not return anything.

4) Inserting values, the best practice using ContentValues (inside try/catch):

ContentValues cv = new ContentValues();
cv.put("column1", "some value");
db.insert("mytable", null, cv);

5) Fetching values. You can provide a static query in the first argument of rawQuery method, or use array of arguments new String[]{"arg1", "arg2", "arg3"}, those will be put in places of "?" in the query:

Cursor c = db.rawQuery("select * from mytable where column1 = ?", new String[]{"some value"});

c.moveToPosition(-1);
while (c.moveToNext()) {
    String column1value = c.getString(0);
}
c.close();

6) Performance optimisation for cycled operations:

db.beginTransaction();
try {
    for(){
        //some insert query
    }
    db.yieldIfContendedSafely();
    db.setTransactionSuccessful();
}catch(Exception exc){
    exc.printStackTrace();
}
finally {
    db.endTransaction();
}

7) A good practice would be to close the db when you will not use it anymore, for example in activity's onPause method:

db.close();

Wednesday, April 1, 2015

Grails mail plugin: implementing email validation (part 2)

Continuing the 1st part of email validation.

In my urlmappings the default controller is user:

//"/"(view:"/index")
"/"(controller:"user", action:"index")

Inside the UserController :
def index() {
if(params.id){
Map model = [queueId: params.id]
redirect(controller:'queue', action:'view', params:model)
}
if(params.confirmationCode){
print "email confirmation code: '${params.confirmationCode}'"
Map params = [confirmationCode: params.confirmationCode]
redirect(controller:'user', action:'confirmEmail', params: params)
}

}

When user opens the link from email like www.yourapp.com/?confirmationCode=32irm4oifjij then the flow passes to the confirmEmail action

confirmEmail action:

def confirmEmail(){
def confirmationCode = params.confirmationCode
def decoded = new String(params.confirmationCode.decodeBase64())
def separatorIndex = decoded.indexOf("QQQ")
def userId = decoded.substring(0, separatorIndex).toInteger()
def user = User.findById(userId)
def proceed = true
if(!user){
proceed = false
flash.message = "User not found with such id."
}
if(confirmationCode != user.temp){
proceed = false
flash.message = "Wrong confirmation code!"
}
if(proceed){
user.temp = null
user.emailVerified = true
user.save(flush:true)
flash.message = "Email was successfully verified for user '${user.userName}'."
}
redirect(controller:'user', action:'index')

}
This is a string decoded in base64 format. For my application queit the format was next: [user id]QQQ[confirmation code]

So the decoded string would look like "1QQQ#@FR$G#ESD", while the encoded string looks something like "ferGW$GFHWgweroih6"

The id is used to fetch user from DB. The user table has a "temp" field where i stored the confirmation code when registering and then sending email.

I believe the rest is self explanatory.

Monday, March 30, 2015

java transliterate cyrillic to latin

Hi! Fast solution without any 3rd party API's:

public static String transliterate(String message){
char[] abcCyr =   {' ','а','б','в','г','д','е', 'ё', 'ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х', 'ц', 'ч', 'ш''щ','ъ','ы','ь','э', 'ю', 'я'
  ,'А','Б','В','Г','Д','Е', 'Ё', 'Ж','З','И','Й','К','Л','М','Н','О','П','Р','С','Т','У','Ф','Х', 'Ц', 'Ч', 'Ш''Щ','Ъ','Ы','Б','Э', 'Ю', 'Я'
  ,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'
  ,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
String[] abcLat = {" ","a","b","v","g","d","e","e","zh","z","i","y","k","l","m","n","o","p","r","s","t","u","f","h","ts","ch","sh","sch", "","i", "","e","ju","ja"
  ,"A","B","V","G","D","E","E","Zh","Z","I","Y","K","L","M","N","O","P","R","S","T","U","F","H","Ts","Ch","Sh","Sch", "","I", "","E","Ju","Ja"
  ,"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"
  ,"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
StringBuilder builder = new StringBuilder();
for (int i = 0; i < message.length(); i++) {
for(int x = 0; x < abcCyr.length; x++ )
if (message.charAt(i) == abcCyr[x]) {
builder.append(abcLat[x]);
}
}
return builder.toString();

}

Wednesday, March 18, 2015

Grails flash message not working in production mode

In dev mode it works, but on production flash messages do not appear. If i put message in session scope then it appears, but not in flash.message. The first idea is that there are some additional redirects happening in production mode, those clear the flash message. Also if i run my app with command grails prod run-app, then flash messages also do not appear. There must be some configuration that disables flash messages for production mode!

The only difference that i found in config.groovy between prod and dev modes was this line:

 grails.logging.jul.usebridge = false

But altering the value didn't affect the production environment flash messages in any way.

Also when i created a test project and ran it in prod mode, the flash message appeared correctly. So there must be something wrong with my project. 

SOLVED:
With a bit of help from ZYRO <3 from grails at freenode.

I had a heroku plugin installed, that was sucking in a database session plugin that had some deprecated class and turns out that was causing the problem.
OMFG. How easy was that:

  1. Check logs.
  2. See what causes the problem.
  3. Disable that thing.

Two days of desperate researching were wasted because of incorrect approach.