Download - Android, the life of your app
![Page 1: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/1.jpg)
THE LIFE OF YOUR APP
![Page 2: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/2.jpg)
Eyal LEZMY
http://eyal.fr
SLIDES http://bit.ly/lifeofapp
![Page 3: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/3.jpg)
AGENDACODEURS EN SEINE
01
Install it
02
Launch it
03
Look at it
04
Use it
![Page 4: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/4.jpg)
IT ALL STARTS ON THE PLAY STORE
01
![Page 5: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/5.jpg)
![Page 6: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/6.jpg)
![Page 7: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/7.jpg)
Request only what your app requires
1/3 of apps request more permissions than they need
MINIMISE PERMISSIONS
Users should prefer apps
requesting the least
permissions
![Page 8: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/8.jpg)
You don’t need permission
Use ContentProviders
MINIMISE PERMISSIONS
Users should prefer apps
requesting the least
permissions
![Page 9: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/9.jpg)
Permission are not required to launch another activity that has the permission
MINIMISE PERMISSIONS
![Page 10: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/10.jpg)
Need a contact?
MINIMISE PERMISSIONS
![Page 11: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/11.jpg)
Use the force, Luke
MINIMISE PERMISSIONS
![Page 12: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/12.jpg)
MINIMISE PERMISSIONS
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType(Phone.CONTENT_ITEM_TYPE);startActivityForResult(intent, MY_REQUEST_CODE);
void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { Uri uri = data.getData(); if (uri != null) { Cursor c = getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME, Phone.NUMBER}, null, null, null);} }
}}
![Page 13: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/13.jpg)
MINIMISE PERMISSIONS
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType(Phone.CONTENT_ITEM_TYPE);startActivityForResult(intent, MY_REQUEST_CODE);
Start the contact app
void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { Uri uri = data.getData(); if (uri != null) { Cursor c = getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME, Phone.NUMBER}, null, null, null);} }
}}
![Page 14: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/14.jpg)
MINIMISE PERMISSIONS
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType(Phone.CONTENT_ITEM_TYPE);startActivityForResult(intent, MY_REQUEST_CODE);
Start the contact app
Handle the result
void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { Uri uri = data.getData(); if (uri != null) { Cursor c = getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME, Phone.NUMBER}, null, null, null);} }
}}
![Page 15: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/15.jpg)
Need an UUID? TelephonyManager.getDeviceId()
Requires READ_PHONE_STATE permission
MINIMISE PERMISSIONS
Settings.Secure.ANDROID_IDReset at every wipeNot applicable on multi user environment
![Page 16: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/16.jpg)
Need an UUID? TelephonyManager.getDeviceId()
Requires READ_PHONE_STATE permission
MINIMISE PERMISSIONS
Settings.Secure.ANDROID_IDReset at every wipeNot applicable on multi user environment
NO!
![Page 17: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/17.jpg)
Need an UUID? Generate your own UUID and use
Backup API !
MINIMISE PERMISSIONS
String id = UUID.randomUUID().toString();
![Page 18: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/18.jpg)
Need an UUID? Generate your own UUID and use
Backup API !
MINIMISE PERMISSIONS
String id = UUID.randomUUID().toString();
YES!
![Page 19: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/19.jpg)
Android Backup API
· API is available on all Android devices. · Manufacturors can implements their own transport and storage for the API
· Each device as its own backup data
· A new device will take a backup from a device associated with your google account.
· IT'S NOT A SYNC API !
MINIMISE PERMISSIONS
![Page 20: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/20.jpg)
THE FIRST LAUNCH02
![Page 21: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/21.jpg)
Make a good
impression
THE FIRST LAUNCH
![Page 22: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/22.jpg)
Lets start!
THE FIRST LAUNCH
![Page 23: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/23.jpg)
http://cyrilmottier.com
![Page 24: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/24.jpg)
THE FIRST LAUNCH
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.MyTheme" ... >
We define our personal theme including the correct styles and colors
![Page 25: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/25.jpg)
Let’s start...Again!
THE FIRST LAUNCH
![Page 26: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/26.jpg)
My Big
BRAND
![Page 27: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/27.jpg)
LOADING ...
![Page 28: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/28.jpg)
A bunch of data to insert? Use SQL transactions to save
time!
THE FIRST LAUNCH
![Page 29: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/29.jpg)
THE FIRST LAUNCH
db.beginTransaction();
try{for(int i=0; i<selectedIds.length; i++){
values.put(COLUMN_ID,selectedIds[i]);values.put(COLUMN_STARRED,starred);db.insert(TABLE_STARRED,null,values);db.yieldIfContendedSafely();
}
db.setTransactionSuccessful();
} finally {db.endTransaction();
}
Start transaction
![Page 30: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/30.jpg)
THE FIRST LAUNCH
db.beginTransaction();
try{for(int i=0; i<selectedIds.length; i++){
values.put(COLUMN_ID,selectedIds[i]);values.put(COLUMN_STARRED,starred);db.insert(TABLE_STARRED,null,values);db.yieldIfContendedSafely();
}
db.setTransactionSuccessful();
} finally {db.endTransaction();
}
Start transaction
End transaction
![Page 31: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/31.jpg)
THE FIRST LAUNCH
db.beginTransaction();
try{for(int i=0; i<selectedIds.length; i++){
values.put(COLUMN_ID,selectedIds[i]);values.put(COLUMN_STARRED,starred);db.insert(TABLE_STARRED,null,values);db.yieldIfContendedSafely();
}
db.setTransactionSuccessful();
} finally {db.endTransaction();
}
Start transaction
End transaction Optimise multi-threaded insertion
![Page 32: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/32.jpg)
Import directly, ready to use databases
Faster?
THE FIRST LAUNCH
![Page 33: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/33.jpg)
openDatabase(dbPath, null,SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.CREATE_IF_NECESSARY);
· Create your db file using SQlite and fill it
· Name your primary key columns "_id"
· Create the table : "android_metadata"
· And insert a single row containing the local if defined (ex: "en_US"), or open your database using :
THE FIRST LAUNCH
ON THE SERVER
![Page 34: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/34.jpg)
· Grab the zipped database from assets or network
· Unzip it to your getDatabaseDir()
· Open the database
THE FIRST LAUNCH
ON THE MOBILE
Be careful of the SQLite version !
![Page 35: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/35.jpg)
LOOK AND FEEL03
![Page 36: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/36.jpg)
? ? ?
![Page 37: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/37.jpg)
LOOK AND FEEL
HOTMAIL OUTLOOK.COM
![Page 38: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/38.jpg)
LOOK AND FEEL
HOTMAIL OUTLOOK.COM
SAME!
![Page 39: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/39.jpg)
LOOK AND FEEL
FOLLOW THE GUIDELINES!http://d.android.com/design
![Page 40: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/40.jpg)
Redesigned by Taylor Ling
LOOK AND FEEL
![Page 41: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/41.jpg)
By Microsoft
LOOK AND FEEL
![Page 42: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/42.jpg)
LOOK AND FEEL
![Page 43: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/43.jpg)
LOOK AND FEEL
![Page 44: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/44.jpg)
LOOK AND FEEL
FOLLOW THE GUIDELINES!http://d.android.com/design
![Page 45: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/45.jpg)
LOOK AND FEEL
FOLLOW THE GUIDELINES!http://d.android.com/design
PLEASE!
![Page 46: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/46.jpg)
A DAILY USE04
![Page 47: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/47.jpg)
UI Thread =
Events+Draw 16 ms to draw a frame (~60 fps)
SMOOTHEN YOUR UI
Garbage Collector may take 10ms And stop all threads
![Page 48: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/48.jpg)
Flatten the View
Hierarchy· Use RelativeLayout instead of LinearLayout
· Use the <merge/> tag when possible
· Use hierarchyviewer to inspect your layouts
SMOOTHEN YOUR UI
![Page 49: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/49.jpg)
Avoid overdraws · Do not draw your background several times
· Use the “GPU Overdraw” tool from Android 4.2
SMOOTHEN YOUR UI
![Page 50: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/50.jpg)
Use SparseArray
SMOOTHEN YOUR UI
Avoid autoboxing
![Page 51: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/51.jpg)
SMOOTHEN YOUR UI
Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);
SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);
![Page 52: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/52.jpg)
SMOOTHEN YOUR UI
Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);
new Integer(1789)
SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);
![Page 53: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/53.jpg)
SMOOTHEN YOUR UI
Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);
new Integer(1789)new Integer(1789)
SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);
![Page 54: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/54.jpg)
SMOOTHEN YOUR UI
Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);
new Integer(1789)new Integer(1789)
Low memory footprint and no more GC!
SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);
![Page 55: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/55.jpg)
· Reuse it
· Use sampling
SMOOTHEN YOUR UI
Load bitmapcleverly
![Page 56: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/56.jpg)
SMOOTHEN YOUR UI
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;
Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;
![Page 57: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/57.jpg)
SMOOTHEN YOUR UI
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;
Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;
Define the bitmap to reuse (API level 11)
![Page 58: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/58.jpg)
SMOOTHEN YOUR UI
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;
Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;
Use the option for loadingDefine the bitmap to reuse (API level 11)
![Page 59: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/59.jpg)
SMOOTHEN YOUR UI
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;
Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);
Define the subsampling level (API Level 1)
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;
Use the option for loadingDefine the bitmap to reuse (API level 11)
![Page 60: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/60.jpg)
PRESERVE THE BATTERY
![Page 61: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/61.jpg)
Screen: 1st item of consumtion30 to 70% of the battery life
PRESERVE THE BATTERY
![Page 62: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/62.jpg)
Basically you don’t need itOnly a few kind of applications should need to stay the device awake (Reader, Games, …)
Think about the contextRelease wake lock if you have good assumptions that the user is not using your app anymore
WakeLocks
PRESERVE THE BATTERY
![Page 63: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/63.jpg)
GPS· Permission ACCESS_FINE_LOCATION · Drains a lot of power· Works offline
Network location· Permission ACCESS_COARSE_LOCATION· Need to be online· Fast· Precise in urban area
Geolocation
PRESERVE THE BATTERY
![Page 64: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/64.jpg)
Define a strategy· What is the needed precision for my app?· Define the measure interval wisely· Consider the GPS fix time
Use Fused Location ProviderOn Google Play Services
Geolocation
PRESERVE THE BATTERY
![Page 65: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/65.jpg)
Radio drains a lot of powerGroup data to minimize the number of requests
Use caching!
Network
PRESERVE THE BATTERY
![Page 66: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/66.jpg)
PRESERVE THE BATTERY
private void enableHttpResponseCache() { try {
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
File httpCacheDir = new File(getCacheDir(), “http”);
Class.forName(“android.net.http.HttpResponseCache”)
.getMethod(“install”, File.class, long.class)
.invoke(null, httpCacheDir, httpCacheSize);
} catch (Exception httpResponseCacheNotAvailable) {
Log.d(TAG, “HTTP response cache is unavailable.”);
}
}
Enable cache, if available (API level 13)Or use a backport like HttpResponseCache
![Page 67: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/67.jpg)
Enable GZIP on the server30 to 50% less trafic
Use ETAGs
Network
PRESERVE THE BATTERY
![Page 68: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/68.jpg)
PRESERVE THE BATTERY
![Page 69: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/69.jpg)
BE INSPIRED
Gestures Text to Speech Accelerometer
Voice recognition Proximity sensor Bluetooth
NFC Direct Wifi Second Screen
![Page 70: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/70.jpg)
BE INSPIRED
Explore all the device’s possibilities
![Page 71: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/71.jpg)
![Page 72: Android, the life of your app](https://reader033.vdocuments.us/reader033/viewer/2022060108/55511725b4c905b1138b4cd8/html5/thumbnails/72.jpg)
Thank You for your time !
http://eyal.fr
SLIDEShttp://bit.ly/lifeofapp